Description: CVE-2017-8054 Acked-By: Mattia Rizzolo Bug-Debian: https://bugs.debian.org/860995 Origin: https://sourceforge.net/p/podofo/code/1872 Origin: https://sourceforge.net/p/podofo/code/1881 Origin: https://sourceforge.net/p/podofo/code/1882 Origin: https://sourceforge.net/p/podofo/code/1883 --- a/src/base/PdfError.cpp +++ b/src/base/PdfError.cpp @@ -60,6 +60,12 @@ { } +PdfErrorInfo::PdfErrorInfo( int line, const char* pszFile, std::string sInfo ) + : m_nLine( line ), m_sFile( pszFile ? pszFile : "" ), m_sInfo( sInfo ) +{ + +} + PdfErrorInfo::PdfErrorInfo( int line, const char* pszFile, const char* pszInfo ) : m_nLine( line ), m_sFile( pszFile ? pszFile : "" ), m_sInfo( pszInfo ? pszInfo : "" ) { @@ -96,6 +102,12 @@ } PdfError::PdfError( const EPdfError & eCode, const char* pszFile, int line, + std::string sInformation ) +{ + this->SetError( eCode, pszFile, line, sInformation ); +} + +PdfError::PdfError( const EPdfError & eCode, const char* pszFile, int line, const char* pszInformation ) { this->SetError( eCode, pszFile, line, pszInformation ); --- a/src/base/PdfError.h +++ b/src/base/PdfError.h @@ -158,8 +158,8 @@ /** \def PODOFO_RAISE_ERROR_INFO( x, y ) * * Set the value of the variable eCode (which has to exist in the current function) to x - * and return the eCode. Additionally additional information on the error y is set. y has - * to be an c-string. + * and return the eCode. Additionally additional information on the error y is set. + * y can be a C string, but can also be a C++ std::string. */ #define PODOFO_RAISE_ERROR_INFO( x, y ) throw ::PoDoFo::PdfError( x, __FILE__, __LINE__, y ); @@ -174,6 +174,7 @@ public: PdfErrorInfo(); PdfErrorInfo( int line, const char* pszFile, const char* pszInfo ); + PdfErrorInfo( int line, const char* pszFile, std::string pszInfo ); PdfErrorInfo( int line, const char* pszFile, const wchar_t* pszInfo ); PdfErrorInfo( const PdfErrorInfo & rhs ); @@ -185,6 +186,7 @@ inline const std::wstring & GetInformationW() const { return m_swInfo; } inline void SetInformation( const char* pszInfo ) { m_sInfo = pszInfo ? pszInfo : ""; } + inline void SetInformation( std::string pszInfo ) { m_sInfo = pszInfo; } inline void SetInformation( const wchar_t* pszInfo ) { m_swInfo = pszInfo ? pszInfo : L""; } private: @@ -242,12 +244,22 @@ * Use the compiler macro __FILE__ to initialize the field. * \param line the line in which the error has occured. * Use the compiler macro __LINE__ to initialize the field. - * \param pszInformation additional information on this error which mayy - * be formatted like printf + * \param pszInformation additional information on this error */ PdfError( const EPdfError & eCode, const char* pszFile = NULL, int line = 0, const char* pszInformation = NULL ); + /** Create a PdfError object with a given error code. + * \param eCode the error code of this object + * \param pszFile the file in which the error has occured. + * Use the compiler macro __FILE__ to initialize the field. + * \param line the line in which the error has occured. + * Use the compiler macro __LINE__ to initialize the field. + * \param sInformation additional information on this error + */ + explicit PdfError( const EPdfError & eCode, const char* pszFile, int line, + std::string sInformation ); + /** Copy constructor * \param rhs copy the contents of rhs into this object */ @@ -309,6 +321,21 @@ * \param line the line of source causing the error * or 0. Typically you will use the gcc * macro __LINE__ here. + * \param sInformation additional information on the error. + * e.g. how to fix the error. This string is intended to + * be shown to the user. + */ + inline void SetError( const EPdfError & eCode, const char* pszFile, int line, + std::string sInformation ); + + /** Set the error code of this object. + * \param eCode the error code of this object + * \param pszFile the filename of the source file causing + * the error or NULL. Typically you will use + * the gcc macro __FILE__ here. + * \param line the line of source causing the error + * or 0. Typically you will use the gcc + * macro __LINE__ here. * \param pszInformation additional information on the error. * e.g. how to fix the error. This string is intended to * be shown to the user. @@ -344,6 +371,21 @@ */ inline void AddToCallstack( const char* pszFile = NULL, int line = 0, const char* pszInformation = NULL ); + /** Add callstack information to an error object. Always call this function + * if you get an error object but do not handle the error but throw it again. + * + * \param pszFile the filename of the source file causing + * the error or NULL. Typically you will use + * the gcc macro __FILE__ here. + * \param line the line of source causing the error + * or 0. Typically you will use the gcc + * macro __LINE__ here. + * \param sInformation additional information on the error. + * e.g. how to fix the error. This string is intended to + * be shown to the user. + */ + inline void AddToCallstack( const char* pszFile, int line, std::string sInformation ); + /** \returns true if an error code was set * and false if the error code is ePdfError_ErrOk */ @@ -478,6 +520,22 @@ // ----------------------------------------------------- // // ----------------------------------------------------- +void PdfError::SetError( const EPdfError & eCode, const char* pszFile, int line, std::string sInformation ) +{ + m_error = eCode; + this->AddToCallstack( pszFile, line, sInformation ); +} + +// ----------------------------------------------------- +// +// ----------------------------------------------------- +void PdfError::AddToCallstack( const char* pszFile, int line, std::string sInformation ) +{ + m_callStack.push_front( PdfErrorInfo( line, pszFile, sInformation ) ); +} +// ----------------------------------------------------- +// +// ----------------------------------------------------- void PdfError::SetErrorInformation( const char* pszInformation ) { if( m_callStack.size() ) --- a/src/doc/PdfPagesTree.cpp +++ b/src/doc/PdfPagesTree.cpp @@ -34,6 +34,7 @@ #include "PdfPagesTree.h" #include "base/PdfDefinesPrivate.h" +#include #include "base/PdfArray.h" #include "base/PdfDictionary.h" @@ -478,7 +479,18 @@ if( rVar.IsArray() ) { // Fixes some broken PDFs who have trees with 1 element kids arrays - return GetPageNodeFromArray( 0, rVar.GetArray(), rLstParents ); + // Recursive call removed to prevent stack overflow (CVE-2017-8054) + // replaced by the following inside this conditional incl. continue + const PdfArray & rVarArray = rVar.GetArray(); + if (rVarArray.GetSize() == 0) + { + PdfError::LogMessage( eLogSeverity_Critical, "Trying to access" + " first page index of empty array" ); + return NULL; + } + PdfVariant rVarFirstEntry = rVarArray[0]; // avoids use-after-free + rVar = rVarFirstEntry; // in this line (rVar-ref'd array is freed) + continue; } else if( !rVar.IsReference() ) { @@ -502,6 +514,18 @@ if( !pgObject->GetDictionary().HasKey( "Kids" ) ) return NULL; + if ( std::find( rLstParents.begin(), rLstParents.end(), pgObject ) + != rLstParents.end() ) // cycle in parent list detected, fend + { // off security vulnerability CVE-2017-8054 (infinite recursion) + std::ostringstream oss; + oss << "Cycle in page tree: child in /Kids array of object " + << ( *(rLstParents.rbegin()) )->Reference().ToString() + << " back-references to object " << pgObject->Reference() + .ToString() << " one of whose descendants the former is."; + + PODOFO_RAISE_ERROR_INFO( ePdfError_PageNotFound, oss.str() ); + } + rLstParents.push_back( pgObject ); rVar = *(pgObject->GetDictionary().GetKey( "Kids" )); } else {