From a84b479d5e86b5257acbc566f5bd5fbd09da8308 Mon Sep 17 00:00:00 2001 From: gal kahana Date: Wed, 21 Aug 2024 20:39:54 +0300 Subject: [PATCH] feat: resolving invalid parsing causing stack overflow --- .vscode/settings.json | 3 +- PDFWriter/PDFObjectParser.cpp | 45 +++++++++++++++++- PDFWriter/PDFObjectParser.h | 3 ++ .../errors/parseLastXrefPositionError_278.pdf | Bin 0 -> 22022 bytes .../errors/parseLastXrefPositionError_279.pdf | Bin 0 -> 713507 bytes 5 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 PDFWriterTesting/Materials/fuzzing/errors/parseLastXrefPositionError_278.pdf create mode 100644 PDFWriterTesting/Materials/fuzzing/errors/parseLastXrefPositionError_279.pdf diff --git a/.vscode/settings.json b/.vscode/settings.json index e69b38ef..8bd5a6bf 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -68,6 +68,7 @@ "unordered_map": "cpp", "variant": "cpp", "algorithm": "cpp", - "tiffio.h": "c" + "tiffio.h": "c", + "utility": "cpp" } } \ No newline at end of file diff --git a/PDFWriter/PDFObjectParser.cpp b/PDFWriter/PDFObjectParser.cpp index 76812418..d6405f86 100644 --- a/PDFWriter/PDFObjectParser.cpp +++ b/PDFWriter/PDFObjectParser.cpp @@ -45,6 +45,8 @@ using namespace PDFHummus; +#define MAX_OBJECT_DEPTH 100 // While PDF does not explicitly define arrays and dicts depth...we do, due to call stack depth limit...and to avoid potential malarky. + PDFObjectParser::PDFObjectParser(void) { @@ -52,6 +54,7 @@ PDFObjectParser::PDFObjectParser(void) mDecryptionHelper = NULL; mOwnsStream = false; mStream = NULL; + mDepth = 0; } PDFObjectParser::~PDFObjectParser(void) @@ -79,6 +82,7 @@ void PDFObjectParser::ResetReadState() { mTokenBuffer.clear(); mTokenizer.ResetReadState(); + mDepth = 0; } void PDFObjectParser::ResetReadState(const PDFParserTokenizer& inExternalTokenizer) @@ -584,14 +588,40 @@ bool PDFObjectParser::IsArray(const std::string& inToken) return scLeftSquare == inToken; } +EStatusCode PDFObjectParser::IncreaseAndCheckDepth() { + ++mDepth; + if(mDepth > MAX_OBJECT_DEPTH) { + TRACE_LOG1("PDFObjectParser::IncreaseAndCeckDepth, reached maximum allowed depth of %d", MAX_OBJECT_DEPTH); + return eFailure; + } + + return eSuccess; +} + +EStatusCode PDFObjectParser::DecreaseAndCheckDepth() { + --mDepth; + if(mDepth < 0) { + TRACE_LOG("PDFObjectParser::DecreaseAndCheckDepth, anomaly. managed to get to negative depth"); + return eFailure; + } + + return eSuccess; +} + + static const std::string scRightSquare = "]"; PDFObject* PDFObjectParser::ParseArray() { - PDFArray* anArray = new PDFArray(); + PDFArray* anArray; bool arrayEndEncountered = false; std::string token; EStatusCode status = PDFHummus::eSuccess; + if(IncreaseAndCheckDepth() != eSuccess) + return NULL; + + anArray = new PDFArray(); + // easy one. just loop till you get to a closing bracket token and recurse while(GetNextToken(token) && PDFHummus::eSuccess == status) { @@ -612,6 +642,9 @@ PDFObject* PDFObjectParser::ParseArray() } } + if(DecreaseAndCheckDepth() != eSuccess) + status = eFailure; + if(arrayEndEncountered && PDFHummus::eSuccess == status) { return anArray; @@ -643,11 +676,16 @@ bool PDFObjectParser::IsDictionary(const std::string& inToken) static const std::string scDoubleRightAngle = ">>"; PDFObject* PDFObjectParser::ParseDictionary() { - PDFDictionary* aDictionary = new PDFDictionary(); + PDFDictionary* aDictionary; bool dictionaryEndEncountered = false; std::string token; EStatusCode status = PDFHummus::eSuccess; + if(IncreaseAndCheckDepth() != eSuccess) + return NULL; + + aDictionary = new PDFDictionary(); + while(GetNextToken(token) && PDFHummus::eSuccess == status) { dictionaryEndEncountered = (scDoubleRightAngle == token); @@ -681,6 +719,9 @@ PDFObject* PDFObjectParser::ParseDictionary() aDictionary->Insert(aKey.GetPtr(),aValue.GetPtr()); } + if(DecreaseAndCheckDepth() != eSuccess) + status = eFailure; + if(dictionaryEndEncountered && PDFHummus::eSuccess == status) { return aDictionary; diff --git a/PDFWriter/PDFObjectParser.h b/PDFWriter/PDFObjectParser.h index ff07e495..6d2c1ddf 100644 --- a/PDFWriter/PDFObjectParser.h +++ b/PDFWriter/PDFObjectParser.h @@ -78,6 +78,7 @@ class PDFObjectParser IPDFParserExtender* mParserExtender; DecryptionHelper* mDecryptionHelper; bool mOwnsStream; + int mDepth; bool GetNextToken(std::string& outToken); void SaveTokenToBuffer(std::string& inToken); @@ -112,5 +113,7 @@ class PDFObjectParser std::string MaybeDecryptString(const std::string& inString); + PDFHummus::EStatusCode IncreaseAndCheckDepth(); + PDFHummus::EStatusCode DecreaseAndCheckDepth(); }; diff --git a/PDFWriterTesting/Materials/fuzzing/errors/parseLastXrefPositionError_278.pdf b/PDFWriterTesting/Materials/fuzzing/errors/parseLastXrefPositionError_278.pdf new file mode 100644 index 0000000000000000000000000000000000000000..5478f48ae367b8714f1f51797c341873da0a2a4b GIT binary patch literal 22022 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 I-Ff}r0a6Vj8UO$Q literal 0 HcmV?d00001