Blame SOURCES/libxml2-2.9.13-CVE-2022-29824.patch

ae3a24
From ecc43dce8e2cd19f635841e788421d0f4bd72cce Mon Sep 17 00:00:00 2001
ae3a24
From: Nick Wellnhofer <wellnhofer@aevum.de>
ae3a24
Date: Tue, 8 Mar 2022 20:10:02 +0100
ae3a24
Subject: [PATCH] [CVE-2022-29824] Fix integer overflows in xmlBuf and
ae3a24
 xmlBuffer
ae3a24
ae3a24
In several places, the code handling string buffers didn't check for
ae3a24
integer overflow or used wrong types for buffer sizes. This could
ae3a24
result in out-of-bounds writes or other memory errors when working on
ae3a24
large, multi-gigabyte buffers.
ae3a24
ae3a24
Thanks to Felix Wilhelm for the report.
ae3a24
---
ae3a24
 buf.c  | 86 +++++++++++++++++++++++-----------------------------------
ae3a24
 tree.c | 72 ++++++++++++++++++------------------------------
ae3a24
 2 files changed, 61 insertions(+), 97 deletions(-)
ae3a24
ae3a24
diff --git a/buf.c b/buf.c
ae3a24
index 24368d37..40a5ee06 100644
ae3a24
--- a/buf.c
ae3a24
+++ b/buf.c
ae3a24
@@ -30,6 +30,10 @@
ae3a24
 #include <libxml/parserInternals.h> /* for XML_MAX_TEXT_LENGTH */
ae3a24
 #include "buf.h"
ae3a24
 
ae3a24
+#ifndef SIZE_MAX
ae3a24
+#define SIZE_MAX ((size_t) -1)
ae3a24
+#endif
ae3a24
+
ae3a24
 #define WITH_BUFFER_COMPAT
ae3a24
 
