Blame SOURCES/expat-2.1.0-Detect-and-prevent-integer-overflow-in-XML_GetBuffer.patch

4cc7ea
commit 513535dbf339c5c5dd38b77809c6a88969c05109
4cc7ea
Author: Tomas Korbar <tkorbar@redhat.com>
4cc7ea
Date:   Mon Feb 21 13:42:02 2022 +0100
4cc7ea
4cc7ea
    CVE-2022-23852
4cc7ea
4cc7ea
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
4cc7ea
index 2ebd80f..d4f30b7 100644
4cc7ea
--- a/lib/xmlparse.c
4cc7ea
+++ b/lib/xmlparse.c
4cc7ea
@@ -75,6 +75,9 @@ typedef char ICHAR;
4cc7ea
 /* Round up n to be a multiple of sz, where sz is a power of 2. */
4cc7ea
 #define ROUND_UP(n, sz) (((n) + ((sz) - 1)) & ~((sz) - 1))
4cc7ea
 
4cc7ea
+/* Do safe (NULL-aware) pointer arithmetic */
4cc7ea
+#define EXPAT_SAFE_PTR_DIFF(p, q) (((p) && (q)) ? ((p) - (q)) : 0)
4cc7ea
+
4cc7ea
 /* Handle the case where memmove() doesn't exist. */
4cc7ea
 #ifndef HAVE_MEMMOVE
4cc7ea
 #ifdef HAVE_BCOPY
4cc7ea
@@ -1678,50 +1681,70 @@ XML_ParseBuffer(XML_Parser parser, int len, int isFinal)
4cc7ea
 void * XMLCALL
4cc7ea
 XML_GetBuffer(XML_Parser parser, int len)
4cc7ea
 {
4cc7ea
+  if (parser == NULL)
4cc7ea
+    return NULL;
4cc7ea
   if (len < 0) {
4cc7ea
-    errorCode = XML_ERROR_NO_MEMORY;
4cc7ea
+    parser->m_errorCode = XML_ERROR_NO_MEMORY;
4cc7ea
     return NULL;
4cc7ea
   }
4cc7ea
-  switch (ps_parsing) {
4cc7ea
+  switch (parser->m_parsingStatus.parsing) {
4cc7ea
   case XML_SUSPENDED:
4cc7ea
-    errorCode = XML_ERROR_SUSPENDED;
4cc7ea
+    parser->m_errorCode = XML_ERROR_SUSPENDED;
4cc7ea
     return NULL;
4cc7ea
   case XML_FINISHED:
4cc7ea
-    errorCode = XML_ERROR_FINISHED;
4cc7ea
+    parser->m_errorCode = XML_ERROR_FINISHED;
4cc7ea
     return NULL;
4cc7ea
   default: ;
4cc7ea
   }
4cc7ea
 
4cc7ea
-  if (len > bufferLim - bufferEnd) {
4cc7ea
-    int neededSize = (int) ((unsigned)len + (unsigned)(bufferEnd - bufferPtr));
4cc7ea
+  if (len > EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferEnd)) {
4cc7ea
+    int keep;
4cc7ea
+    /* Do not invoke signed arithmetic overflow: */
4cc7ea
+    int neededSize = (int)((unsigned)len
4cc7ea
+                           + (unsigned)EXPAT_SAFE_PTR_DIFF(
4cc7ea
+                               parser->m_bufferEnd, parser->m_bufferPtr));
4cc7ea
     if (neededSize < 0) {
4cc7ea
-      errorCode = XML_ERROR_NO_MEMORY;
4cc7ea
+      parser->m_errorCode = XML_ERROR_NO_MEMORY;
4cc7ea
       return NULL;
4cc7ea
     }
4cc7ea
-#ifdef XML_CONTEXT_BYTES
4cc7ea
-    int keep = (int)(bufferPtr - buffer);
4cc7ea
 
4cc7ea
+    keep = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer);
4cc7ea
     if (keep > XML_CONTEXT_BYTES)
4cc7ea
       keep = XML_CONTEXT_BYTES;
4cc7ea
+    /* Detect and prevent integer overflow */
4cc7ea
+    if (keep > INT_MAX - neededSize) {
4cc7ea
+      parser->m_errorCode = XML_ERROR_NO_MEMORY;
4cc7ea
+      return NULL;
4cc7ea
+    }
4cc7ea
     neededSize += keep;
4cc7ea
-#endif  /* defined XML_CONTEXT_BYTES */
4cc7ea
-    if (neededSize  <= bufferLim - buffer) {
4cc7ea
+    if (neededSize
4cc7ea
+        <= EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_buffer)) {
4cc7ea
 #ifdef XML_CONTEXT_BYTES
4cc7ea
-      if (keep < bufferPtr - buffer) {
4cc7ea
-        int offset = (int)(bufferPtr - buffer) - keep;
4cc7ea
-        memmove(buffer, &buffer[offset], bufferEnd - bufferPtr + keep);
4cc7ea
-        bufferEnd -= offset;
4cc7ea
-        bufferPtr -= offset;
4cc7ea
+      if (keep < EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer)) {
4cc7ea
+        int offset
4cc7ea
+            = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer)
4cc7ea
+              - keep;
4cc7ea
+        /* The buffer pointers cannot be NULL here; we have at least some bytes
4cc7ea
+         * in the buffer */
4cc7ea
+        memmove(parser->m_buffer, &parser->m_buffer[offset],
4cc7ea
+                parser->m_bufferEnd - parser->m_bufferPtr + keep);
4cc7ea
+        parser->m_bufferEnd -= offset;
4cc7ea
+        parser->m_bufferPtr -= offset;
4cc7ea
       }
4cc7ea
 #else
4cc7ea
-      memmove(buffer, bufferPtr, bufferEnd - bufferPtr);
4cc7ea
-      bufferEnd = buffer + (bufferEnd - bufferPtr);
4cc7ea
-      bufferPtr = buffer;
4cc7ea
-#endif  /* not defined XML_CONTEXT_BYTES */
4cc7ea
-    }
4cc7ea
-    else {
4cc7ea
+      if (parser->m_buffer && parser->m_bufferPtr) {
4cc7ea
+        memmove(parser->m_buffer, parser->m_bufferPtr,
4cc7ea
+                EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr));
4cc7ea
+        parser->m_bufferEnd
4cc7ea
+            = parser->m_buffer
4cc7ea
+              + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr);
4cc7ea
+        parser->m_bufferPtr = parser->m_buffer;
4cc7ea
+      }
4cc7ea
+#endif /* not defined XML_CONTEXT_BYTES */
4cc7ea
+    } else {
4cc7ea
       char *newBuf;
4cc7ea
-      int bufferSize = (int)(bufferLim - bufferPtr);
4cc7ea
+      int bufferSize
4cc7ea
+          = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferPtr);
4cc7ea
       if (bufferSize == 0)
