File 0024-Fix-CVE-2017-8054-Detect-cycles-in-PdfPagesTree.patch of Package podofo
Subject: Fix by Matthias Brinke: CVE-2017-8054 - Detect cycles in PdfPagesTree
Url: https://sourceforge.net/p/podofo/code/1872/
--- a/podofo/trunk/src/base/PdfError.cpp
+++ b/podofo/trunk/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 : "" )
{
@@ -93,6 +99,12 @@
PdfError::PdfError()
{
m_error = ePdfError_ErrOk;
+}
+
+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,
--- a/podofo/trunk/src/base/PdfError.h
+++ b/podofo/trunk/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 );
@@ -184,6 +184,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 );
@@ -195,6 +196,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:
@@ -252,11 +254,21 @@
* 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
@@ -310,6 +322,21 @@
* \return the callstack
*/
inline const TDequeErrorInfo & GetCallstack() const;
+
+ /** 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 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
@@ -354,6 +381,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
*/
@@ -488,6 +530,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/podofo/trunk/src/doc/PdfPagesTree.cpp
+++ b/podofo/trunk/src/doc/PdfPagesTree.cpp
@@ -34,6 +34,7 @@
#include "PdfPagesTree.h"
#include "base/PdfDefinesPrivate.h"
+#include <algorithm>
#include "base/PdfArray.h"
#include "base/PdfDictionary.h"
@@ -478,7 +479,17 @@
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, replaced by:
+ // all the following inside this conditional, plus restart looping
+ const PdfArray & rVarArray = rVar.GetArray();
+ if (rVarArray.GetSize() == 0)
+ {
+ PdfError::LogMessage( eLogSeverity_Critical, "Trying to access"
+ " first page index of empty array" );
+ return NULL;
+ }
+ rVar = rVarArray[0];
+ continue;
}
else if( !rVar.IsReference() )
{
@@ -502,6 +513,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 {