1
0
mirror of https://gitlab.gnome.org/GNOME/libxml2.git synced 2025-10-24 13:33:01 +03:00

[CVE-2022-29824] Fix integer overflows in xmlBuf and xmlBuffer

In several places, the code handling string buffers didn't check for
integer overflow or used wrong types for buffer sizes. This could
result in out-of-bounds writes or other memory errors when working on
large, multi-gigabyte buffers.

Thanks to Felix Wilhelm for the report.
This commit is contained in:
Nick Wellnhofer
2022-03-08 20:10:02 +01:00
parent 433b318668
commit 6c283d83ec
2 changed files with 60 additions and 96 deletions

84
buf.c
View File

@@ -26,6 +26,10 @@
#include <libxml/parserInternals.h> /* for XML_MAX_TEXT_LENGTH */ #include <libxml/parserInternals.h> /* for XML_MAX_TEXT_LENGTH */
#include "buf.h" #include "buf.h"
#ifndef SIZE_MAX
#define SIZE_MAX ((size_t) -1)
#endif
#define WITH_BUFFER_COMPAT #define WITH_BUFFER_COMPAT
/** /**
@@ -151,6 +155,8 @@ xmlBufPtr
xmlBufCreateSize(size_t size) { xmlBufCreateSize(size_t size) {
xmlBufPtr ret; xmlBufPtr ret;
if (size == SIZE_MAX)
return(NULL);
ret = (xmlBufPtr) xmlMalloc(sizeof(xmlBuf)); ret = (xmlBufPtr) xmlMalloc(sizeof(xmlBuf));
if (ret == NULL) { if (ret == NULL) {
xmlBufMemoryError(NULL, "creating buffer"); xmlBufMemoryError(NULL, "creating buffer");
@@ -160,7 +166,7 @@ xmlBufCreateSize(size_t size) {
ret->error = 0; ret->error = 0;
ret->buffer = NULL; ret->buffer = NULL;
ret->alloc = xmlBufferAllocScheme; ret->alloc = xmlBufferAllocScheme;
ret->size = (size ? size+2 : 0); /* +1 for ending null */ ret->size = (size ? size + 1 : 0); /* +1 for ending null */
UPDATE_COMPAT(ret); UPDATE_COMPAT(ret);
if (ret->size){ if (ret->size){
ret->content = (xmlChar *) xmlMallocAtomic(ret->size * sizeof(xmlChar)); ret->content = (xmlChar *) xmlMallocAtomic(ret->size * sizeof(xmlChar));
@@ -429,23 +435,17 @@ xmlBufGrowInternal(xmlBufPtr buf, size_t len) {
CHECK_COMPAT(buf) CHECK_COMPAT(buf)
if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE) return(0); if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE) return(0);
if (buf->use + len < buf->size) if (len < buf->size - buf->use)
return(buf->size - buf->use); return(buf->size - buf->use);
if (len > SIZE_MAX - buf->use)
return(0);
/* if (buf->size > (size_t) len) {
* Windows has a BIG problem on realloc timing, so we try to double size = buf->size > SIZE_MAX / 2 ? SIZE_MAX : buf->size * 2;
* the buffer size (if that's enough) (bug 146697) } else {
* Apparently BSD too, and it's probably best for linux too size = buf->use + len;
* On an embedded system this may be something to change size = size > SIZE_MAX - 100 ? SIZE_MAX : size + 100;
*/ }
#if 1
if (buf->size > (size_t) len)
size = buf->size * 2;
else
size = buf->use + len + 100;
#else
size = buf->use + len + 100;
#endif
if (buf->alloc == XML_BUFFER_ALLOC_BOUNDED) { if (buf->alloc == XML_BUFFER_ALLOC_BOUNDED) {
/* /*
@@ -731,7 +731,7 @@ xmlBufIsEmpty(const xmlBufPtr buf)
int int
xmlBufResize(xmlBufPtr buf, size_t size) xmlBufResize(xmlBufPtr buf, size_t size)
{ {
unsigned int newSize; size_t newSize;
xmlChar* rebuf = NULL; xmlChar* rebuf = NULL;
size_t start_buf; size_t start_buf;
@@ -759,9 +759,13 @@ xmlBufResize(xmlBufPtr buf, size_t size)
case XML_BUFFER_ALLOC_IO: case XML_BUFFER_ALLOC_IO:
case XML_BUFFER_ALLOC_DOUBLEIT: case XML_BUFFER_ALLOC_DOUBLEIT:
/*take care of empty case*/ /*take care of empty case*/
newSize = (buf->size ? buf->size*2 : size + 10); if (buf->size == 0) {
newSize = (size > SIZE_MAX - 10 ? SIZE_MAX : size + 10);
} else {
newSize = buf->size;
}
while (size > newSize) { while (size > newSize) {
if (newSize > UINT_MAX / 2) { if (newSize > SIZE_MAX / 2) {
xmlBufMemoryError(buf, "growing buffer"); xmlBufMemoryError(buf, "growing buffer");
return 0; return 0;
} }
@@ -769,15 +773,15 @@ xmlBufResize(xmlBufPtr buf, size_t size)
} }
break; break;
case XML_BUFFER_ALLOC_EXACT: case XML_BUFFER_ALLOC_EXACT:
newSize = size+10; newSize = (size > SIZE_MAX - 10 ? SIZE_MAX : size + 10);
break; break;
case XML_BUFFER_ALLOC_HYBRID: case XML_BUFFER_ALLOC_HYBRID:
if (buf->use < BASE_BUFFER_SIZE) if (buf->use < BASE_BUFFER_SIZE)
newSize = size; newSize = size;
else { else {
newSize = buf->size * 2; newSize = buf->size;
while (size > newSize) { while (size > newSize) {
if (newSize > UINT_MAX / 2) { if (newSize > SIZE_MAX / 2) {
xmlBufMemoryError(buf, "growing buffer"); xmlBufMemoryError(buf, "growing buffer");
return 0; return 0;
} }
@@ -787,7 +791,7 @@ xmlBufResize(xmlBufPtr buf, size_t size)
break; break;
default: default:
newSize = size+10; newSize = (size > SIZE_MAX - 10 ? SIZE_MAX : size + 10);
break; break;
} }
@@ -853,7 +857,7 @@ xmlBufResize(xmlBufPtr buf, size_t size)
*/ */
int int
xmlBufAdd(xmlBufPtr buf, const xmlChar *str, int len) { xmlBufAdd(xmlBufPtr buf, const xmlChar *str, int len) {
unsigned int needSize; size_t needSize;
if ((str == NULL) || (buf == NULL) || (buf->error)) if ((str == NULL) || (buf == NULL) || (buf->error))
return -1; return -1;
@@ -875,8 +879,10 @@ xmlBufAdd(xmlBufPtr buf, const xmlChar *str, int len) {
if (len < 0) return -1; if (len < 0) return -1;
if (len == 0) return 0; if (len == 0) return 0;
needSize = buf->use + len + 2; if ((size_t) len >= buf->size - buf->use) {
if (needSize > buf->size){ if ((size_t) len >= SIZE_MAX - buf->use)
return(-1);
needSize = buf->use + len + 1;
if (buf->alloc == XML_BUFFER_ALLOC_BOUNDED) { if (buf->alloc == XML_BUFFER_ALLOC_BOUNDED) {
/* /*
* Used to provide parsing limits * Used to provide parsing limits
@@ -1012,31 +1018,7 @@ xmlBufCat(xmlBufPtr buf, const xmlChar *str) {
*/ */
int int
xmlBufCCat(xmlBufPtr buf, const char *str) { xmlBufCCat(xmlBufPtr buf, const char *str) {
const char *cur; return xmlBufCat(buf, (const xmlChar *) str);
if ((buf == NULL) || (buf->error))
return(-1);
CHECK_COMPAT(buf)
if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE) return -1;
if (str == NULL) {
#ifdef DEBUG_BUFFER
xmlGenericError(xmlGenericErrorContext,
"xmlBufCCat: str == NULL\n");
#endif
return -1;
}
for (cur = str;*cur != 0;cur++) {
if (buf->use + 10 >= buf->size) {
if (!xmlBufResize(buf, buf->use+10)){
xmlBufMemoryError(buf, "growing buffer");
return XML_ERR_NO_MEMORY;
}
}
buf->content[buf->use++] = *cur;
}
buf->content[buf->use] = 0;
UPDATE_COMPAT(buf)
return 0;
} }
/** /**

72
tree.c
View File

@@ -7120,6 +7120,8 @@ xmlBufferPtr
xmlBufferCreateSize(size_t size) { xmlBufferCreateSize(size_t size) {
xmlBufferPtr ret; xmlBufferPtr ret;
if (size >= UINT_MAX)
return(NULL);
ret = (xmlBufferPtr) xmlMalloc(sizeof(xmlBuffer)); ret = (xmlBufferPtr) xmlMalloc(sizeof(xmlBuffer));
if (ret == NULL) { if (ret == NULL) {
xmlTreeErrMemory("creating buffer"); xmlTreeErrMemory("creating buffer");
@@ -7127,7 +7129,7 @@ xmlBufferCreateSize(size_t size) {
} }
ret->use = 0; ret->use = 0;
ret->alloc = xmlBufferAllocScheme; ret->alloc = xmlBufferAllocScheme;
ret->size = (size ? size+2 : 0); /* +1 for ending null */ ret->size = (size ? size + 1 : 0); /* +1 for ending null */
if (ret->size){ if (ret->size){
ret->content = (xmlChar *) xmlMallocAtomic(ret->size * sizeof(xmlChar)); ret->content = (xmlChar *) xmlMallocAtomic(ret->size * sizeof(xmlChar));
if (ret->content == NULL) { if (ret->content == NULL) {
@@ -7187,6 +7189,8 @@ xmlBufferCreateStatic(void *mem, size_t size) {
if ((mem == NULL) || (size == 0)) if ((mem == NULL) || (size == 0))
return(NULL); return(NULL);
if (size > UINT_MAX)
return(NULL);
ret = (xmlBufferPtr) xmlMalloc(sizeof(xmlBuffer)); ret = (xmlBufferPtr) xmlMalloc(sizeof(xmlBuffer));
if (ret == NULL) { if (ret == NULL) {
@@ -7334,28 +7338,23 @@ xmlBufferShrink(xmlBufferPtr buf, unsigned int len) {
*/ */
int int
xmlBufferGrow(xmlBufferPtr buf, unsigned int len) { xmlBufferGrow(xmlBufferPtr buf, unsigned int len) {
int size; unsigned int size;
xmlChar *newbuf; xmlChar *newbuf;
if (buf == NULL) return(-1); if (buf == NULL) return(-1);
if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE) return(0); if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE) return(0);
if (len + buf->use < buf->size) return(0); if (len < buf->size - buf->use)
return(0);
if (len > UINT_MAX - buf->use)
return(-1);
/* if (buf->size > (size_t) len) {
* Windows has a BIG problem on realloc timing, so we try to double size = buf->size > UINT_MAX / 2 ? UINT_MAX : buf->size * 2;
* the buffer size (if that's enough) (bug 146697) } else {
* Apparently BSD too, and it's probably best for linux too size = buf->use + len;
* On an embedded system this may be something to change size = size > UINT_MAX - 100 ? UINT_MAX : size + 100;
*/ }
#if 1
if (buf->size > len)
size = buf->size * 2;
else
size = buf->use + len + 100;
#else
size = buf->use + len + 100;
#endif
if ((buf->alloc == XML_BUFFER_ALLOC_IO) && (buf->contentIO != NULL)) { if ((buf->alloc == XML_BUFFER_ALLOC_IO) && (buf->contentIO != NULL)) {
size_t start_buf = buf->content - buf->contentIO; size_t start_buf = buf->content - buf->contentIO;
@@ -7482,7 +7481,10 @@ xmlBufferResize(xmlBufferPtr buf, unsigned int size)
case XML_BUFFER_ALLOC_IO: case XML_BUFFER_ALLOC_IO:
case XML_BUFFER_ALLOC_DOUBLEIT: case XML_BUFFER_ALLOC_DOUBLEIT:
/*take care of empty case*/ /*take care of empty case*/
newSize = (buf->size ? buf->size : size + 10); if (buf->size == 0)
newSize = (size > UINT_MAX - 10 ? UINT_MAX : size + 10);
else
newSize = buf->size;
while (size > newSize) { while (size > newSize) {
if (newSize > UINT_MAX / 2) { if (newSize > UINT_MAX / 2) {
xmlTreeErrMemory("growing buffer"); xmlTreeErrMemory("growing buffer");
@@ -7492,7 +7494,7 @@ xmlBufferResize(xmlBufferPtr buf, unsigned int size)
} }
break; break;
case XML_BUFFER_ALLOC_EXACT: case XML_BUFFER_ALLOC_EXACT:
newSize = size+10; newSize = (size > UINT_MAX - 10 ? UINT_MAX : size + 10);;
break; break;
case XML_BUFFER_ALLOC_HYBRID: case XML_BUFFER_ALLOC_HYBRID:
if (buf->use < BASE_BUFFER_SIZE) if (buf->use < BASE_BUFFER_SIZE)
@@ -7510,7 +7512,7 @@ xmlBufferResize(xmlBufferPtr buf, unsigned int size)
break; break;
default: default:
newSize = size+10; newSize = (size > UINT_MAX - 10 ? UINT_MAX : size + 10);;
break; break;
} }
@@ -7596,8 +7598,10 @@ xmlBufferAdd(xmlBufferPtr buf, const xmlChar *str, int len) {
if (len < 0) return -1; if (len < 0) return -1;
if (len == 0) return 0; if (len == 0) return 0;
needSize = buf->use + len + 2; if ((unsigned) len >= buf->size - buf->use) {
if (needSize > buf->size){ if ((unsigned) len >= UINT_MAX - buf->use)
return XML_ERR_NO_MEMORY;
needSize = buf->use + len + 1;
if (!xmlBufferResize(buf, needSize)){ if (!xmlBufferResize(buf, needSize)){
xmlTreeErrMemory("growing buffer"); xmlTreeErrMemory("growing buffer");
return XML_ERR_NO_MEMORY; return XML_ERR_NO_MEMORY;
@@ -7710,29 +7714,7 @@ xmlBufferCat(xmlBufferPtr buf, const xmlChar *str) {
*/ */
int int
xmlBufferCCat(xmlBufferPtr buf, const char *str) { xmlBufferCCat(xmlBufferPtr buf, const char *str) {
const char *cur; return xmlBufferCat(buf, (const xmlChar *) str);
if (buf == NULL)
return(-1);
if (buf->alloc == XML_BUFFER_ALLOC_IMMUTABLE) return -1;
if (str == NULL) {
#ifdef DEBUG_BUFFER
xmlGenericError(xmlGenericErrorContext,
"xmlBufferCCat: str == NULL\n");
#endif
return -1;
}
for (cur = str;*cur != 0;cur++) {
if (buf->use + 10 >= buf->size) {
if (!xmlBufferResize(buf, buf->use+10)){
xmlTreeErrMemory("growing buffer");
return XML_ERR_NO_MEMORY;
}
}
buf->content[buf->use++] = *cur;
}
buf->content[buf->use] = 0;
return 0;
} }
/** /**