Description: CVE-2017-5852 The part coming from the email is not (yet?) merged, it's there only to prevent an ABI breakage and can be safely dropped at the next SONAME bump. Acked-By: Mattia Rizzolo Bug-Debian: https://bugs.debian.org/854600 Origin: https://sourceforge.net/p/podofo/code/1838 Origin: https://sourceforge.net/p/podofo/code/1835 Origin: https://sourceforge.net/p/podofo/code/1841 Origin: https://sourceforge.net/p/podofo/mailman/message/36084628/ Last-Update: 2017-11-12 --- a/src/base/PdfError.cpp +++ b/src/base/PdfError.cpp @@ -222,6 +222,9 @@ case ePdfError_InvalidEnumValue: pszMsg = "ePdfError_InvalidEnumValue"; break; + case ePdfError_BrokenFile: + pszMsg = "ePdfError_BrokenFile"; + break; case ePdfError_PageNotFound: pszMsg = "ePdfError_PageNotFound"; break; @@ -397,6 +400,9 @@ case ePdfError_InvalidEnumValue: pszMsg = "An invalid enum value was specified."; break; + case ePdfError_BrokenFile: + pszMsg = "The file content is broken."; + break; case ePdfError_PageNotFound: pszMsg = "The requested page could not be found in the PDF."; break; --- a/src/base/PdfError.h +++ b/src/base/PdfError.h @@ -73,6 +73,7 @@ ePdfError_ValueOutOfRange, /**< The specified memory is out of the allowed range. */ ePdfError_InternalLogic, /**< An internal sanity check or assertion failed. */ ePdfError_InvalidEnumValue, /**< An invalid enum value was specified. */ + ePdfError_BrokenFile, /**< The file content is broken. */ ePdfError_PageNotFound, /**< The requested page could not be found in the PDF. */ --- a/src/doc/PdfPage.cpp +++ b/src/doc/PdfPage.cpp @@ -214,6 +214,11 @@ const PdfObject* PdfPage::GetInheritedKeyFromObject( const char* inKey, const PdfObject* inObject ) const { + return GetInheritedKeyFromObject( inKey, inObject, 0); +} + +const PdfObject* PdfPage::GetInheritedKeyFromObject( const char* inKey, const PdfObject* inObject, int depth ) const +{ const PdfObject* pObj = NULL; // check for it in the object itself @@ -227,9 +232,29 @@ // if we get here, we need to go check the parent - if there is one! if( inObject->GetDictionary().HasKey( "Parent" ) ) { + // CVE-2017-5852 - prevent stack overflow if Parent chain contains a loop, or is very long + // e.g. pObj->GetParent() == pObj or pObj->GetParent()->GetParent() == pObj + // default stack sizes + // Windows: 1 MB + // Linux: 2 MB + // macOS: 8 MB for main thread, 0.5 MB for secondary threads + // 0.5 MB is enough space for 1000 512 byte stack frames and 2000 256 byte stack frames + const int maxRecursionDepth = 1000; + + if ( depth > maxRecursionDepth ) + PODOFO_RAISE_ERROR( ePdfError_ValueOutOfRange ); + pObj = inObject->GetIndirectKey( "Parent" ); + if( pObj == inObject ) + { + std::ostringstream oss; + oss << "Object " << inObject->Reference().ObjectNumber() << " " + << inObject->Reference().GenerationNumber() << " references itself as Parent"; + PODOFO_RAISE_ERROR_INFO( ePdfError_BrokenFile, oss.str().c_str() ); + } + if( pObj ) - pObj = GetInheritedKeyFromObject( inKey, pObj ); + pObj = GetInheritedKeyFromObject( inKey, pObj, depth + 1 ); } return pObj; @@ -523,6 +548,11 @@ PdfObject* pParent = this->GetObject()->GetIndirectKey( "Parent" ); PdfReference ref = this->GetObject()->Reference(); + // CVE-2017-5852 - prevent infinite loop if Parent chain contains a loop + // e.g. pParent->GetIndirectKey( "Parent" ) == pParent or pParent->GetIndirectKey( "Parent" )->GetIndirectKey( "Parent" ) == pParent + const int maxRecursionDepth = 1000; + int depth = 0; + while( pParent ) { PdfObject* pKids = pParent->GetIndirectKey( "Kids" ); @@ -554,6 +584,12 @@ ref = pParent->Reference(); pParent = pParent->GetIndirectKey( "Parent" ); + ++depth; + + if ( depth > maxRecursionDepth ) + { + PODOFO_RAISE_ERROR_INFO( ePdfError_BrokenFile, "Loop in Parent chain" ); + } } return ++nPageNumber; --- a/src/doc/PdfPage.h +++ b/src/doc/PdfPage.h @@ -291,7 +291,10 @@ /** Method for getting a key value that could be inherited (such as the boxes, resources, etc.) * \returns PdfObject - the result of the key fetching or NULL */ - const PdfObject* GetInheritedKeyFromObject( const char* inKey, const PdfObject* inObject ) const; + const PdfObject* GetInheritedKeyFromObject( const char* inKey, const PdfObject* inObject ) const; // wraps the next one + + // this is introduced by the fix for CVE-2017-5852, the depth param counts recursion depth, is checked against a max + const PdfObject* GetInheritedKeyFromObject( const char* inKey, const PdfObject* inObject, int depth ) const PODOFO_LOCAL; /** Get the annotations array. * \param bCreate if true the annotations array is created