4cc7ea
         bufferSize = INIT_BUFFER_SIZE;
4cc7ea
       do {
4cc7ea
@@ -1729,43 +1752,51 @@ XML_GetBuffer(XML_Parser parser, int len)
4cc7ea
         bufferSize = (int) (2U * (unsigned) bufferSize);
4cc7ea
       } while (bufferSize < neededSize && bufferSize > 0);
4cc7ea
       if (bufferSize <= 0) {
4cc7ea
-        errorCode = XML_ERROR_NO_MEMORY;
4cc7ea
+        parser->m_errorCode = XML_ERROR_NO_MEMORY;
4cc7ea
         return NULL;
4cc7ea
       }
4cc7ea
       newBuf = (char *)MALLOC(bufferSize);
4cc7ea
       if (newBuf == 0) {
4cc7ea
-        errorCode = XML_ERROR_NO_MEMORY;
4cc7ea
+        parser->m_errorCode = XML_ERROR_NO_MEMORY;
4cc7ea
         return NULL;
4cc7ea
       }
4cc7ea
-      bufferLim = newBuf + bufferSize;
4cc7ea
+      parser->m_bufferLim = newBuf + bufferSize;
4cc7ea
 #ifdef XML_CONTEXT_BYTES
4cc7ea
-      if (bufferPtr) {
4cc7ea
-        int keep = (int)(bufferPtr - buffer);
4cc7ea
-        if (keep > XML_CONTEXT_BYTES)
4cc7ea
-          keep = XML_CONTEXT_BYTES;
4cc7ea
-        memcpy(newBuf, &bufferPtr[-keep], bufferEnd - bufferPtr + keep);
4cc7ea
-        FREE(buffer);
4cc7ea
-        buffer = newBuf;
4cc7ea
-        bufferEnd = buffer + (bufferEnd - bufferPtr) + keep;
4cc7ea
-        bufferPtr = buffer + keep;
4cc7ea
-      }
4cc7ea
-      else {
4cc7ea
-        bufferEnd = newBuf + (bufferEnd - bufferPtr);
4cc7ea
-        bufferPtr = buffer = newBuf;
4cc7ea
+      if (parser->m_bufferPtr) {
4cc7ea
+        memcpy(newBuf, &parser->m_bufferPtr[-keep],
4cc7ea
+               EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr)
4cc7ea
+                   + keep);
4cc7ea
+        FREE(parser->m_buffer);
4cc7ea
+        parser->m_buffer = newBuf;
4cc7ea
+        parser->m_bufferEnd
4cc7ea
+            = parser->m_buffer
4cc7ea
+              + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr)
4cc7ea
+              + keep;
4cc7ea
+        parser->m_bufferPtr = parser->m_buffer + keep;
4cc7ea
+      } else {
4cc7ea
+        /* This must be a brand new buffer with no data in it yet */
4cc7ea
+        parser->m_bufferEnd = newBuf;
4cc7ea
+        parser->m_bufferPtr = parser->m_buffer = newBuf;
4cc7ea
       }
