|
|
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);
|