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:
84
buf.c
84
buf.c
@@ -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
72
tree.c
@@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user