CVE-2023-2241 (published 2023-04-22T16:15Z)

A vulnerability, which was classified as critical, was found in PoDoFo 0.10.0. Affected is the function readXRefStreamEntry of the file PdfXRefStreamParserObject.cpp. The manipulation leads to heap-based buffer overflow. An attack has to be approached locally. The exploit has been disclosed to the public and may be used. The patch is identified as 535a786f124b739e3c857529cecc29e4eeb79778. It is recommended to apply a patch to fix this issue. VDB-227226 is the identifier assigned to this vulnerability.

CVSS v3 base score: 7.8, HIGH (CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-787
cwe_name: Out-of-bounds Write
description: The product writes data past the end, or before the beginning, of the intended buffer.
extended_description: 
            
            
             
			 
         
         

Fix information:
repo_url: https://github.com/podofo/podofo
commit_details: repo_url: https://github.com/podofo/podofo
author: Francesco Pretto
author_date: 2023-04-21 16:54:24+02:00
msg: PdfXRefStreamParserObject: Fixed handling of invalid XRef stream entries

Should fix #69
repository: repo_url: https://github.com/podofo/podofo
repo_name: podofo/podofo
description: A C++17 PDF manipulation library
repo_language: C++
owner: podofo
file_changes: filename: CMakeLists.txt
change_type: ModificationType.MODIFY
diff: @@ -8,7 +8,7 @@ endif()
 unset(CMAKE_CONFIGURATION_TYPES CACHE) # It will also remove it from GUI
 set(CMAKE_CONFIGURATION_TYPES "${CMAKE_BUILD_TYPE}")
 
-project(PoDoFo)
+project(podofo)
 enable_testing() # Needed to enable "make test" from binary dir
 
 # An useful variable to check if we are running from playground

programming_language: Shell
filename: PdfXRefStreamParserObject.cpp
change_type: ModificationType.MODIFY
diff: @@ -67,7 +67,7 @@ void PdfXRefStreamParserObject::ReadXRefTable()
     // all of them have to be integers
     const PdfArray* arr;
     if (!arrObj.TryGetArray(arr) || arr->size() != 3)
-        PODOFO_RAISE_ERROR_INFO(PdfErrorCode::NoXRef, "Invalid XRef stream /W array");
+        PODOFO_RAISE_ERROR_INFO(PdfErrorCode::InvalidXRefStream, "Invalid XRef stream /W array");
 
     int64_t wArray[W_ARRAY_SIZE] = { 0, 0, 0 };
     int64_t num;
@@ -75,7 +75,7 @@ void PdfXRefStreamParserObject::ReadXRefTable()
     {
 
         if (!(*arr)[i].TryGetNumber(num))
-            PODOFO_RAISE_ERROR_INFO(PdfErrorCode::NoXRef, "Invalid XRef stream /W array");
+            PODOFO_RAISE_ERROR_INFO(PdfErrorCode::InvalidXRefStream, "Invalid XRef stream /W array");
 
         wArray[i] = num;
     }
@@ -92,12 +92,12 @@ void PdfXRefStreamParserObject::parseStream(const int64_t wArray[W_ARRAY_SIZE],
     {
         if (wArray[i] < 0)
         {
-            PODOFO_RAISE_ERROR_INFO(PdfErrorCode::NoXRef,
+            PODOFO_RAISE_ERROR_INFO(PdfErrorCode::InvalidXRefStream,
                 "Negative field length in XRef stream");
         }
         if (numeric_limits<int64_t>::max() - lengthSum < wArray[i])
         {
-            PODOFO_RAISE_ERROR_INFO(PdfErrorCode::NoXRef,
+            PODOFO_RAISE_ERROR_INFO(PdfErrorCode::InvalidXRefStream,
                 "Invalid entry length in XRef stream");
         }
         else
@@ -112,24 +112,24 @@ void PdfXRefStreamParserObject::parseStream(const int64_t wArray[W_ARRAY_SIZE],
     this->GetOrCreateStream().CopyTo(buffer);
 
     vector<int64_t>::const_iterator it = indices.begin();
-    char* cursor = buffer.data();
+    size_t offset = 0;
     while (it != indices.end())
     {
         int64_t firstObj = *it++;
         int64_t count = *it++;
 
+        if ((offset + count * entryLen) > buffer.size())
+            PODOFO_RAISE_ERROR_INFO(PdfErrorCode::InvalidXRefStream, "Invalid count in XRef stream");
+
         m_entries->Enlarge(firstObj + count);
         for (unsigned index = 0; index < (unsigned)count; index++)
         {
-            if ((size_t)(cursor - buffer.data()) >= buffer.size())
-                PODOFO_RAISE_ERROR_INFO(PdfErrorCode::NoXRef, "Invalid count in XRef stream");
-
             unsigned objIndex = (unsigned)firstObj + index;
             auto& entry = (*m_entries)[objIndex];
             if (objIndex < m_entries->GetSize() && !entry.Parsed)
-                readXRefStreamEntry(entry, cursor, wArray);
+                readXRefStreamEntry(entry, buffer.data() + offset, wArray);
 
-            cursor += entryLen;
+            offset += entryLen;
         }
     }
 }
@@ -149,7 +149,7 @@ void PdfXRefStreamParserObject::getIndices(vector<int64_t>& indices, int64_t siz
     {
         const PdfArray* arr;
         if (!indexObj->TryGetArray(arr))
-            PODOFO_RAISE_ERROR_INFO(PdfErrorCode::NoXRef, "Invalid XRef Stream /Index");
+            PODOFO_RAISE_ERROR_INFO(PdfErrorCode::InvalidXRefStream, "Invalid XRef Stream /Index");
 
         for (auto index : *arr)
             indices.push_back(index.GetNumber());
@@ -157,7 +157,7 @@ void PdfXRefStreamParserObject::getIndices(vector<int64_t>& indices, int64_t siz
 
     // indices must be a multiple of 2
     if (indices.size() % 2 != 0)
-        PODOFO_RAISE_ERROR_INFO(PdfErrorCode::NoXRef, "Invalid XRef Stream /Index");
+        PODOFO_RAISE_ERROR_INFO(PdfErrorCode::InvalidXRefStream, "Invalid XRef Stream /Index");
 }
 
 void PdfXRefStreamParserObject::readXRefStreamEntry(PdfXRefEntry& entry, char* buffer, const int64_t wArray[W_ARRAY_SIZE])

programming_language: C++
filename: ParserTest.cpp
change_type: ModificationType.MODIFY
diff: @@ -1060,7 +1060,7 @@ void testReadXRefSubsection()
     }
 }
 
-TEST_CASE("testReadXRefStreamContents")
+TEST_CASE("TestReadXRefStreamContents")
 {
     // test valid stream
     try
@@ -1157,7 +1157,7 @@ TEST_CASE("testReadXRefStreamContents")
     }
     catch (PdfError& error)
     {
-        REQUIRE(error.GetCode() == PdfErrorCode::NoXRef);
+        REQUIRE(error.GetCode() == PdfErrorCode::InvalidXRefStream);
     }
     catch (exception&)
     {
@@ -1210,7 +1210,7 @@ TEST_CASE("testReadXRefStreamContents")
     }
     catch (PdfError& error)
     {
-        REQUIRE(error.GetCode() == PdfErrorCode::NoXRef);
+        REQUIRE(error.GetCode() == PdfErrorCode::InvalidXRefStream);
     }
     catch (exception&)
     {
@@ -1314,7 +1314,7 @@ TEST_CASE("testReadXRefStreamContents")
     }
     catch (PdfError& error)
     {
-        REQUIRE(error.GetCode() == PdfErrorCode::InvalidXRefType);
+        REQUIRE(error.GetCode() == PdfErrorCode::InvalidXRefStream);
     }
     catch (exception&)
     {
@@ -1363,7 +1363,7 @@ TEST_CASE("testReadXRefStreamContents")
     }
     catch (PdfError& error)
     {
-        REQUIRE(error.GetCode() == PdfErrorCode::NoXRef);
+        REQUIRE(error.GetCode() == PdfErrorCode::InvalidXRefStream);
     }
     catch (exception&)
     {
@@ -1722,7 +1722,7 @@ TEST_CASE("testReadXRefStreamContents")
     }
     catch (PdfError& error)
     {
-        REQUIRE(error.GetCode() == PdfErrorCode::NoXRef);
+        REQUIRE(error.GetCode() == PdfErrorCode::InvalidXRefStream);
     }
     catch (exception&)
     {
@@ -1772,7 +1772,7 @@ TEST_CASE("testReadXRefStreamContents")
     }
     catch (PdfError& error)
     {
-        REQUIRE(error.GetCode() == PdfErrorCode::NoXRef);
+        REQUIRE(error.GetCode() == PdfErrorCode::InvalidXRefStream);
     }
     catch (exception&)
     {

programming_language: C++