------------------------------------------------------------------------
r1872 | mc-zyx | 2018-01-26 07:30:27 +0100 (vie, 26 ene 2018) | 2 lines

Fix by Matthias Brinke: CVE-2017-8054 - Detect cycles in PdfPagesTree


Index: src/base/PdfError.cpp
===================================================================
--- src/base/PdfError.cpp	(revision 1871)
+++ src/base/PdfError.cpp	(revision 1872)
@@ -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 );
Index: src/base/PdfError.h
===================================================================
--- src/base/PdfError.h	(revision 1871)
+++ src/base/PdfError.h	(revision 1872)
@@ -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 );
 	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,12 +254,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
      */
@@ -319,6 +331,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.
@@ -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() )
Index: src/doc/PdfPagesTree.cpp
===================================================================
--- src/doc/PdfPagesTree.cpp	(revision 1871)
+++ src/doc/PdfPagesTree.cpp	(revision 1872)
@@ -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 {

------------------------------------------------------------------------