ae3a24
 /**
ae3a24
@@ -156,6 +160,8 @@ xmlBufPtr
ae3a24
 xmlBufCreateSize(size_t size) {
ae3a24
     xmlBufPtr ret;
ae3a24
 
ae3a24
+    if (size == SIZE_MAX)
ae3a24
+        return(NULL);
ae3a24
     ret = (xmlBufPtr) xmlMalloc(sizeof(xmlBuf));
ae3a24
     if (ret == NULL) {
ae3a24
 	xmlBufMemoryError(NULL, "creating buffer");
ae3a24
@@ -166,8 +172,8 @@ xmlBufCreateSize(size_t size) {
ae3a24
     ret->error = 0;
ae3a24
     ret->buffer = NULL;
ae3a24
     ret->alloc = xmlBufferAllocScheme;
ae3a24
-    ret->size = (size ? size+2 : 0);         /* +1 for ending null */
ae3a24
-    ret->compat_size = (int) ret->size;
ae3a24
+    ret->size = (size ? size + 1 : 0);         /* +1 for ending null */
ae3a24
+    ret->compat_size = (ret->size > INT_MAX ? INT_MAX : ret->size);
ae3a24
     if (ret->size){
ae3a24
         ret->content = (xmlChar *) xmlMallocAtomic(ret->size * sizeof(xmlChar));
ae3a24
         if (ret->content == NULL) {
ae3a24
@@ -442,23 +448,17 @@ xmlBufGrowInternal(xmlBufPtr buf, size_t len) {
ae3a24
     CHECK_COMPAT(buf)
ae3a24
 
ae3a24
     if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE) return(0);
ae3a24
-    if (buf->use + len < buf->size)
ae3a24
+    if (len < buf->size - buf->use)
ae3a24
         return(buf->size - buf->use);
ae3a24
+    if (len > SIZE_MAX - buf->use)
ae3a24
+        return(0);
ae3a24
 
ae3a24
-    /*
ae3a24
-     * Windows has a BIG problem on realloc timing, so we try to double
ae3a24
-     * the buffer size (if that's enough) (bug 146697)
ae3a24
-     * Apparently BSD too, and it's probably best for linux too
ae3a24
-     * On an embedded system this may be something to change
ae3a24
-     */
ae3a24
-#if 1
ae3a24
-    if (buf->size > (size_t) len)
ae3a24
-        size = buf->size * 2;
ae3a24
-    else
ae3a24
-        size = buf->use + len + 100;
ae3a24
-#else
ae3a24
-    size = buf->use + len + 100;
ae3a24
-#endif
ae3a24
+    if (buf->size > (size_t) len) {
ae3a24
+        size = buf->size > SIZE_MAX / 2 ? SIZE_MAX : buf->size * 2;
ae3a24
+    } else {
ae3a24
+        size = buf->use + len;
ae3a24
+        size = size > SIZE_MAX - 100 ? SIZE_MAX : size + 100;
ae3a24
+    }
ae3a24
 
ae3a24
     if (buf->alloc == XML_BUFFER_ALLOC_BOUNDED) {
ae3a24
         /*
ae3a24
@@ -744,7 +744,7 @@ xmlBufIsEmpty(const xmlBufPtr buf)
ae3a24
 int
ae3a24
 xmlBufResize(xmlBufPtr buf, size_t size)
ae3a24
 {
ae3a24
-    unsigned int newSize;
ae3a24
+    size_t newSize;
ae3a24
     xmlChar* rebuf = NULL;
ae3a24
     size_t start_buf;
ae3a24
 
ae3a24
@@ -772,9 +772,13 @@ xmlBufResize(xmlBufPtr buf, size_t size)
ae3a24
 	case XML_BUFFER_ALLOC_IO:
ae3a24
 	case XML_BUFFER_ALLOC_DOUBLEIT:
ae3a24
 	    /*take care of empty case*/
ae3a24
-	    newSize = (buf->size ? buf->size*2 : size + 10);
ae3a24
+            if (buf->size == 0) {
ae3a24
+                newSize = (size > SIZE_MAX - 10 ? SIZE_MAX : size + 10);
ae3a24
+            } else {
ae3a24
+                newSize = buf->size;
ae3a24
+            }
ae3a24
 	    while (size > newSize) {
ae3a24
-	        if (newSize > UINT_MAX / 2) {
ae3a24
+	        if (newSize > SIZE_MAX / 2) {
ae3a24
 	            xmlBufMemoryError(buf, "growing buffer");
ae3a24
 	            return 0;
ae3a24
 	        }
ae3a24
@@ -782,15 +786,15 @@ xmlBufResize(xmlBufPtr buf, size_t size)
ae3a24
 	    }
ae3a24
 	    break;
ae3a24
 	case XML_BUFFER_ALLOC_EXACT:
ae3a24
-	    newSize = size+10;
ae3a24
+            newSize = (size > SIZE_MAX - 10 ? SIZE_MAX : size + 10);
ae3a24
 	    break;
ae3a24
         case XML_BUFFER_ALLOC_HYBRID:
ae3a24
             if (buf->use < BASE_BUFFER_SIZE)
ae3a24
                 newSize = size;
ae3a24
             else {
ae3a24
-                newSize = buf->size * 2;
ae3a24
+                newSize = buf->size;
ae3a24
                 while (size > newSize) {
ae3a24
-                    if (newSize > UINT_MAX / 2) {
ae3a24
+                    if (newSize > SIZE_MAX / 2) {
ae3a24
                         xmlBufMemoryError(buf, "growing buffer");
ae3a24
                         return 0;
ae3a24
                     }
ae3a24
@@ -800,7 +804,7 @@ xmlBufResize(xmlBufPtr buf, size_t size)
ae3a24
             break;
ae3a24
 
ae3a24
 	default:
ae3a24
-	    newSize = size+10;
ae3a24
+            newSize = (size > SIZE_MAX - 10 ? SIZE_MAX : size + 10);
ae3a24
 	    break;
ae3a24
     }
ae3a24
 
ae3a24
@@ -866,7 +870,7 @@ xmlBufResize(xmlBufPtr buf, size_t size)
ae3a24
  */
ae3a24
 int
ae3a24
 xmlBufAdd(xmlBufPtr buf, const xmlChar *str, int len) {
ae3a24
-    unsigned int needSize;
ae3a24
+    size_t needSize;
ae3a24
 
ae3a24
     if ((str == NULL) || (buf == NULL) || (buf->error))
ae3a24
 	return -1;
ae3a24
@@ -888,8 +892,10 @@ xmlBufAdd(xmlBufPtr buf, const xmlChar *str, int len) {
ae3a24
     if (len < 0) return -1;
ae3a24
     if (len == 0) return 0;
ae3a24
 
ae3a24
-    needSize = buf->use + len + 2;
ae3a24
-    if (needSize > buf->size){
ae3a24
+    if ((size_t) len >= buf->size - buf->use) {
ae3a24
+        if ((size_t) len >= SIZE_MAX - buf->use)
ae3a24
+            return(-1);
ae3a24
+        needSize = buf->use + len + 1;
ae3a24
 	if (buf->alloc == XML_BUFFER_ALLOC_BOUNDED) {
ae3a24
 	    /*
ae3a24
 	     * Used to provide parsing limits
ae3a24
@@ -1025,31 +1031,7 @@ xmlBufCat(xmlBufPtr buf, const xmlChar *str) {
ae3a24
  */
ae3a24
 int
ae3a24
 xmlBufCCat(xmlBufPtr buf, const char *str) {
ae3a24
-    const char *cur;
ae3a24
-
ae3a24
-    if ((buf == NULL) || (buf->error))
ae3a24
-        return(-1);
ae3a24
-    CHECK_COMPAT(buf)
ae3a24
-    if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE) return -1;
ae3a24
-    if (str == NULL) {
ae3a24
-#ifdef DEBUG_BUFFER
ae3a24
-        xmlGenericError(xmlGenericErrorContext,
ae3a24
-		"xmlBufCCat: str == NULL\n");
ae3a24
-#endif
ae3a24
-	return -1;
ae3a24
-    }
ae3a24
-    for (cur = str;*cur != 0;cur++) {
ae3a24
-        if (buf->use  + 10 >= buf->size) {
ae3a24
-            if (!xmlBufResize(buf, buf->use+10)){
ae3a24
-		xmlBufMemoryError(buf, "growing buffer");
ae3a24
-                return XML_ERR_NO_MEMORY;
ae3a24
-            }
ae3a24
-        }
ae3a24
-        buf->content[buf->use++] = *cur;
ae3a24
-    }
ae3a24
-    buf->content[buf->use] = 0;
ae3a24
-    UPDATE_COMPAT(buf)
ae3a24
-    return 0;
ae3a24
+    return xmlBufCat(buf, (const xmlChar *) str);
ae3a24
 }
ae3a24
 
ae3a24
 /**
ae3a24
diff --git a/tree.c b/tree.c
ae3a24
index 9d94aa42..86afb7d6 100644
ae3a24
--- a/tree.c
ae3a24
+++ b/tree.c
ae3a24
@@ -7104,6 +7104,8 @@ xmlBufferPtr
ae3a24
 xmlBufferCreateSize(size_t size) {
ae3a24
     xmlBufferPtr ret;
ae3a24
 
ae3a24
+    if (size >= UINT_MAX)
ae3a24
+        return(NULL);
ae3a24
     ret = (xmlBufferPtr) xmlMalloc(sizeof(xmlBuffer));
ae3a24
     if (ret == NULL) {
ae3a24
 	xmlTreeErrMemory("creating buffer");
ae3a24
@@ -7111,7 +7113,7 @@ xmlBufferCreateSize(size_t size) {
ae3a24
     }
ae3a24
     ret->use = 0;
ae3a24
     ret->alloc = xmlBufferAllocScheme;
ae3a24
-    ret->size = (size ? size+2 : 0);         /* +1 for ending null */
ae3a24
+    ret->size = (size ? size + 1 : 0);         /* +1 for ending null */
ae3a24
     if (ret->size){
ae3a24
         ret->content = (xmlChar *) xmlMallocAtomic(ret->size * sizeof(xmlChar));
ae3a24
         if (ret->content == NULL) {
ae3a24
@@ -7171,6 +7173,8 @@ xmlBufferCreateStatic(void *mem, size_t size) {
ae3a24
 
ae3a24
     if ((mem == NULL) || (size == 0))
ae3a24
         return(NULL);
ae3a24
+    if (size > UINT_MAX)
ae3a24
+        return(NULL);
ae3a24
 
ae3a24
     ret = (xmlBufferPtr) xmlMalloc(sizeof(xmlBuffer));
ae3a24
     if (ret == NULL) {
ae3a24
@@ -7318,28 +7322,23 @@ xmlBufferShrink(xmlBufferPtr buf, unsigned int len) {
ae3a24
  */
ae3a24
 int
ae3a24
 xmlBufferGrow(xmlBufferPtr buf, unsigned int len) {
ae3a24
-    int size;
ae3a24
+    unsigned int size;
ae3a24
     xmlChar *newbuf;
ae3a24
 
ae3a24
     if (buf == NULL) return(-1);
ae3a24
 
ae3a24
     if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE) return(0);
ae3a24
-    if (len + buf->use < buf->size) return(0);
ae3a24
+    if (len < buf->size - buf->use)
ae3a24
+        return(0);
ae3a24
+    if (len > UINT_MAX - buf->use)
ae3a24
+        return(-1);
ae3a24
 
ae3a24
-    /*
ae3a24
-     * Windows has a BIG problem on realloc timing, so we try to double
ae3a24
-     * the buffer size (if that's enough) (bug 146697)
ae3a24
-     * Apparently BSD too, and it's probably best for linux too
ae3a24
-     * On an embedded system this may be something to change
ae3a24
-     */
ae3a24
-#if 1
ae3a24
-    if (buf->size > len)
ae3a24
-        size = buf->size * 2;
ae3a24
-    else
ae3a24
-        size = buf->use + len + 100;
ae3a24
-#else
ae3a24
-    size = buf->use + len + 100;
ae3a24
-#endif
ae3a24
+    if (buf->size > (size_t) len) {
ae3a24
+        size = buf->size > UINT_MAX / 2 ? UINT_MAX : buf->size * 2;
ae3a24
+    } else {
ae3a24
+        size = buf->use + len;
ae3a24
+        size = size > UINT_MAX - 100 ? UINT_MAX : size + 100;
ae3a24
+    }
ae3a24
 
ae3a24
     if ((buf->alloc == XML_BUFFER_ALLOC_IO) && (buf->contentIO != NULL)) {
ae3a24
         size_t start_buf = buf->content - buf->contentIO;
ae3a24
@@ -7466,7 +7465,10 @@ xmlBufferResize(xmlBufferPtr buf, unsigned int size)
ae3a24
 	case XML_BUFFER_ALLOC_IO:
ae3a24
 	case XML_BUFFER_ALLOC_DOUBLEIT:
ae3a24
 	    /*take care of empty case*/
ae3a24
-	    newSize = (buf->size ? buf->size : size + 10);
ae3a24
+            if (buf->size == 0)
ae3a24
+                newSize = (size > UINT_MAX - 10 ? UINT_MAX : size + 10);
ae3a24
+            else
ae3a24
+                newSize = buf->size;
ae3a24
 	    while (size > newSize) {
ae3a24
 	        if (newSize > UINT_MAX / 2) {
ae3a24
 	            xmlTreeErrMemory("growing buffer");
ae3a24
@@ -7476,7 +7478,7 @@ xmlBufferResize(xmlBufferPtr buf, unsigned int size)
ae3a24
 	    }
ae3a24
 	    break;
ae3a24
 	case XML_BUFFER_ALLOC_EXACT:
ae3a24
-	    newSize = size+10;
ae3a24
+	    newSize = (size > UINT_MAX - 10 ? UINT_MAX : size + 10);;
ae3a24
 	    break;
ae3a24
         case XML_BUFFER_ALLOC_HYBRID:
ae3a24
             if (buf->use < BASE_BUFFER_SIZE)
ae3a24
@@ -7494,7 +7496,7 @@ xmlBufferResize(xmlBufferPtr buf, unsigned int size)
ae3a24
             break;
ae3a24
 
ae3a24
 	default:
ae3a24
-	    newSize = size+10;
ae3a24
+	    newSize = (size > UINT_MAX - 10 ? UINT_MAX : size + 10);;
ae3a24
 	    break;
ae3a24
     }
ae3a24
 
ae3a24
@@ -7580,8 +7582,10 @@ xmlBufferAdd(xmlBufferPtr buf, const xmlChar *str, int len) {
ae3a24
     if (len < 0) return -1;
ae3a24
     if (len == 0) return 0;
ae3a24
 
ae3a24
-    needSize = buf->use + len + 2;
ae3a24
-    if (needSize > buf->size){
ae3a24
+    if ((unsigned) len >= buf->size - buf->use) {
ae3a24
+        if ((unsigned) len >= UINT_MAX - buf->use)
ae3a24
+            return XML_ERR_NO_MEMORY;
ae3a24
+        needSize = buf->use + len + 1;
ae3a24
         if (!xmlBufferResize(buf, needSize)){
ae3a24
 	    xmlTreeErrMemory("growing buffer");
ae3a24
             return XML_ERR_NO_MEMORY;
ae3a24
@@ -7694,29 +7698,7 @@ xmlBufferCat(xmlBufferPtr buf, const xmlChar *str) {
ae3a24
  */
ae3a24
 int
ae3a24
 xmlBufferCCat(xmlBufferPtr buf, const char *str) {
ae3a24
-    const char *cur;
ae3a24
-
ae3a24
-    if (buf == NULL)
ae3a24
-        return(-1);
ae3a24
-    if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE) return -1;
ae3a24
-    if (str == NULL) {
ae3a24
-#ifdef DEBUG_BUFFER
ae3a24
-        xmlGenericError(xmlGenericErrorContext,
ae3a24
-		"xmlBufferCCat: str == NULL\n");
ae3a24
-#endif
ae3a24
-	return -1;
ae3a24
-    }
ae3a24
-    for (cur = str;*cur != 0;cur++) {
ae3a24
-        if (buf->use  + 10 >= buf->size) {
ae3a24
-            if (!xmlBufferResize(buf, buf->use+10)){
ae3a24
-		xmlTreeErrMemory("growing buffer");
ae3a24
-                return XML_ERR_NO_MEMORY;
ae3a24
-            }
ae3a24
-        }
ae3a24
-        buf->content[buf->use++] = *cur;
ae3a24
-    }
ae3a24
-    buf->content[buf->use] = 0;
ae3a24
-    return 0;
ae3a24
+    return xmlBufferCat(buf, (const xmlChar *) str);
ae3a24
 }
ae3a24
 
ae3a24
 /**
ae3a24
-- 
ae3a24
2.36.0
ae3a24