4cc7ea
 #else
4cc7ea
-      if (bufferPtr) {
4cc7ea
-        memcpy(newBuf, bufferPtr, bufferEnd - bufferPtr);
4cc7ea
-        FREE(buffer);
4cc7ea
+      if (parser->m_bufferPtr) {
4cc7ea
+        memcpy(newBuf, parser->m_bufferPtr,
4cc7ea
+               EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr));
4cc7ea
+        FREE(parser->m_buffer);
4cc7ea
+        parser->m_bufferEnd
4cc7ea
+            = newBuf
4cc7ea
+              + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr);
4cc7ea
+      } else {
4cc7ea
+        /* This must be a brand new buffer with no data in it yet */
4cc7ea
+        parser->m_bufferEnd = newBuf;
4cc7ea
       }
4cc7ea
-      bufferEnd = newBuf + (bufferEnd - bufferPtr);
4cc7ea
-      bufferPtr = buffer = newBuf;
4cc7ea
+      parser->m_bufferPtr = parser->m_buffer = newBuf;
4cc7ea
 #endif  /* not defined XML_CONTEXT_BYTES */
4cc7ea
     }
4cc7ea
-    eventPtr = eventEndPtr = NULL;
4cc7ea
-    positionPtr = NULL;
4cc7ea
+    parser->m_eventPtr = parser->m_eventEndPtr = NULL;
4cc7ea
+    parser->m_positionPtr = NULL;
4cc7ea
   }
4cc7ea
-  return bufferEnd;
4cc7ea
+  return parser->m_bufferEnd;
4cc7ea
 }
4cc7ea
 
4cc7ea
 enum XML_Status XMLCALL
4cc7ea
diff --git a/tests/runtests.c b/tests/runtests.c
4cc7ea
index 233956e..b99e375 100644
4cc7ea
--- a/tests/runtests.c
4cc7ea
+++ b/tests/runtests.c
4cc7ea
@@ -13,6 +13,7 @@
4cc7ea
 #include <stdio.h>
4cc7ea
 #include <string.h>
4cc7ea
 #include <stdint.h>
4cc7ea
+#include <limits.h>
4cc7ea
 
4cc7ea
 #include "expat.h"
4cc7ea
 #include "chardata.h"
4cc7ea
@@ -149,6 +150,30 @@ dummy_start_element(void *userData,
4cc7ea
 {}
4cc7ea
 
4cc7ea
 
4cc7ea
+/* Test for signed integer overflow CVE-2022-23852 */
4cc7ea
+#if defined(XML_CONTEXT_BYTES)
4cc7ea
+START_TEST(test_get_buffer_3_overflow) {
4cc7ea
+  XML_Parser parser = XML_ParserCreate(NULL);
4cc7ea
+  assert(parser != NULL);
4cc7ea
+
4cc7ea
+  const char *const text = "\n";
4cc7ea
+  const int expectedKeepValue = (int)strlen(text);
4cc7ea
+
4cc7ea
+  // After this call, variable "keep" in XML_GetBuffer will
4cc7ea
+  // have value expectedKeepValue
4cc7ea
+  if (XML_Parse(parser, text, (int)strlen(text), XML_FALSE /* isFinal */)
4cc7ea
+      == XML_STATUS_ERROR)
4cc7ea
+    xml_failure(parser);
4cc7ea
+
4cc7ea
+  assert(expectedKeepValue > 0);
4cc7ea
+  if (XML_GetBuffer(parser, INT_MAX - expectedKeepValue + 1) != NULL)
4cc7ea
+    fail("enlarging buffer not failed");
4cc7ea
+
4cc7ea
+  XML_ParserFree(parser);
4cc7ea
+}
4cc7ea
+END_TEST
4cc7ea
+#endif // defined(XML_CONTEXT_BYTES)
4cc7ea
+
4cc7ea
 /*
4cc7ea
  * Character & encoding tests.
4cc7ea
  */
4cc7ea
@@ -1483,6 +1508,9 @@ make_suite(void)
4cc7ea
 
4cc7ea
     suite_add_tcase(s, tc_basic);
4cc7ea
     tcase_add_checked_fixture(tc_basic, basic_setup, basic_teardown);
4cc7ea
+#if defined(XML_CONTEXT_BYTES)
4cc7ea
+    tcase_add_test(tc_basic, test_get_buffer_3_overflow);
4cc7ea
+#endif
4cc7ea
     tcase_add_test(tc_basic, test_nul_byte);
4cc7ea
     tcase_add_test(tc_basic, test_u0000_char);
4cc7ea
     tcase_add_test(tc_basic, test_bom_utf8);