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

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