Compare commits

...

No commits in common. "c8" and "c8-beta" have entirely different histories.
c8 ... c8-beta

28 changed files with 1672 additions and 7492 deletions

View File

@ -1 +1 @@
03d9882ede56aa48919fbf50fe17614630257a82 SOURCES/expat-2.5.0.tar.gz fa46ccce6770ccae767c28f6ac55e2428089d4a0 SOURCES/expat-2.2.5.tar.gz

2
.gitignore vendored
View File

@ -1 +1 @@
SOURCES/expat-2.5.0.tar.gz SOURCES/expat-2.2.5.tar.gz

View File

@ -0,0 +1,200 @@
commit e8f285b522a907603501329e5b4212755f525fdf
Author: Tomas Korbar <tkorbar@redhat.com>
Date: Thu Mar 3 12:04:09 2022 +0100
CVE-2022-25235
diff --git a/lib/xmltok.c b/lib/xmltok.c
index 6b415d8..b55732a 100644
--- a/lib/xmltok.c
+++ b/lib/xmltok.c
@@ -103,13 +103,6 @@
+ ((((byte)[2]) >> 5) & 1)] \
& (1u << (((byte)[2]) & 0x1F)))
-#define UTF8_GET_NAMING(pages, p, n) \
- ((n) == 2 \
- ? UTF8_GET_NAMING2(pages, (const unsigned char *)(p)) \
- : ((n) == 3 \
- ? UTF8_GET_NAMING3(pages, (const unsigned char *)(p)) \
- : 0))
-
/* Detection of invalid UTF-8 sequences is based on Table 3.1B
of Unicode 3.2: http://www.unicode.org/unicode/reports/tr28/
with the additional restriction of not allowing the Unicode
diff --git a/lib/xmltok_impl.c b/lib/xmltok_impl.c
index 0403dd3..56d7a40 100644
--- a/lib/xmltok_impl.c
+++ b/lib/xmltok_impl.c
@@ -61,7 +61,7 @@
case BT_LEAD ## n: \
if (end - ptr < n) \
return XML_TOK_PARTIAL_CHAR; \
- if (!IS_NAME_CHAR(enc, ptr, n)) { \
+ if (IS_INVALID_CHAR(enc, ptr, n) || ! IS_NAME_CHAR(enc, ptr, n)) { \
*nextTokPtr = ptr; \
return XML_TOK_INVALID; \
} \
@@ -89,7 +89,7 @@
case BT_LEAD ## n: \
if (end - ptr < n) \
return XML_TOK_PARTIAL_CHAR; \
- if (!IS_NMSTRT_CHAR(enc, ptr, n)) { \
+ if (IS_INVALID_CHAR(enc, ptr, n) || ! IS_NMSTRT_CHAR(enc, ptr, n)) { \
*nextTokPtr = ptr; \
return XML_TOK_INVALID; \
} \
@@ -1117,6 +1117,10 @@ PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end,
case BT_LEAD ## n: \
if (end - ptr < n) \
return XML_TOK_PARTIAL_CHAR; \
+ if (IS_INVALID_CHAR(enc, ptr, n)) { \
+ *nextTokPtr = ptr; \
+ return XML_TOK_INVALID; \
+ } \
if (IS_NMSTRT_CHAR(enc, ptr, n)) { \
ptr += n; \
tok = XML_TOK_NAME; \
diff --git a/tests/runtests.c b/tests/runtests.c
index 278bfa1..0f3afde 100644
--- a/tests/runtests.c
+++ b/tests/runtests.c
@@ -6540,6 +6540,106 @@ START_TEST(test_utf8_in_cdata_section_2)
}
END_TEST
+START_TEST(test_utf8_in_start_tags) {
+ struct test_case {
+ bool goodName;
+ bool goodNameStart;
+ const char *tagName;
+ };
+
+ // The idea with the tests below is this:
+ // We want to cover 1-, 2- and 3-byte sequences, 4-byte sequences
+ // go to isNever and are hence not a concern.
+ //
+ // We start with a character that is a valid name character
+ // (or even name-start character, see XML 1.0r4 spec) and then we flip
+ // single bits at places where (1) the result leaves the UTF-8 encoding space
+ // and (2) we stay in the same n-byte sequence family.
+ //
+ // The flipped bits are highlighted in angle brackets in comments,
+ // e.g. "[<1>011 1001]" means we had [0011 1001] but we now flipped
+ // the most significant bit to 1 to leave UTF-8 encoding space.
+ struct test_case cases[] = {
+ // 1-byte UTF-8: [0xxx xxxx]
+ {true, true, "\x3A"}, // [0011 1010] = ASCII colon ':'
+ {false, false, "\xBA"}, // [<1>011 1010]
+ {true, false, "\x39"}, // [0011 1001] = ASCII nine '9'
+ {false, false, "\xB9"}, // [<1>011 1001]
+
+ // 2-byte UTF-8: [110x xxxx] [10xx xxxx]
+ {true, true, "\xDB\xA5"}, // [1101 1011] [1010 0101] =
+ // Arabic small waw U+06E5
+ {false, false, "\x9B\xA5"}, // [1<0>01 1011] [1010 0101]
+ {false, false, "\xDB\x25"}, // [1101 1011] [<0>010 0101]
+ {false, false, "\xDB\xE5"}, // [1101 1011] [1<1>10 0101]
+ {true, false, "\xCC\x81"}, // [1100 1100] [1000 0001] =
+ // combining char U+0301
+ {false, false, "\x8C\x81"}, // [1<0>00 1100] [1000 0001]
+ {false, false, "\xCC\x01"}, // [1100 1100] [<0>000 0001]
+ {false, false, "\xCC\xC1"}, // [1100 1100] [1<1>00 0001]
+
+ // 3-byte UTF-8: [1110 xxxx] [10xx xxxx] [10xxxxxx]
+ {true, true, "\xE0\xA4\x85"}, // [1110 0000] [1010 0100] [1000 0101] =
+ // Devanagari Letter A U+0905
+ {false, false, "\xA0\xA4\x85"}, // [1<0>10 0000] [1010 0100] [1000 0101]
+ {false, false, "\xE0\x24\x85"}, // [1110 0000] [<0>010 0100] [1000 0101]
+ {false, false, "\xE0\xE4\x85"}, // [1110 0000] [1<1>10 0100] [1000 0101]
+ {false, false, "\xE0\xA4\x05"}, // [1110 0000] [1010 0100] [<0>000 0101]
+ {false, false, "\xE0\xA4\xC5"}, // [1110 0000] [1010 0100] [1<1>00 0101]
+ {true, false, "\xE0\xA4\x81"}, // [1110 0000] [1010 0100] [1000 0001] =
+ // combining char U+0901
+ {false, false, "\xA0\xA4\x81"}, // [1<0>10 0000] [1010 0100] [1000 0001]
+ {false, false, "\xE0\x24\x81"}, // [1110 0000] [<0>010 0100] [1000 0001]
+ {false, false, "\xE0\xE4\x81"}, // [1110 0000] [1<1>10 0100] [1000 0001]
+ {false, false, "\xE0\xA4\x01"}, // [1110 0000] [1010 0100] [<0>000 0001]
+ {false, false, "\xE0\xA4\xC1"}, // [1110 0000] [1010 0100] [1<1>00 0001]
+ };
+ const bool atNameStart[] = {true, false};
+
+ size_t i = 0;
+ char doc[1024];
+ size_t failCount = 0;
+
+ for (; i < sizeof(cases) / sizeof(cases[0]); i++) {
+ size_t j = 0;
+ for (; j < sizeof(atNameStart) / sizeof(atNameStart[0]); j++) {
+ const bool expectedSuccess
+ = atNameStart[j] ? cases[i].goodNameStart : cases[i].goodName;
+ sprintf(doc, "<%s%s><!--", atNameStart[j] ? "" : "a", cases[i].tagName);
+ XML_Parser parser = XML_ParserCreate(NULL);
+
+ const enum XML_Status status
+ = XML_Parse(parser, doc, (int)strlen(doc), /*isFinal=*/XML_FALSE);
+
+ bool success = true;
+ if ((status == XML_STATUS_OK) != expectedSuccess) {
+ success = false;
+ }
+ if ((status == XML_STATUS_ERROR)
+ && (XML_GetErrorCode(parser) != XML_ERROR_INVALID_TOKEN)) {
+ success = false;
+ }
+
+ if (! success) {
+ fprintf(
+ stderr,
+ "FAIL case %2u (%sat name start, %u-byte sequence, error code %d)\n",
+ (unsigned)i + 1u, atNameStart[j] ? " " : "not ",
+ (unsigned)strlen(cases[i].tagName), XML_GetErrorCode(parser));
+ failCount++;
+ }
+
+ XML_ParserFree(parser);
+ }
+ }
+
+ if (failCount > 0) {
+ fail("UTF-8 regression detected");
+ }
+}
+END_TEST
+
+
/* Test trailing spaces in elements are accepted */
static void XMLCALL
record_element_end_handler(void *userData,
@@ -6734,6 +6834,15 @@ START_TEST(test_bad_doctype)
}
END_TEST
+START_TEST(test_bad_doctype_utf8) {
+ const char *text = "<!DOCTYPE \xDB\x25"
+ "doc><doc/>"; // [1101 1011] [<0>010 0101]
+ expect_failure(text, XML_ERROR_INVALID_TOKEN,
+ "Invalid UTF-8 in DOCTYPE not faulted");
+}
+END_TEST
+
+
START_TEST(test_bad_doctype_utf16)
{
const char text[] =
@@ -12256,6 +12365,7 @@ make_suite(void)
tcase_add_test(tc_basic, test_ext_entity_utf8_non_bom);
tcase_add_test(tc_basic, test_utf8_in_cdata_section);
tcase_add_test(tc_basic, test_utf8_in_cdata_section_2);
+ tcase_add_test(tc_basic, test_utf8_in_start_tags);
tcase_add_test(tc_basic, test_trailing_spaces_in_elements);
tcase_add_test(tc_basic, test_utf16_attribute);
tcase_add_test(tc_basic, test_utf16_second_attr);
@@ -12264,6 +12374,7 @@ make_suite(void)
tcase_add_test(tc_basic, test_bad_attr_desc_keyword);
tcase_add_test(tc_basic, test_bad_attr_desc_keyword_utf16);
tcase_add_test(tc_basic, test_bad_doctype);
+ tcase_add_test(tc_basic, test_bad_doctype_utf8);
tcase_add_test(tc_basic, test_bad_doctype_utf16);
tcase_add_test(tc_basic, test_bad_doctype_plus);
tcase_add_test(tc_basic, test_bad_doctype_star);

View File

@ -0,0 +1,15 @@
https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2018-20843
https://github.com/libexpat/libexpat/commit/11f8838bf99ea0a6f0b76f9760c43704d00c4ff6
--- libexpat-R_2_2_5/expat/lib/xmlparse.c.cve20843
+++ libexpat-R_2_2_5/expat/lib/xmlparse.c
@@ -6057,7 +6057,7 @@ setElementTypePrefix(XML_Parser parser,
else
poolDiscard(&dtd->pool);
elementType->prefix = prefix;
-
+ break;
}
}
return 1;

View File

@ -0,0 +1,171 @@
https://bugzilla.redhat.com/show_bug.cgi?id=1752592
https://github.com/libexpat/libexpat/commit/6da1f19625592bfb928253620cac568d9a9b9c65
--- libexpat-R_2_2_5/expat/lib/xmlparse.c.cve15903
+++ libexpat-R_2_2_5/expat/lib/xmlparse.c
@@ -411,7 +411,7 @@ initializeEncoding(XML_Parser parser);
static enum XML_Error
doProlog(XML_Parser parser, const ENCODING *enc, const char *s,
const char *end, int tok, const char *next, const char **nextPtr,
- XML_Bool haveMore);
+ XML_Bool haveMore, XML_Bool allowClosingDoctype);
static enum XML_Error
processInternalEntity(XML_Parser parser, ENTITY *entity,
XML_Bool betweenDecl);
@@ -4218,7 +4218,7 @@ externalParEntProcessor(XML_Parser parse
parser->m_processor = prologProcessor;
return doProlog(parser, parser->m_encoding, s, end, tok, next,
- nextPtr, (XML_Bool)!parser->m_parsingStatus.finalBuffer);
+ nextPtr, (XML_Bool)!parser->m_parsingStatus.finalBuffer, XML_TRUE);
}
static enum XML_Error PTRCALL
@@ -4268,19 +4268,13 @@ prologProcessor(XML_Parser parser,
const char *next = s;
int tok = XmlPrologTok(parser->m_encoding, s, end, &next);
return doProlog(parser, parser->m_encoding, s, end, tok, next,
- nextPtr, (XML_Bool)!parser->m_parsingStatus.finalBuffer);
+ nextPtr, (XML_Bool)!parser->m_parsingStatus.finalBuffer, XML_TRUE);
}
static enum XML_Error
-doProlog(XML_Parser parser,
- const ENCODING *enc,
- const char *s,
- const char *end,
- int tok,
- const char *next,
- const char **nextPtr,
- XML_Bool haveMore)
-{
+doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
+ int tok, const char *next, const char **nextPtr, XML_Bool haveMore,
+ XML_Bool allowClosingDoctype) {
#ifdef XML_DTD
static const XML_Char externalSubsetName[] = { ASCII_HASH , '\0' };
#endif /* XML_DTD */
@@ -4458,6 +4452,11 @@ doProlog(XML_Parser parser,
}
break;
case XML_ROLE_DOCTYPE_CLOSE:
+ if (allowClosingDoctype != XML_TRUE) {
+ /* Must not close doctype from within expanded parameter entities */
+ return XML_ERROR_INVALID_TOKEN;
+ }
+
if (parser->m_doctypeName) {
parser->m_startDoctypeDeclHandler(parser->m_handlerArg, parser->m_doctypeName,
parser->m_doctypeSysid, parser->m_doctypePubid, 0);
@@ -5395,7 +5394,7 @@ processInternalEntity(XML_Parser parser,
if (entity->is_param) {
int tok = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next);
result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd, tok,
- next, &next, XML_FALSE);
+ next, &next, XML_FALSE, XML_FALSE);
}
else
#endif /* XML_DTD */
@@ -5442,7 +5441,7 @@ internalEntityProcessor(XML_Parser parse
if (entity->is_param) {
int tok = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next);
result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd, tok,
- next, &next, XML_FALSE);
+ next, &next, XML_FALSE, XML_TRUE);
}
else
#endif /* XML_DTD */
@@ -5469,7 +5468,7 @@ internalEntityProcessor(XML_Parser parse
parser->m_processor = prologProcessor;
tok = XmlPrologTok(parser->m_encoding, s, end, &next);
return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr,
- (XML_Bool)!parser->m_parsingStatus.finalBuffer);
+ (XML_Bool)!parser->m_parsingStatus.finalBuffer, XML_TRUE);
}
else
#endif /* XML_DTD */
--- libexpat-R_2_2_5/expat/tests/runtests.c.cve15903
+++ libexpat-R_2_2_5/expat/tests/runtests.c
@@ -7193,6 +7193,69 @@ overwrite_end_checker(void *userData, co
CharData_AppendXMLChars(storage, XCS("\n"), 1);
}
+#ifdef XML_DTD
+START_TEST(test_misc_deny_internal_entity_closing_doctype_issue_317) {
+ const char *const inputOne = "<!DOCTYPE d [\n"
+ "<!ENTITY % e ']><d/>'>\n"
+ "\n"
+ "%e;";
+ const char *const inputTwo = "<!DOCTYPE d [\n"
+ "<!ENTITY % e1 ']><d/>'><!ENTITY % e2 '&e1;'>\n"
+ "\n"
+ "%e2;";
+ const char *const inputThree = "<!DOCTYPE d [\n"
+ "<!ENTITY % e ']><d'>\n"
+ "\n"
+ "%e;";
+ const char *const inputIssue317 = "<!DOCTYPE doc [\n"
+ "<!ENTITY % foo ']>\n"
+ "<doc>Hell<oc (#PCDATA)*>'>\n"
+ "%foo;\n"
+ "]>\n"
+ "<doc>Hello, world</dVc>";
+
+ const char *const inputs[] = {inputOne, inputTwo, inputThree, inputIssue317};
+ size_t inputIndex = 0;
+
+ for (; inputIndex < sizeof(inputs) / sizeof(inputs[0]); inputIndex++) {
+ XML_Parser parser;
+ enum XML_Status parseResult;
+ int setParamEntityResult;
+ XML_Size lineNumber;
+ XML_Size columnNumber;
+ const char *const input = inputs[inputIndex];
+
+ parser = XML_ParserCreate(NULL);
+ setParamEntityResult
+ = XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+ if (setParamEntityResult != 1)
+ fail("Failed to set XML_PARAM_ENTITY_PARSING_ALWAYS.");
+
+ parseResult = XML_Parse(parser, input, (int)strlen(input), 0);
+ if (parseResult != XML_STATUS_ERROR) {
+ parseResult = XML_Parse(parser, "", 0, 1);
+ if (parseResult != XML_STATUS_ERROR) {
+ fail("Parsing was expected to fail but succeeded.");
+ }
+ }
+
+ if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_TOKEN)
+ fail("Error code does not match XML_ERROR_INVALID_TOKEN");
+
+ lineNumber = XML_GetCurrentLineNumber(parser);
+ if (lineNumber != 4)
+ fail("XML_GetCurrentLineNumber does not work as expected.");
+
+ columnNumber = XML_GetCurrentColumnNumber(parser);
+ if (columnNumber != 0)
+ fail("XML_GetCurrentColumnNumber does not work as expected.");
+
+ XML_ParserFree(parser);
+ }
+}
+END_TEST
+#endif
+
static void
run_ns_tagname_overwrite_test(const char *text, const XML_Char *result)
{
@@ -12210,6 +12273,10 @@ make_suite(void)
tcase_add_test(tc_misc, test_misc_features);
tcase_add_test(tc_misc, test_misc_attribute_leak);
tcase_add_test(tc_misc, test_misc_utf16le);
+#ifdef XML_DTD
+ tcase_add_test(tc_misc,
+ test_misc_deny_internal_entity_closing_doctype_issue_317);
+#endif
suite_add_tcase(s, tc_alloc);
tcase_add_checked_fixture(tc_alloc, alloc_setup, alloc_teardown);

View File

@ -0,0 +1,89 @@
commit a739613cfb5ee60919bd5ad545a5582fa8a6dad9
Author: Tomas Korbar <tkorbar@redhat.com>
Date: Mon Nov 14 12:37:16 2022 +0100
Fix CVE-2022-43680
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
index 0cc24f6..3f765f7 100644
--- a/lib/xmlparse.c
+++ b/lib/xmlparse.c
@@ -1016,6 +1016,14 @@ parserCreate(const XML_Char *encodingName,
parserInit(parser, encodingName);
if (encodingName && !parser->m_protocolEncodingName) {
+ if (dtd) {
+ // We need to stop the upcoming call to XML_ParserFree from happily
+ // destroying parser->m_dtd because the DTD is shared with the parent
+ // parser and the only guard that keeps XML_ParserFree from destroying
+ // parser->m_dtd is parser->m_isParamEntity but it will be set to
+ // XML_TRUE only later in XML_ExternalEntityParserCreate (or not at all).
+ parser->m_dtd = NULL;
+ }
XML_ParserFree(parser);
return NULL;
}
diff --git a/tests/runtests.c b/tests/runtests.c
index f3ebbd7..f58f794 100644
--- a/tests/runtests.c
+++ b/tests/runtests.c
@@ -10819,6 +10819,48 @@ START_TEST(test_alloc_long_notation)
}
END_TEST
+static int XMLCALL
+external_entity_parser_create_alloc_fail_handler(XML_Parser parser,
+ const XML_Char *context,
+ const XML_Char *UNUSED_P(base),
+ const XML_Char *UNUSED_P(systemId),
+ const XML_Char *UNUSED_P(publicId)) {
+ if (context != NULL)
+ fail("Unexpected non-NULL context");
+
+ // The following number intends to fail the upcoming allocation in line
+ // "parser->m_protocolEncodingName = copyString(encodingName,
+ // &(parser->m_mem));" in function parserInit.
+ allocation_count = 3;
+
+ const XML_Char *const encodingName = XCS("UTF-8"); // needs something non-NULL
+ const XML_Parser ext_parser
+ = XML_ExternalEntityParserCreate(parser, context, encodingName);
+ if (ext_parser != NULL)
+ fail(
+ "Call to XML_ExternalEntityParserCreate was expected to fail out-of-memory");
+
+ allocation_count = ALLOC_ALWAYS_SUCCEED;
+ return XML_STATUS_ERROR;
+}
+
+START_TEST(test_alloc_reset_after_external_entity_parser_create_fail) {
+ const char *const text = "<!DOCTYPE doc SYSTEM 'foo'><doc/>";
+
+ XML_SetExternalEntityRefHandler(
+ parser, external_entity_parser_create_alloc_fail_handler);
+ XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+
+ if (XML_Parse(parser, text, (int)strlen(text), XML_TRUE)
+ != XML_STATUS_ERROR)
+ fail("Call to parse was expected to fail");
+
+ if (XML_GetErrorCode(parser) != XML_ERROR_EXTERNAL_ENTITY_HANDLING)
+ fail("Call to parse was expected to fail from the external entity handler");
+
+ XML_ParserReset(parser, NULL);
+}
+END_TEST
static void
nsalloc_setup(void)
@@ -12653,6 +12695,10 @@ make_suite(void)
tcase_add_test(tc_alloc, test_alloc_long_entity_value);
tcase_add_test(tc_alloc, test_alloc_long_notation);
+ #ifdef XML_DTD
+ tcase_add_test(tc_alloc,
+ test_alloc_reset_after_external_entity_parser_create_fail);
+ #endif
suite_add_tcase(s, tc_nsalloc);
tcase_add_checked_fixture(tc_nsalloc, nsalloc_setup, nsalloc_teardown);
tcase_add_test(tc_nsalloc, test_nsalloc_xmlns);

View File

@ -0,0 +1,183 @@
commit 22fe2da8e2bc0625d3c492f42d6b716adb36d5c2
Author: Tomas Korbar <tkorbar@redhat.com>
Date: Mon Feb 14 12:09:42 2022 +0100
CVE-2022-23852
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
index 85ee0a8..4552680 100644
--- a/lib/xmlparse.c
+++ b/lib/xmlparse.c
@@ -161,6 +161,9 @@ typedef char ICHAR;
/* Round up n to be a multiple of sz, where sz is a power of 2. */
#define ROUND_UP(n, sz) (((n) + ((sz) - 1)) & ~((sz) - 1))
+/* Do safe (NULL-aware) pointer arithmetic */
+#define EXPAT_SAFE_PTR_DIFF(p, q) (((p) && (q)) ? ((p) - (q)) : 0)
+
/* Handle the case where memmove() doesn't exist. */
#ifndef HAVE_MEMMOVE
#ifdef HAVE_BCOPY
@@ -2026,39 +2029,54 @@ XML_GetBuffer(XML_Parser parser, int len)
default: ;
}
- if (len > parser->m_bufferLim - parser->m_bufferEnd) {
-#ifdef XML_CONTEXT_BYTES
+ if (len > EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferEnd)) {
int keep;
-#endif /* defined XML_CONTEXT_BYTES */
/* Do not invoke signed arithmetic overflow: */
- int neededSize = (int) ((unsigned)len + (unsigned)(parser->m_bufferEnd - parser->m_bufferPtr));
+ int neededSize = (int)((unsigned)len
+ + (unsigned)EXPAT_SAFE_PTR_DIFF(
+ parser->m_bufferEnd, parser->m_bufferPtr));
if (neededSize < 0) {
parser->m_errorCode = XML_ERROR_NO_MEMORY;
return NULL;
}
-#ifdef XML_CONTEXT_BYTES
- keep = (int)(parser->m_bufferPtr - parser->m_buffer);
+
+ keep = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer);
if (keep > XML_CONTEXT_BYTES)
keep = XML_CONTEXT_BYTES;
+ /* Detect and prevent integer overflow */
+ if (keep > INT_MAX - neededSize) {
+ parser->m_errorCode = XML_ERROR_NO_MEMORY;
+ return NULL;
+ }
neededSize += keep;
-#endif /* defined XML_CONTEXT_BYTES */
- if (neededSize <= parser->m_bufferLim - parser->m_buffer) {
+ if (neededSize
+ <= EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_buffer)) {
#ifdef XML_CONTEXT_BYTES
- if (keep < parser->m_bufferPtr - parser->m_buffer) {
- int offset = (int)(parser->m_bufferPtr - parser->m_buffer) - keep;
- memmove(parser->m_buffer, &parser->m_buffer[offset], parser->m_bufferEnd - parser->m_bufferPtr + keep);
+ if (keep < EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer)) {
+ int offset
+ = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer)
+ - keep;
+ /* The buffer pointers cannot be NULL here; we have at least some bytes
+ * in the buffer */
+ memmove(parser->m_buffer, &parser->m_buffer[offset],
+ parser->m_bufferEnd - parser->m_bufferPtr + keep);
parser->m_bufferEnd -= offset;
parser->m_bufferPtr -= offset;
}
#else
- memmove(parser->m_buffer, parser->m_bufferPtr, parser->m_bufferEnd - parser->m_bufferPtr);
- parser->m_bufferEnd = parser->m_buffer + (parser->m_bufferEnd - parser->m_bufferPtr);
- parser->m_bufferPtr = parser->m_buffer;
-#endif /* not defined XML_CONTEXT_BYTES */
- }
- else {
+ if (parser->m_buffer && parser->m_bufferPtr) {
+ memmove(parser->m_buffer, parser->m_bufferPtr,
+ EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr));
+ parser->m_bufferEnd
+ = parser->m_buffer
+ + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr);
+ parser->m_bufferPtr = parser->m_buffer;
+ }
+#endif /* not defined XML_CONTEXT_BYTES */
+ } else {
char *newBuf;
- int bufferSize = (int)(parser->m_bufferLim - parser->m_bufferPtr);
+ int bufferSize
+ = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferPtr);
if (bufferSize == 0)
bufferSize = INIT_BUFFER_SIZE;
do {
@@ -2077,25 +2095,33 @@ XML_GetBuffer(XML_Parser parser, int len)
parser->m_bufferLim = newBuf + bufferSize;
#ifdef XML_CONTEXT_BYTES
if (parser->m_bufferPtr) {
- int keep = (int)(parser->m_bufferPtr - parser->m_buffer);
- if (keep > XML_CONTEXT_BYTES)
- keep = XML_CONTEXT_BYTES;
- memcpy(newBuf, &parser->m_bufferPtr[-keep], parser->m_bufferEnd - parser->m_bufferPtr + keep);
+ memcpy(newBuf, &parser->m_bufferPtr[-keep],
+ EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr)
+ + keep);
FREE(parser, parser->m_buffer);
parser->m_buffer = newBuf;
- parser->m_bufferEnd = parser->m_buffer + (parser->m_bufferEnd - parser->m_bufferPtr) + keep;
+ parser->m_bufferEnd
+ = parser->m_buffer
+ + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr)
+ + keep;
parser->m_bufferPtr = parser->m_buffer + keep;
- }
- else {
- parser->m_bufferEnd = newBuf + (parser->m_bufferEnd - parser->m_bufferPtr);
+ } else {
+ /* This must be a brand new buffer with no data in it yet */
+ parser->m_bufferEnd = newBuf;
parser->m_bufferPtr = parser->m_buffer = newBuf;
}
#else
if (parser->m_bufferPtr) {
- memcpy(newBuf, parser->m_bufferPtr, parser->m_bufferEnd - parser->m_bufferPtr);
+ memcpy(newBuf, parser->m_bufferPtr,
+ EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr));
FREE(parser, parser->m_buffer);
+ parser->m_bufferEnd
+ = newBuf
+ + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr);
+ } else {
+ /* This must be a brand new buffer with no data in it yet */
+ parser->m_bufferEnd = newBuf;
}
- parser->m_bufferEnd = newBuf + (parser->m_bufferEnd - parser->m_bufferPtr);
parser->m_bufferPtr = parser->m_buffer = newBuf;
#endif /* not defined XML_CONTEXT_BYTES */
}
diff --git a/tests/runtests.c b/tests/runtests.c
index e1f1ad1..ecc6f47 100644
--- a/tests/runtests.c
+++ b/tests/runtests.c
@@ -4116,6 +4116,31 @@ START_TEST(test_get_buffer_2)
}
END_TEST
+/* Test for signed integer overflow CVE-2022-23852 */
+#if defined(XML_CONTEXT_BYTES)
+START_TEST(test_get_buffer_3_overflow) {
+ XML_Parser parser = XML_ParserCreate(NULL);
+ assert(parser != NULL);
+
+ const char *const text = "\n";
+ const int expectedKeepValue = (int)strlen(text);
+
+ // After this call, variable "keep" in XML_GetBuffer will
+ // have value expectedKeepValue
+ if (XML_Parse(parser, text, (int)strlen(text), XML_FALSE /* isFinal */)
+ == XML_STATUS_ERROR)
+ xml_failure(parser);
+
+ assert(expectedKeepValue > 0);
+ if (XML_GetBuffer(parser, INT_MAX - expectedKeepValue + 1) != NULL)
+ fail("enlarging buffer not failed");
+
+ XML_ParserFree(parser);
+}
+END_TEST
+#endif // defined(XML_CONTEXT_BYTES)
+
+
/* Test position information macros */
START_TEST(test_byte_info_at_end)
{
@@ -12117,6 +12142,9 @@ make_suite(void)
tcase_add_test(tc_basic, test_empty_parse);
tcase_add_test(tc_basic, test_get_buffer_1);
tcase_add_test(tc_basic, test_get_buffer_2);
+#if defined(XML_CONTEXT_BYTES)
+ tcase_add_test(tc_basic, test_get_buffer_3_overflow);
+#endif
tcase_add_test(tc_basic, test_byte_info_at_end);
tcase_add_test(tc_basic, test_byte_info_at_error);
tcase_add_test(tc_basic, test_byte_info_at_cdata);

View File

@ -0,0 +1,54 @@
commit dbac77ddbccb23d507758c591fad622e2b6e6324
Author: Tomas Korbar <tkorbar@redhat.com>
Date: Mon Feb 14 12:20:25 2022 +0100
CVE-2021-45960
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
index 2821c6f..c45be0c 100644
--- a/lib/xmlparse.c
+++ b/lib/xmlparse.c
@@ -3341,7 +3341,12 @@ storeAtts(XML_Parser parser, const ENCODING *enc,
if (nPrefixes) {
int j; /* hash table index */
unsigned long version = parser->m_nsAttsVersion;
- int nsAttsSize = (int)1 << parser->m_nsAttsPower;
+ /* Detect and prevent invalid shift */
+ if (parser->m_nsAttsPower >= sizeof(unsigned int) * 8 /* bits per byte */) {
+ return XML_ERROR_NO_MEMORY;
+ }
+
+ unsigned int nsAttsSize = 1u << parser->m_nsAttsPower;
unsigned char oldNsAttsPower = parser->m_nsAttsPower;
/* size of hash table must be at least 2 * (# of prefixed attributes) */
if ((nPrefixes << 1) >> parser->m_nsAttsPower) { /* true for m_nsAttsPower = 0 */
@@ -3350,7 +3355,28 @@ storeAtts(XML_Parser parser, const ENCODING *enc,
while (nPrefixes >> parser->m_nsAttsPower++);
if (parser->m_nsAttsPower < 3)
parser->m_nsAttsPower = 3;
- nsAttsSize = (int)1 << parser->m_nsAttsPower;
+
+ /* Detect and prevent invalid shift */
+ if (parser->m_nsAttsPower >= sizeof(nsAttsSize) * 8 /* bits per byte */) {
+ /* Restore actual size of memory in m_nsAtts */
+ parser->m_nsAttsPower = oldNsAttsPower;
+ return XML_ERROR_NO_MEMORY;
+ }
+
+ nsAttsSize = 1u << parser->m_nsAttsPower;
+
+ /* Detect and prevent integer overflow.
+ * The preprocessor guard addresses the "always false" warning
+ * from -Wtype-limits on platforms where
+ * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
+#if UINT_MAX >= SIZE_MAX
+ if (nsAttsSize > (size_t)(-1) / sizeof(NS_ATT)) {
+ /* Restore actual size of memory in m_nsAtts */
+ parser->m_nsAttsPower = oldNsAttsPower;
+ return XML_ERROR_NO_MEMORY;
+ }
+#endif
+
temp = (NS_ATT *)REALLOC(parser, parser->m_nsAtts, nsAttsSize * sizeof(NS_ATT));
if (!temp) {
/* Restore actual size of memory in m_nsAtts */

View File

@ -0,0 +1,118 @@
commit bfecc1f11ab5f0cc2aa3dc5cb87d3236a87ce61d
Author: Tomas Korbar <tkorbar@redhat.com>
Date: Fri Sep 30 10:52:04 2022 +0200
Fix CVE-2022-40674
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
index d47e42c..0cc24f6 100644
--- a/lib/xmlparse.c
+++ b/lib/xmlparse.c
@@ -5765,8 +5765,14 @@ internalEntityProcessor(XML_Parser parser,
{
parser->m_processor = contentProcessor;
/* see externalEntityContentProcessor vs contentProcessor */
- return doContent(parser, parser->m_parentParser ? 1 : 0, parser->m_encoding, s, end,
- nextPtr, (XML_Bool)!parser->m_parsingStatus.finalBuffer);
+ result = doContent(parser, parser->m_parentParser ? 1 : 0, parser->m_encoding,
+ s, end, nextPtr,
+ (XML_Bool)! parser->m_parsingStatus.finalBuffer);
+ if (result == XML_ERROR_NONE) {
+ if (! storeRawNames(parser))
+ return XML_ERROR_NO_MEMORY;
+ }
+ return result;
}
}
diff --git a/tests/runtests.c b/tests/runtests.c
index 569ad8c..f3ebbd7 100644
--- a/tests/runtests.c
+++ b/tests/runtests.c
@@ -5401,6 +5401,78 @@ START_TEST(test_resume_entity_with_syntax_error)
}
END_TEST
+void
+suspending_comment_handler(void *userData, const XML_Char *UNUSED_P(data)) {
+ XML_Parser parser = (XML_Parser)userData;
+ XML_StopParser(parser, XML_TRUE);
+}
+
+START_TEST(test_suspend_resume_internal_entity_issue_629) {
+ const char *const text
+ = "<!DOCTYPE a [<!ENTITY e '<!--COMMENT-->a'>]><a>&e;<b>\n"
+ "<"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "/>"
+ "</b></a>";
+ const size_t firstChunkSizeBytes = 54;
+
+ XML_Parser parser = XML_ParserCreate(NULL);
+ XML_SetUserData(parser, parser);
+ XML_SetCommentHandler(parser, suspending_comment_handler);
+
+ if (XML_Parse(parser, text, (int)firstChunkSizeBytes, XML_FALSE)
+ != XML_STATUS_SUSPENDED)
+ xml_failure(parser);
+ if (XML_ResumeParser(parser) != XML_STATUS_OK)
+ xml_failure(parser);
+ if (XML_Parse(parser, text + firstChunkSizeBytes,
+ (int)(strlen(text) - firstChunkSizeBytes), XML_TRUE)
+ != XML_STATUS_OK)
+ xml_failure(parser);
+ XML_ParserFree(parser);
+}
+END_TEST
+
+
/* Test suspending and resuming in a parameter entity substitution */
static void XMLCALL
element_decl_suspender(void *UNUSED_P(userData),
@@ -12395,6 +12467,7 @@ make_suite(void)
tcase_add_test(tc_basic, test_hash_collision);
tcase_add_test(tc_basic, test_suspend_resume_internal_entity);
tcase_add_test(tc_basic, test_resume_entity_with_syntax_error);
+ tcase_add_test(tc_basic, test_suspend_resume_internal_entity_issue_629);
tcase_add_test(tc_basic, test_suspend_resume_parameter_entity);
tcase_add_test(tc_basic, test_restart_on_error);
tcase_add_test(tc_basic, test_reject_lt_in_attribute_value);

View File

@ -0,0 +1,19 @@
commit e5b609876e5a266725fba1c377b0ac95c737e6ed
Author: Tomas Korbar <tkorbar@redhat.com>
Date: Mon May 2 12:44:06 2022 +0200
Fix CVE-2022-25314
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
index 1f1413f..ceeec26 100644
--- a/lib/xmlparse.c
+++ b/lib/xmlparse.c
@@ -7525,7 +7525,7 @@ static XML_Char *
copyString(const XML_Char *s,
const XML_Memory_Handling_Suite *memsuite)
{
- int charsRequired = 0;
+ size_t charsRequired = 0;
XML_Char *result;
/* First determine how long the string is */

View File

@ -0,0 +1,31 @@
commit 3a4141add108097fa548b196f5950c6663e1578e
Author: Tomas Korbar <tkorbar@redhat.com>
Date: Thu Mar 3 13:50:20 2022 +0100
CVE-2022-25315
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
index f0061c8..45fda00 100644
--- a/lib/xmlparse.c
+++ b/lib/xmlparse.c
@@ -2508,6 +2508,7 @@ storeRawNames(XML_Parser parser)
while (tag) {
int bufSize;
int nameLen = sizeof(XML_Char) * (tag->name.strLen + 1);
+ size_t rawNameLen;
char *rawNameBuf = tag->buf + nameLen;
/* Stop if already stored. Since m_tagStack is a stack, we can stop
at the first entry that has already been copied; everything
@@ -2519,7 +2520,11 @@ storeRawNames(XML_Parser parser)
/* For re-use purposes we need to ensure that the
size of tag->buf is a multiple of sizeof(XML_Char).
*/
- bufSize = nameLen + ROUND_UP(tag->rawNameLength, sizeof(XML_Char));
+ rawNameLen = ROUND_UP(tag->rawNameLength, sizeof(XML_Char));
+ /* Detect and prevent integer overflow. */
+ if (rawNameLen > (size_t)INT_MAX - nameLen)
+ return XML_FALSE;
+ bufSize = nameLen + (int)rawNameLen;
if (bufSize > tag->bufEnd - tag->buf) {
char *temp = (char *)REALLOC(parser, tag->buf, bufSize);
if (temp == NULL)

View File

@ -0,0 +1,38 @@
commit 835df27bc1a1eae1ec51b14122ea40c974dd7409
Author: Tomas Korbar <tkorbar@redhat.com>
Date: Mon Feb 14 12:29:20 2022 +0100
CVE-2021-46143
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
index c45be0c..22d0a75 100644
--- a/lib/xmlparse.c
+++ b/lib/xmlparse.c
@@ -4995,6 +4995,11 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
case XML_ROLE_GROUP_OPEN:
if (parser->m_prologState.level >= parser->m_groupSize) {
if (parser->m_groupSize) {
+ /* Detect and prevent integer overflow */
+ if (parser->m_groupSize > (unsigned int)(-1) / 2u) {
+ return XML_ERROR_NO_MEMORY;
+ }
+
char *temp = (char *)REALLOC(parser, parser->m_groupConnector, parser->m_groupSize *= 2);
if (temp == NULL) {
parser->m_groupSize /= 2;
@@ -5002,6 +5007,15 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
}
parser->m_groupConnector = temp;
if (dtd->scaffIndex) {
+ /* Detect and prevent integer overflow.
+ * The preprocessor guard addresses the "always false" warning
+ * from -Wtype-limits on platforms where
+ * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
+#if UINT_MAX >= SIZE_MAX
+ if (parser->m_groupSize > (size_t)(-1) / sizeof(int)) {
+ return XML_ERROR_NO_MEMORY;
+ }
+#endif
int *temp = (int *)REALLOC(parser, dtd->scaffIndex,
parser->m_groupSize * sizeof(int));
if (temp == NULL)

View File

@ -0,0 +1,238 @@
commit 0f920007dc157e052fed2fc66a83c6c23ccec0aa
Author: Tomas Korbar <tkorbar@redhat.com>
Date: Mon Feb 14 12:41:56 2022 +0100
CVE-2022-22822 to CVE-2022-22827
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
index 22d0a75..6a880af 100644
--- a/lib/xmlparse.c
+++ b/lib/xmlparse.c
@@ -3187,13 +3187,38 @@ storeAtts(XML_Parser parser, const ENCODING *enc,
/* get the attributes from the tokenizer */
n = XmlGetAttributes(enc, attStr, parser->m_attsSize, parser->m_atts);
+
+ /* Detect and prevent integer overflow */
+ if (n > INT_MAX - nDefaultAtts) {
+ return XML_ERROR_NO_MEMORY;
+ }
+
if (n + nDefaultAtts > parser->m_attsSize) {
int oldAttsSize = parser->m_attsSize;
ATTRIBUTE *temp;
#ifdef XML_ATTR_INFO
XML_AttrInfo *temp2;
#endif
+
+ /* Detect and prevent integer overflow */
+ if ((nDefaultAtts > INT_MAX - INIT_ATTS_SIZE)
+ || (n > INT_MAX - (nDefaultAtts + INIT_ATTS_SIZE))) {
+ return XML_ERROR_NO_MEMORY;
+ }
+
parser->m_attsSize = n + nDefaultAtts + INIT_ATTS_SIZE;
+
+ /* Detect and prevent integer overflow.
+ * The preprocessor guard addresses the "always false" warning
+ * from -Wtype-limits on platforms where
+ * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
+#if UINT_MAX >= SIZE_MAX
+ if ((unsigned)parser->m_attsSize > (size_t)(-1) / sizeof(ATTRIBUTE)) {
+ parser->m_attsSize = oldAttsSize;
+ return XML_ERROR_NO_MEMORY;
+ }
+#endif
+
temp = (ATTRIBUTE *)REALLOC(parser, (void *)parser->m_atts, parser->m_attsSize * sizeof(ATTRIBUTE));
if (temp == NULL) {
parser->m_attsSize = oldAttsSize;
@@ -3201,6 +3226,17 @@ storeAtts(XML_Parser parser, const ENCODING *enc,
}
parser->m_atts = temp;
#ifdef XML_ATTR_INFO
+ /* Detect and prevent integer overflow.
+ * The preprocessor guard addresses the "always false" warning
+ * from -Wtype-limits on platforms where
+ * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
+# if UINT_MAX >= SIZE_MAX
+ if ((unsigned)parser->m_attsSize > (size_t)(-1) / sizeof(XML_AttrInfo)) {
+ parser->m_attsSize = oldAttsSize;
+ return XML_ERROR_NO_MEMORY;
+ }
+# endif
+
temp2 = (XML_AttrInfo *)REALLOC(parser, (void *)parser->m_attInfo, parser->m_attsSize * sizeof(XML_AttrInfo));
if (temp2 == NULL) {
parser->m_attsSize = oldAttsSize;
@@ -3535,9 +3571,30 @@ storeAtts(XML_Parser parser, const ENCODING *enc,
tagNamePtr->prefixLen = prefixLen;
for (i = 0; localPart[i++];)
; /* i includes null terminator */
+
+ /* Detect and prevent integer overflow */
+ if (binding->uriLen > INT_MAX - prefixLen
+ || i > INT_MAX - (binding->uriLen + prefixLen)) {
+ return XML_ERROR_NO_MEMORY;
+ }
+
n = i + binding->uriLen + prefixLen;
if (n > binding->uriAlloc) {
TAG *p;
+ /* Detect and prevent integer overflow */
+ if (n > INT_MAX - EXPAND_SPARE) {
+ return XML_ERROR_NO_MEMORY;
+ }
+ /* Detect and prevent integer overflow.
+ * The preprocessor guard addresses the "always false" warning
+ * from -Wtype-limits on platforms where
+ * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
+#if UINT_MAX >= SIZE_MAX
+ if ((unsigned)(n + EXPAND_SPARE) > (size_t)(-1) / sizeof(XML_Char)) {
+ return XML_ERROR_NO_MEMORY;
+ }
+#endif
+
uri = (XML_Char *)MALLOC(parser, (n + EXPAND_SPARE) * sizeof(XML_Char));
if (!uri)
return XML_ERROR_NO_MEMORY;
@@ -3638,6 +3695,21 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId,
if (parser->m_freeBindingList) {
b = parser->m_freeBindingList;
if (len > b->uriAlloc) {
+ /* Detect and prevent integer overflow */
+ if (len > INT_MAX - EXPAND_SPARE) {
+ return XML_ERROR_NO_MEMORY;
+ }
+
+ /* Detect and prevent integer overflow.
+ * The preprocessor guard addresses the "always false" warning
+ * from -Wtype-limits on platforms where
+ * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
+#if UINT_MAX >= SIZE_MAX
+ if ((unsigned)(len + EXPAND_SPARE) > (size_t)(-1) / sizeof(XML_Char)) {
+ return XML_ERROR_NO_MEMORY;
+ }
+#endif
+
XML_Char *temp = (XML_Char *)REALLOC(parser, b->uri,
sizeof(XML_Char) * (len + EXPAND_SPARE));
if (temp == NULL)
@@ -3651,6 +3723,21 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId,
b = (BINDING *)MALLOC(parser, sizeof(BINDING));
if (!b)
return XML_ERROR_NO_MEMORY;
+
+ /* Detect and prevent integer overflow */
+ if (len > INT_MAX - EXPAND_SPARE) {
+ return XML_ERROR_NO_MEMORY;
+ }
+ /* Detect and prevent integer overflow.
+ * The preprocessor guard addresses the "always false" warning
+ * from -Wtype-limits on platforms where
+ * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
+#if UINT_MAX >= SIZE_MAX
+ if ((unsigned)(len + EXPAND_SPARE) > (size_t)(-1) / sizeof(XML_Char)) {
+ return XML_ERROR_NO_MEMORY;
+ }
+#endif
+
b->uri = (XML_Char *)MALLOC(parser, sizeof(XML_Char) * (len + EXPAND_SPARE));
if (!b->uri) {
FREE(parser, b);
@@ -6058,7 +6145,24 @@ defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, XML_Bool isCdata,
}
else {
DEFAULT_ATTRIBUTE *temp;
+
+ /* Detect and prevent integer overflow */
+ if (type->allocDefaultAtts > INT_MAX / 2) {
+ return 0;
+ }
+
int count = type->allocDefaultAtts * 2;
+
+ /* Detect and prevent integer overflow.
+ * The preprocessor guard addresses the "always false" warning
+ * from -Wtype-limits on platforms where
+ * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
+#if UINT_MAX >= SIZE_MAX
+ if ((unsigned)count > (size_t)(-1) / sizeof(DEFAULT_ATTRIBUTE)) {
+ return 0;
+ }
+#endif
+
temp = (DEFAULT_ATTRIBUTE *)
REALLOC(parser, type->defaultAtts, (count * sizeof(DEFAULT_ATTRIBUTE)));
if (temp == NULL)
@@ -6733,8 +6837,20 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize)
/* check for overflow (table is half full) */
if (table->used >> (table->power - 1)) {
unsigned char newPower = table->power + 1;
+
+ /* Detect and prevent invalid shift */
+ if (newPower >= sizeof(unsigned long) * 8 /* bits per byte */) {
+ return NULL;
+ }
+
size_t newSize = (size_t)1 << newPower;
unsigned long newMask = (unsigned long)newSize - 1;
+
+ /* Detect and prevent integer overflow */
+ if (newSize > (size_t)(-1) / sizeof(NAMED *)) {
+ return NULL;
+ }
+
size_t tsize = newSize * sizeof(NAMED *);
NAMED **newV = (NAMED **)table->mem->malloc_fcn(tsize);
if (!newV)
@@ -7100,6 +7216,20 @@ nextScaffoldPart(XML_Parser parser)
if (dtd->scaffCount >= dtd->scaffSize) {
CONTENT_SCAFFOLD *temp;
if (dtd->scaffold) {
+ /* Detect and prevent integer overflow */
+ if (dtd->scaffSize > UINT_MAX / 2u) {
+ return -1;
+ }
+ /* Detect and prevent integer overflow.
+ * The preprocessor guard addresses the "always false" warning
+ * from -Wtype-limits on platforms where
+ * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
+#if UINT_MAX >= SIZE_MAX
+ if (dtd->scaffSize > (size_t)(-1) / 2u / sizeof(CONTENT_SCAFFOLD)) {
+ return -1;
+ }
+#endif
+
temp = (CONTENT_SCAFFOLD *)
REALLOC(parser, dtd->scaffold, dtd->scaffSize * 2 * sizeof(CONTENT_SCAFFOLD));
if (temp == NULL)
@@ -7176,8 +7306,26 @@ build_model (XML_Parser parser)
XML_Content *ret;
XML_Content *cpos;
XML_Char * str;
- int allocsize = (dtd->scaffCount * sizeof(XML_Content)
- + (dtd->contentStringLen * sizeof(XML_Char)));
+
+ /* Detect and prevent integer overflow.
+ * The preprocessor guard addresses the "always false" warning
+ * from -Wtype-limits on platforms where
+ * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
+#if UINT_MAX >= SIZE_MAX
+ if (dtd->scaffCount > (size_t)(-1) / sizeof(XML_Content)) {
+ return NULL;
+ }
+ if (dtd->contentStringLen > (size_t)(-1) / sizeof(XML_Char)) {
+ return NULL;
+ }
+#endif
+ if (dtd->scaffCount * sizeof(XML_Content)
+ > (size_t)(-1) - dtd->contentStringLen * sizeof(XML_Char)) {
+ return NULL;
+ }
+
+ const size_t allocsize = (dtd->scaffCount * sizeof(XML_Content)
+ + (dtd->contentStringLen * sizeof(XML_Char)));
ret = (XML_Content *)MALLOC(parser, allocsize);
if (!ret)

View File

@ -0,0 +1,228 @@
commit f1b61e6fbaedbb2bbea736269a015d97d4df46ce
Author: Tomas Korbar <tkorbar@redhat.com>
Date: Tue May 3 13:42:54 2022 +0200
Fix CVE-2022-25313
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
index ceeec26..d47e42c 100644
--- a/lib/xmlparse.c
+++ b/lib/xmlparse.c
@@ -7458,12 +7458,14 @@ build_node(XML_Parser parser,
}
static XML_Content *
-build_model (XML_Parser parser)
-{
- DTD * const dtd = parser->m_dtd; /* save one level of indirection */
+build_model(XML_Parser parser) {
+ /* Function build_model transforms the existing parser->m_dtd->scaffold
+ * array of CONTENT_SCAFFOLD tree nodes into a new array of
+ * XML_Content tree nodes followed by a gapless list of zero-terminated
+ * strings. */
+ DTD *const dtd = parser->m_dtd; /* save one level of indirection */
XML_Content *ret;
- XML_Content *cpos;
- XML_Char * str;
+ XML_Char *str; /* the current string writing location */
/* Detect and prevent integer overflow.
* The preprocessor guard addresses the "always false" warning
@@ -7486,13 +7488,99 @@ build_model (XML_Parser parser)
+ (dtd->contentStringLen * sizeof(XML_Char)));
ret = (XML_Content *)MALLOC(parser, allocsize);
- if (!ret)
+ if (! ret)
return NULL;
- str = (XML_Char *) (&ret[dtd->scaffCount]);
- cpos = &ret[1];
+ /* What follows is an iterative implementation (of what was previously done
+ * recursively in a dedicated function called "build_node". The old recursive
+ * build_node could be forced into stack exhaustion from input as small as a
+ * few megabyte, and so that was a security issue. Hence, a function call
+ * stack is avoided now by resolving recursion.)
+ *
+ * The iterative approach works as follows:
+ *
+ * - We have two writing pointers, both walking up the result array; one does
+ * the work, the other creates "jobs" for its colleague to do, and leads
+ * the way:
+ *
+ * - The faster one, pointer jobDest, always leads and writes "what job
+ * to do" by the other, once they reach that place in the
+ * array: leader "jobDest" stores the source node array index (relative
+ * to array dtd->scaffold) in field "numchildren".
+ *
+ * - The slower one, pointer dest, looks at the value stored in the
+ * "numchildren" field (which actually holds a source node array index
+ * at that time) and puts the real data from dtd->scaffold in.
+ *
+ * - Before the loop starts, jobDest writes source array index 0
+ * (where the root node is located) so that dest will have something to do
+ * when it starts operation.
+ *
+ * - Whenever nodes with children are encountered, jobDest appends
+ * them as new jobs, in order. As a result, tree node siblings are
+ * adjacent in the resulting array, for example:
+ *
+ * [0] root, has two children
+ * [1] first child of 0, has three children
+ * [3] first child of 1, does not have children
+ * [4] second child of 1, does not have children
+ * [5] third child of 1, does not have children
+ * [2] second child of 0, does not have children
+ *
+ * Or (the same data) presented in flat array view:
+ *
+ * [0] root, has two children
+ *
+ * [1] first child of 0, has three children
+ * [2] second child of 0, does not have children
+ *
+ * [3] first child of 1, does not have children
+ * [4] second child of 1, does not have children
+ * [5] third child of 1, does not have children
+ *
+ * - The algorithm repeats until all target array indices have been processed.
+ */
+ XML_Content *dest = ret; /* tree node writing location, moves upwards */
+ XML_Content *const destLimit = &ret[dtd->scaffCount];
+ XML_Content *jobDest = ret; /* next free writing location in target array */
+ str = (XML_Char *)&ret[dtd->scaffCount];
+
+ /* Add the starting job, the root node (index 0) of the source tree */
+ (jobDest++)->numchildren = 0;
+
+ for (; dest < destLimit; dest++) {
+ /* Retrieve source tree array index from job storage */
+ const int src_node = (int)dest->numchildren;
+
+ /* Convert item */
+ dest->type = dtd->scaffold[src_node].type;
+ dest->quant = dtd->scaffold[src_node].quant;
+ if (dest->type == XML_CTYPE_NAME) {
+ const XML_Char *src;
+ dest->name = str;
+ src = dtd->scaffold[src_node].name;
+ for (;;) {
+ *str++ = *src;
+ if (! *src)
+ break;
+ src++;
+ }
+ dest->numchildren = 0;
+ dest->children = NULL;
+ } else {
+ unsigned int i;
+ int cn;
+ dest->name = NULL;
+ dest->numchildren = dtd->scaffold[src_node].childcnt;
+ dest->children = jobDest;
+
+ /* Append scaffold indices of children to array */
+ for (i = 0, cn = dtd->scaffold[src_node].firstchild;
+ i < dest->numchildren; i++, cn = dtd->scaffold[cn].nextsib)
+ (jobDest++)->numchildren = (unsigned int)cn;
+ }
+ }
- build_node(parser, 0, ret, &cpos, &str);
return ret;
}
diff --git a/tests/runtests.c b/tests/runtests.c
index eacd163..569ad8c 100644
--- a/tests/runtests.c
+++ b/tests/runtests.c
@@ -2848,6 +2848,81 @@ START_TEST(test_dtd_elements)
}
END_TEST
+static void XMLCALL
+element_decl_check_model(void *UNUSED_P(userData), const XML_Char *name,
+ XML_Content *model) {
+ uint32_t errorFlags = 0;
+
+ /* Expected model array structure is this:
+ * [0] (type 6, quant 0)
+ * [1] (type 5, quant 0)
+ * [3] (type 4, quant 0, name "bar")
+ * [4] (type 4, quant 0, name "foo")
+ * [5] (type 4, quant 3, name "xyz")
+ * [2] (type 4, quant 2, name "zebra")
+ */
+ errorFlags |= ((xcstrcmp(name, XCS("junk")) == 0) ? 0 : (1u << 0));
+ errorFlags |= ((model != NULL) ? 0 : (1u << 1));
+
+ errorFlags |= ((model[0].type == XML_CTYPE_SEQ) ? 0 : (1u << 2));
+ errorFlags |= ((model[0].quant == XML_CQUANT_NONE) ? 0 : (1u << 3));
+ errorFlags |= ((model[0].numchildren == 2) ? 0 : (1u << 4));
+ errorFlags |= ((model[0].children == &model[1]) ? 0 : (1u << 5));
+ errorFlags |= ((model[0].name == NULL) ? 0 : (1u << 6));
+
+ errorFlags |= ((model[1].type == XML_CTYPE_CHOICE) ? 0 : (1u << 7));
+ errorFlags |= ((model[1].quant == XML_CQUANT_NONE) ? 0 : (1u << 8));
+ errorFlags |= ((model[1].numchildren == 3) ? 0 : (1u << 9));
+ errorFlags |= ((model[1].children == &model[3]) ? 0 : (1u << 10));
+ errorFlags |= ((model[1].name == NULL) ? 0 : (1u << 11));
+
+ errorFlags |= ((model[2].type == XML_CTYPE_NAME) ? 0 : (1u << 12));
+ errorFlags |= ((model[2].quant == XML_CQUANT_REP) ? 0 : (1u << 13));
+ errorFlags |= ((model[2].numchildren == 0) ? 0 : (1u << 14));
+ errorFlags |= ((model[2].children == NULL) ? 0 : (1u << 15));
+ errorFlags |= ((xcstrcmp(model[2].name, XCS("zebra")) == 0) ? 0 : (1u << 16));
+
+ errorFlags |= ((model[3].type == XML_CTYPE_NAME) ? 0 : (1u << 17));
+ errorFlags |= ((model[3].quant == XML_CQUANT_NONE) ? 0 : (1u << 18));
+ errorFlags |= ((model[3].numchildren == 0) ? 0 : (1u << 19));
+ errorFlags |= ((model[3].children == NULL) ? 0 : (1u << 20));
+ errorFlags |= ((xcstrcmp(model[3].name, XCS("bar")) == 0) ? 0 : (1u << 21));
+
+ errorFlags |= ((model[4].type == XML_CTYPE_NAME) ? 0 : (1u << 22));
+ errorFlags |= ((model[4].quant == XML_CQUANT_NONE) ? 0 : (1u << 23));
+ errorFlags |= ((model[4].numchildren == 0) ? 0 : (1u << 24));
+ errorFlags |= ((model[4].children == NULL) ? 0 : (1u << 25));
+ errorFlags |= ((xcstrcmp(model[4].name, XCS("foo")) == 0) ? 0 : (1u << 26));
+
+ errorFlags |= ((model[5].type == XML_CTYPE_NAME) ? 0 : (1u << 27));
+ errorFlags |= ((model[5].quant == XML_CQUANT_PLUS) ? 0 : (1u << 28));
+ errorFlags |= ((model[5].numchildren == 0) ? 0 : (1u << 29));
+ errorFlags |= ((model[5].children == NULL) ? 0 : (1u << 30));
+ errorFlags |= ((xcstrcmp(model[5].name, XCS("xyz")) == 0) ? 0 : (1u << 31));
+
+ XML_SetUserData(parser, (void *)(uintptr_t)errorFlags);
+ XML_FreeContentModel(parser, model);
+}
+
+START_TEST(test_dtd_elements_nesting) {
+ // Payload inspired by a test in Perl's XML::Parser
+ const char *text = "<!DOCTYPE foo [\n"
+ "<!ELEMENT junk ((bar|foo|xyz+), zebra*)>\n"
+ "]>\n"
+ "<foo/>";
+
+ XML_SetUserData(parser, (void *)(uintptr_t)-1);
+
+ XML_SetElementDeclHandler(parser, element_decl_check_model);
+ if (XML_Parse(parser, text, (int)strlen(text), XML_TRUE)
+ == XML_STATUS_ERROR)
+ xml_failure(parser);
+
+ if ((uint32_t)(uintptr_t)XML_GetUserData(parser) != 0)
+ fail("Element declaration model regression detected");
+}
+END_TEST
+
/* Test foreign DTD handling */
START_TEST(test_set_foreign_dtd)
{
@@ -12256,6 +12331,7 @@ make_suite(void)
tcase_add_test(tc_basic, test_memory_allocation);
tcase_add_test(tc_basic, test_default_current);
tcase_add_test(tc_basic, test_dtd_elements);
+ tcase_add_test(tc_basic, test_dtd_elements_nesting);
tcase_add_test(tc_basic, test_set_foreign_dtd);
tcase_add_test(tc_basic, test_foreign_dtd_not_standalone);
tcase_add_test(tc_basic, test_invalid_foreign_dtd);

View File

@ -0,0 +1,229 @@
commit fd5473ef5873048eadef344a1f16f71ad8eefe99
Author: Tomas Korbar <tkorbar@redhat.com>
Date: Mon Mar 14 12:17:41 2022 +0100
Protect against malicious namespace declarations
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
index 581b9a4..6f3510b 100644
--- a/lib/xmlparse.c
+++ b/lib/xmlparse.c
@@ -661,8 +661,7 @@ XML_ParserCreate(const XML_Char *encodingName)
XML_Parser XMLCALL
XML_ParserCreateNS(const XML_Char *encodingName, XML_Char nsSep)
{
- XML_Char tmp[2];
- *tmp = nsSep;
+ XML_Char tmp[2] = {nsSep, 0};
return XML_ParserCreate_MM(encodingName, NULL, tmp);
}
@@ -1288,8 +1287,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser,
would be otherwise.
*/
if (parser->m_ns) {
- XML_Char tmp[2];
- *tmp = parser->m_namespaceSeparator;
+ XML_Char tmp[2] = {parser->m_namespaceSeparator, 0};
parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd);
}
else {
@@ -3640,6 +3638,117 @@ storeAtts(XML_Parser parser, const ENCODING *enc,
return XML_ERROR_NONE;
}
+static XML_Bool
+is_rfc3986_uri_char(XML_Char candidate) {
+ // For the RFC 3986 ANBF grammar see
+ // https://datatracker.ietf.org/doc/html/rfc3986#appendix-A
+
+ switch (candidate) {
+ // From rule "ALPHA" (uppercase half)
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z':
+
+ // From rule "ALPHA" (lowercase half)
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z':
+
+ // From rule "DIGIT"
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+
+ // From rule "pct-encoded"
+ case '%':
+
+ // From rule "unreserved"
+ case '-':
+ case '.':
+ case '_':
+ case '~':
+
+ // From rule "gen-delims"
+ case ':':
+ case '/':
+ case '?':
+ case '#':
+ case '[':
+ case ']':
+ case '@':
+
+ // From rule "sub-delims"
+ case '!':
+ case '$':
+ case '&':
+ case '\'':
+ case '(':
+ case ')':
+ case '*':
+ case '+':
+ case ',':
+ case ';':
+ case '=':
+ return XML_TRUE;
+
+ default:
+ return XML_FALSE;
+ }
+}
+
/* addBinding() overwrites the value of prefix->binding without checking.
Therefore one must keep track of the old value outside of addBinding().
*/
@@ -3700,6 +3809,29 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId,
if (!mustBeXML && isXMLNS
&& (len > xmlnsLen || uri[len] != xmlnsNamespace[len]))
isXMLNS = XML_FALSE;
+
+ // NOTE: While Expat does not validate namespace URIs against RFC 3986
+ // today (and is not REQUIRED to do so with regard to the XML 1.0
+ // namespaces specification) we have to at least make sure, that
+ // the application on top of Expat (that is likely splitting expanded
+ // element names ("qualified names") of form
+ // "[uri sep] local [sep prefix] '\0'" back into 1, 2 or 3 pieces
+ // in its element handler code) cannot be confused by an attacker
+ // putting additional namespace separator characters into namespace
+ // declarations. That would be ambiguous and not to be expected.
+ //
+ // While the HTML API docs of function XML_ParserCreateNS have been
+ // advising against use of a namespace separator character that can
+ // appear in a URI for >20 years now, some widespread applications
+ // are using URI characters (':' (colon) in particular) for a
+ // namespace separator, in practice. To keep these applications
+ // functional, we only reject namespaces URIs containing the
+ // application-chosen namespace separator if the chosen separator
+ // is a non-URI character with regard to RFC 3986.
+ if (parser->m_ns && (uri[len] == parser->m_namespaceSeparator)
+ && ! is_rfc3986_uri_char(uri[len])) {
+ return XML_ERROR_SYNTAX;
+ }
}
isXML = isXML && len == xmlLen;
isXMLNS = isXMLNS && len == xmlnsLen;
diff --git a/tests/runtests.c b/tests/runtests.c
index ecc6f47..eabd55d 100644
--- a/tests/runtests.c
+++ b/tests/runtests.c
@@ -7950,6 +7950,38 @@ START_TEST(test_ns_double_colon_doctype)
}
END_TEST
+START_TEST(test_ns_separator_in_uri) {
+ struct test_case {
+ enum XML_Status expectedStatus;
+ const char *doc;
+ XML_Char namesep;
+ };
+ struct test_case cases[] = {
+ {XML_STATUS_OK, "<doc xmlns='one_two' />", XCS('\n')},
+ {XML_STATUS_ERROR, "<doc xmlns='one&#x0A;two' />", XCS('\n')},
+ {XML_STATUS_OK, "<doc xmlns='one:two' />", XCS(':')},
+ };
+
+ size_t i = 0;
+ size_t failCount = 0;
+ for (; i < sizeof(cases) / sizeof(cases[0]); i++) {
+ XML_Parser parser = XML_ParserCreateNS(NULL, cases[i].namesep);
+ XML_SetElementHandler(parser, dummy_start_element, dummy_end_element);
+ if (XML_Parse(parser, cases[i].doc, (int)strlen(cases[i].doc),
+ /*isFinal*/ XML_TRUE)
+ != cases[i].expectedStatus) {
+ failCount++;
+ }
+ XML_ParserFree(parser);
+ }
+
+ if (failCount) {
+ fail("Namespace separator handling is broken");
+ }
+}
+END_TEST
+
+
/* Control variable; the number of times duff_allocator() will successfully allocate */
#define ALLOC_ALWAYS_SUCCEED (-1)
#define REALLOC_ALWAYS_SUCCEED (-1)
@@ -12290,6 +12322,7 @@ make_suite(void)
tcase_add_test(tc_namespace, test_ns_utf16_doctype);
tcase_add_test(tc_namespace, test_ns_invalid_doctype);
tcase_add_test(tc_namespace, test_ns_double_colon_doctype);
+ tcase_add_test(tc_namespace, test_ns_separator_in_uri);
suite_add_tcase(s, tc_misc);
tcase_add_checked_fixture(tc_misc, NULL, basic_teardown);

View File

@ -0,0 +1,26 @@
diff -uap libexpat-R_2_2_5/expat/configure.ac.doc2man libexpat-R_2_2_5/expat/configure.ac
--- libexpat-R_2_2_5/expat/configure.ac.doc2man
+++ libexpat-R_2_2_5/expat/configure.ac
@@ -241,7 +241,7 @@ AS_IF([test "x$with_docbook" != xno],
[if test "x$with_docbook" != xcheck; then
AC_MSG_ERROR([Required program 'docbook2x-man' not found.])])])
-AM_CONDITIONAL(WITH_DOCBOOK, [test x${DOCBOOK_TO_MAN} != x])
+AM_CONDITIONAL(WITH_DOCBOOK, [test "x${DOCBOOK_TO_MAN}" != x])
AC_CONFIG_FILES([Makefile expat.pc])
AC_CONFIG_FILES([
diff -uap libexpat-R_2_2_5/expat/doc/Makefile.am.doc2man libexpat-R_2_2_5/expat/doc/Makefile.am
--- libexpat-R_2_2_5/expat/doc/Makefile.am.doc2man
+++ libexpat-R_2_2_5/expat/doc/Makefile.am
@@ -32,8 +32,9 @@ dist_man_MANS = xmlwf.1
xmlwf.1: xmlwf.xml
if WITH_DOCBOOK
+ -rm -f $@
$(DOCBOOK_TO_MAN) $<
- mv XMLWF.1 $@
+ test -f $@ || mv XMLWF.1 $@
else
@echo 'ERROR: Configure with --with-docbook for "make dist".' 1>&2
@false

File diff suppressed because it is too large Load Diff

View File

@ -1,172 +0,0 @@
commit cd3b344e0dbd19a812d0b4f34f9d089ed7c5c411
Author: Tomas Korbar <tkorbar@redhat.com>
Date: Tue Mar 19 15:12:18 2024 +0100
Fix CVE-2024-28757
Upstream PRs #841 and #842
diff --git a/expat/lib/xmlparse.c b/expat/lib/xmlparse.c
index 2ae64e9..0896b16 100644
--- a/expat/lib/xmlparse.c
+++ b/expat/lib/xmlparse.c
@@ -6164,7 +6164,7 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc,
dtd->keepProcessing = dtd->standalone;
goto endEntityValue;
}
- if (entity->open) {
+ if (entity->open || (entity == parser->m_declEntity)) {
if (enc == parser->m_encoding)
parser->m_eventPtr = entityTextPtr;
result = XML_ERROR_RECURSIVE_ENTITY_REF;
@@ -7680,6 +7680,8 @@ copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) {
static float
accountingGetCurrentAmplification(XML_Parser rootParser) {
+ // 1.........1.........12 => 22
+ const size_t lenOfShortestInclude = sizeof("<!ENTITY a SYSTEM 'b'>") - 1;
const XmlBigCount countBytesOutput
= rootParser->m_accounting.countBytesDirect
+ rootParser->m_accounting.countBytesIndirect;
@@ -7687,7 +7689,9 @@ accountingGetCurrentAmplification(XML_Parser rootParser) {
= rootParser->m_accounting.countBytesDirect
? (countBytesOutput
/ (float)(rootParser->m_accounting.countBytesDirect))
- : 1.0f;
+ : ((lenOfShortestInclude
+ + rootParser->m_accounting.countBytesIndirect)
+ / (float)lenOfShortestInclude);
assert(! rootParser->m_parentParser);
return amplificationFactor;
}
diff --git a/expat/tests/runtests.c b/expat/tests/runtests.c
index 941f61d..93adc45 100644
--- a/expat/tests/runtests.c
+++ b/expat/tests/runtests.c
@@ -1788,6 +1788,48 @@ START_TEST(test_wfc_no_recursive_entity_refs) {
}
END_TEST
+START_TEST(test_recursive_external_parameter_entity_2) {
+ struct TestCase {
+ const char *doc;
+ enum XML_Status expectedStatus;
+ };
+
+ struct TestCase cases[] = {
+ {"<!ENTITY % p1 '%p1;'>", XML_STATUS_ERROR},
+ {"<!ENTITY % p1 '%p1;'>"
+ "<!ENTITY % p1 'first declaration wins'>",
+ XML_STATUS_ERROR},
+ {"<!ENTITY % p1 'first declaration wins'>"
+ "<!ENTITY % p1 '%p1;'>",
+ XML_STATUS_OK},
+ {"<!ENTITY % p1 '&#37;p1;'>", XML_STATUS_OK},
+ };
+
+ for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
+ const char *const doc = cases[i].doc;
+ const enum XML_Status expectedStatus = cases[i].expectedStatus;
+
+ XML_Parser parser = XML_ParserCreate(NULL);
+ assert_true(parser != NULL);
+
+ XML_Parser ext_parser = XML_ExternalEntityParserCreate(parser, NULL, NULL);
+ assert_true(ext_parser != NULL);
+
+ const enum XML_Status actualStatus
+ = _XML_Parse_SINGLE_BYTES(ext_parser, doc, (int)strlen(doc), XML_TRUE);
+
+ assert_true(actualStatus == expectedStatus);
+ if (actualStatus != XML_STATUS_OK) {
+ assert_true(XML_GetErrorCode(ext_parser)
+ == XML_ERROR_RECURSIVE_ENTITY_REF);
+ }
+
+ XML_ParserFree(ext_parser);
+ XML_ParserFree(parser);
+ }
+}
+END_TEST
+
/* Test incomplete external entities are faulted */
START_TEST(test_ext_entity_invalid_parse) {
const char *text = "<!DOCTYPE doc [\n"
@@ -12719,6 +12761,60 @@ START_TEST(test_helper_unsigned_char_to_printable) {
fail("unsignedCharToPrintable result mistaken");
}
END_TEST
+
+START_TEST(test_amplification_isolated_external_parser) {
+ // NOTE: Length 44 is precisely twice the length of "<!ENTITY a SYSTEM 'b'>"
+ // (22) that is used in function accountingGetCurrentAmplification in
+ // xmlparse.c.
+ // 1.........1.........1.........1.........1..4 => 44
+ const char doc[] = "<!ENTITY % p1 '123456789_123456789_1234567'>";
+ const int docLen = (int)sizeof(doc) - 1;
+ const float maximumToleratedAmplification = 2.0f;
+
+ struct TestCase {
+ int offsetOfThreshold;
+ enum XML_Status expectedStatus;
+ };
+
+ struct TestCase cases[] = {
+ {-2, XML_STATUS_ERROR}, {-1, XML_STATUS_ERROR}, {0, XML_STATUS_ERROR},
+ {+1, XML_STATUS_OK}, {+2, XML_STATUS_OK},
+ };
+
+ for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
+ const int offsetOfThreshold = cases[i].offsetOfThreshold;
+ const enum XML_Status expectedStatus = cases[i].expectedStatus;
+ const unsigned long long activationThresholdBytes
+ = docLen + offsetOfThreshold;
+
+ XML_Parser parser = XML_ParserCreate(NULL);
+ assert_true(parser != NULL);
+
+ assert_true(XML_SetBillionLaughsAttackProtectionMaximumAmplification(
+ parser, maximumToleratedAmplification)
+ == XML_TRUE);
+ assert_true(XML_SetBillionLaughsAttackProtectionActivationThreshold(
+ parser, activationThresholdBytes)
+ == XML_TRUE);
+
+ XML_Parser ext_parser = XML_ExternalEntityParserCreate(parser, NULL, NULL);
+ assert_true(ext_parser != NULL);
+
+ const enum XML_Status actualStatus
+ = _XML_Parse_SINGLE_BYTES(ext_parser, doc, docLen, XML_TRUE);
+
+ assert_true(actualStatus == expectedStatus);
+ if (actualStatus != XML_STATUS_OK) {
+ assert_true(XML_GetErrorCode(ext_parser)
+ == XML_ERROR_AMPLIFICATION_LIMIT_BREACH);
+ }
+
+ XML_ParserFree(ext_parser);
+ XML_ParserFree(parser);
+ }
+}
+END_TEST
+
#endif // defined(XML_DTD)
static Suite *
@@ -12871,6 +12967,8 @@ make_suite(void) {
tcase_add_test__ifdef_xml_dtd(tc_basic, test_skipped_parameter_entity);
tcase_add_test__ifdef_xml_dtd(tc_basic,
test_recursive_external_parameter_entity);
+ tcase_add_test__ifdef_xml_dtd(tc_basic,
+ test_recursive_external_parameter_entity_2);
tcase_add_test(tc_basic, test_undefined_ext_entity_in_external_dtd);
tcase_add_test(tc_basic, test_suspend_xdecl);
tcase_add_test(tc_basic, test_abort_epilog);
@@ -13120,6 +13218,7 @@ make_suite(void) {
tcase_add_test(tc_accounting, test_accounting_precision);
tcase_add_test(tc_accounting, test_billion_laughs_attack_protection_api);
tcase_add_test(tc_accounting, test_helper_unsigned_char_to_printable);
+ tcase_add_test(tc_accounting, test_amplification_isolated_external_parser);
#endif
return s;

View File

@ -1,129 +0,0 @@
commit 05d87eb116ddde35bfa4e4c1d2ec7bcbda38c09b
Author: Tomas Korbar <tkorbar@redhat.com>
Date: Wed Sep 11 13:48:58 2024 +0200
Fix CVE-2024-45490
https://github.com/libexpat/libexpat/pull/890
diff --git a/expat/doc/reference.html b/expat/doc/reference.html
index a10f3cb..d618bd8 100644
--- a/expat/doc/reference.html
+++ b/expat/doc/reference.html
@@ -1098,7 +1098,9 @@ containing part (or perhaps all) of the document. The number of bytes of s
that are part of the document is indicated by <code>len</code>. This means
that <code>s</code> doesn't have to be null terminated. It also means that
if <code>len</code> is larger than the number of bytes in the block of
-memory that <code>s</code> points at, then a memory fault is likely. The
+memory that <code>s</code> points at, then a memory fault is likely.
+Negative values for <code>len</code> are rejected since Expat 2.2.1.
+The
<code>isFinal</code> parameter informs the parser that this is the last
piece of the document. Frequently, the last piece is empty (i.e.
<code>len</code> is zero.)
@@ -1114,11 +1116,17 @@ XML_ParseBuffer(XML_Parser p,
int isFinal);
</pre>
<div class="fcndef">
+<p>
This is just like <code><a href= "#XML_Parse" >XML_Parse</a></code>,
except in this case Expat provides the buffer. By obtaining the
buffer from Expat with the <code><a href= "#XML_GetBuffer"
>XML_GetBuffer</a></code> function, the application can avoid double
copying of the input.
+</p>
+
+<p>
+Negative values for <code>len</code> are rejected since Expat 2.6.3.
+</p>
</div>
<h4 id="XML_GetBuffer">XML_GetBuffer</h4>
diff --git a/expat/lib/xmlparse.c b/expat/lib/xmlparse.c
index 0896b16..f54e258 100644
--- a/expat/lib/xmlparse.c
+++ b/expat/lib/xmlparse.c
@@ -1998,6 +1998,12 @@ XML_ParseBuffer(XML_Parser parser, int len, int isFinal) {
if (parser == NULL)
return XML_STATUS_ERROR;
+
+ if (len < 0) {
+ parser->m_errorCode = XML_ERROR_INVALID_ARGUMENT;
+ return XML_STATUS_ERROR;
+ }
+
switch (parser->m_parsingStatus.parsing) {
case XML_SUSPENDED:
parser->m_errorCode = XML_ERROR_SUSPENDED;
diff --git a/expat/tests/runtests.c b/expat/tests/runtests.c
index 93adc45..ed88f9f 100644
--- a/expat/tests/runtests.c
+++ b/expat/tests/runtests.c
@@ -3856,6 +3856,57 @@ START_TEST(test_empty_parse) {
}
END_TEST
+/* Test XML_Parse for len < 0 */
+START_TEST(test_negative_len_parse) {
+ const char *const doc = "<root/>";
+ for (int isFinal = 0; isFinal < 2; isFinal++) {
+ XML_Parser parser = XML_ParserCreate(NULL);
+
+ if (XML_GetErrorCode(parser) != XML_ERROR_NONE)
+ fail("There was not supposed to be any initial parse error.");
+
+ const enum XML_Status status = XML_Parse(parser, doc, -1, isFinal);
+
+ if (status != XML_STATUS_ERROR)
+ fail("Negative len was expected to fail the parse but did not.");
+
+ if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_ARGUMENT)
+ fail("Parse error does not match XML_ERROR_INVALID_ARGUMENT.");
+
+ XML_ParserFree(parser);
+ }
+}
+END_TEST
+
+/* Test XML_ParseBuffer for len < 0 */
+START_TEST(test_negative_len_parse_buffer) {
+ const char *const doc = "<root/>";
+ for (int isFinal = 0; isFinal < 2; isFinal++) {
+ XML_Parser parser = XML_ParserCreate(NULL);
+
+ if (XML_GetErrorCode(parser) != XML_ERROR_NONE)
+ fail("There was not supposed to be any initial parse error.");
+
+ void *const buffer = XML_GetBuffer(parser, (int)strlen(doc));
+
+ if (buffer == NULL)
+ fail("XML_GetBuffer failed.");
+
+ memcpy(buffer, doc, strlen(doc));
+
+ const enum XML_Status status = XML_ParseBuffer(parser, -1, isFinal);
+
+ if (status != XML_STATUS_ERROR)
+ fail("Negative len was expected to fail the parse but did not.");
+
+ if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_ARGUMENT)
+ fail("Parse error does not match XML_ERROR_INVALID_ARGUMENT.");
+
+ XML_ParserFree(parser);
+ }
+}
+END_TEST
+
/* Test odd corners of the XML_GetBuffer interface */
static enum XML_Status
get_feature(enum XML_FeatureEnum feature_id, long *presult) {
@@ -12937,6 +12988,8 @@ make_suite(void) {
tcase_add_test__ifdef_xml_dtd(tc_basic, test_user_parameters);
tcase_add_test__ifdef_xml_dtd(tc_basic, test_ext_entity_ref_parameter);
tcase_add_test(tc_basic, test_empty_parse);
+ tcase_add_test(tc_basic, test_negative_len_parse);
+ tcase_add_test(tc_basic, test_negative_len_parse_buffer);
tcase_add_test(tc_basic, test_get_buffer_1);
tcase_add_test(tc_basic, test_get_buffer_2);
#if defined(XML_CONTEXT_BYTES)

View File

@ -1,31 +0,0 @@
From 8e439a9947e9dc80a395c0c7456545d8d9d9e421 Mon Sep 17 00:00:00 2001
From: Sebastian Pipping <sebastian@pipping.org>
Date: Mon, 19 Aug 2024 22:34:13 +0200
Subject: [PATCH] lib: Detect integer overflow in dtdCopy
Reported by TaiYou
---
expat/lib/xmlparse.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/expat/lib/xmlparse.c b/expat/lib/xmlparse.c
index 91682c188..e2327bdcf 100644
--- a/expat/lib/xmlparse.c
+++ b/expat/lib/xmlparse.c
@@ -7016,6 +7016,16 @@ dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd,
if (! newE)
return 0;
if (oldE->nDefaultAtts) {
+ /* Detect and prevent integer overflow.
+ * The preprocessor guard addresses the "always false" warning
+ * from -Wtype-limits on platforms where
+ * sizeof(int) < sizeof(size_t), e.g. on x86_64. */
+#if UINT_MAX >= SIZE_MAX
+ if ((size_t)oldE->nDefaultAtts
+ > ((size_t)(-1) / sizeof(DEFAULT_ATTRIBUTE))) {
+ return 0;
+ }
+#endif
newE->defaultAtts
= ms->malloc_fcn(oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE));
if (! newE->defaultAtts) {

View File

@ -1,30 +0,0 @@
From 9bf0f2c16ee86f644dd1432507edff94c08dc232 Mon Sep 17 00:00:00 2001
From: Sebastian Pipping <sebastian@pipping.org>
Date: Mon, 19 Aug 2024 22:37:16 +0200
Subject: [PATCH] lib: Detect integer overflow in function nextScaffoldPart
Reported by TaiYou
---
expat/lib/xmlparse.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/expat/lib/xmlparse.c b/expat/lib/xmlparse.c
index 91682c188..f737575ea 100644
--- a/expat/lib/xmlparse.c
+++ b/expat/lib/xmlparse.c
@@ -7558,6 +7558,15 @@ nextScaffoldPart(XML_Parser parser) {
int next;
if (! dtd->scaffIndex) {
+ /* Detect and prevent integer overflow.
+ * The preprocessor guard addresses the "always false" warning
+ * from -Wtype-limits on platforms where
+ * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
+#if UINT_MAX >= SIZE_MAX
+ if (parser->m_groupSize > ((size_t)(-1) / sizeof(int))) {
+ return -1;
+ }
+#endif
dtd->scaffIndex = (int *)MALLOC(parser, parser->m_groupSize * sizeof(int));
if (! dtd->scaffIndex)
return -1;

View File

@ -1,108 +0,0 @@
commit 38905b99bb78a6a691ed8358f30030116783656c
Author: Tomas Korbar <tkorbar@redhat.com>
Date: Thu Nov 7 15:00:46 2024 +0100
Fix CVE-2024-50602
See https://github.com/libexpat/libexpat/pull/915
diff --git a/expat/lib/expat.h b/expat/lib/expat.h
index 842dd70..69b0ba1 100644
--- a/expat/lib/expat.h
+++ b/expat/lib/expat.h
@@ -128,7 +128,9 @@ enum XML_Error {
/* Added in 2.3.0. */
XML_ERROR_NO_BUFFER,
/* Added in 2.4.0. */
- XML_ERROR_AMPLIFICATION_LIMIT_BREACH
+ XML_ERROR_AMPLIFICATION_LIMIT_BREACH,
+ /* Added in 2.6.4. */
+ XML_ERROR_NOT_STARTED,
};
enum XML_Content_Type {
diff --git a/expat/lib/xmlparse.c b/expat/lib/xmlparse.c
index e0c2873..8b2af91 100644
--- a/expat/lib/xmlparse.c
+++ b/expat/lib/xmlparse.c
@@ -2193,6 +2193,9 @@ XML_StopParser(XML_Parser parser, XML_Bool resumable) {
if (parser == NULL)
return XML_STATUS_ERROR;
switch (parser->m_parsingStatus.parsing) {
+ case XML_INITIALIZED:
+ parser->m_errorCode = XML_ERROR_NOT_STARTED;
+ return XML_STATUS_ERROR;
case XML_SUSPENDED:
if (resumable) {
parser->m_errorCode = XML_ERROR_SUSPENDED;
@@ -2203,7 +2206,7 @@ XML_StopParser(XML_Parser parser, XML_Bool resumable) {
case XML_FINISHED:
parser->m_errorCode = XML_ERROR_FINISHED;
return XML_STATUS_ERROR;
- default:
+ case XML_PARSING:
if (resumable) {
#ifdef XML_DTD
if (parser->m_isParamEntity) {
@@ -2214,6 +2217,9 @@ XML_StopParser(XML_Parser parser, XML_Bool resumable) {
parser->m_parsingStatus.parsing = XML_SUSPENDED;
} else
parser->m_parsingStatus.parsing = XML_FINISHED;
+ break;
+ default:
+ assert(0);
}
return XML_STATUS_OK;
}
@@ -2478,6 +2484,9 @@ XML_ErrorString(enum XML_Error code) {
case XML_ERROR_AMPLIFICATION_LIMIT_BREACH:
return XML_L(
"limit on input amplification factor (from DTD and entities) breached");
+ /* Added in 2.6.4. */
+ case XML_ERROR_NOT_STARTED:
+ return XML_L("parser not started");
}
return NULL;
}
diff --git a/expat/tests/runtests.c b/expat/tests/runtests.c
index ed88f9f..5769aa0 100644
--- a/expat/tests/runtests.c
+++ b/expat/tests/runtests.c
@@ -8711,6 +8711,28 @@ START_TEST(test_misc_tag_mismatch_reset_leak) {
}
END_TEST
+START_TEST(test_misc_resumeparser_not_crashing) {
+ XML_Parser parser = XML_ParserCreate(NULL);
+ XML_GetBuffer(parser, 1);
+ XML_StopParser(parser, /*resumable=*/XML_TRUE);
+ XML_ResumeParser(parser); // could crash here, previously
+ XML_ParserFree(parser);
+}
+END_TEST
+
+START_TEST(test_misc_stopparser_rejects_unstarted_parser) {
+ const XML_Bool cases[] = {XML_TRUE, XML_FALSE};
+ for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
+ const XML_Bool resumable = cases[i];
+ XML_Parser parser = XML_ParserCreate(NULL);
+ assert_true(XML_GetErrorCode(parser) == XML_ERROR_NONE);
+ assert_true(XML_StopParser(parser, resumable) == XML_STATUS_ERROR);
+ assert_true(XML_GetErrorCode(parser) == XML_ERROR_NOT_STARTED);
+ XML_ParserFree(parser);
+ }
+}
+END_TEST
+
static void
alloc_setup(void) {
XML_Memory_Handling_Suite memsuite = {duff_allocator, duff_reallocator, free};
@@ -13176,6 +13198,8 @@ make_suite(void) {
tcase_add_test__ifdef_xml_dtd(
tc_misc, test_misc_deny_internal_entity_closing_doctype_issue_317);
tcase_add_test(tc_misc, test_misc_tag_mismatch_reset_leak);
+ tcase_add_test(tc_misc, test_misc_resumeparser_not_crashing);
+ tcase_add_test(tc_misc, test_misc_stopparser_rejects_unstarted_parser);
suite_add_tcase(s, tc_alloc);
tcase_add_checked_fixture(tc_alloc, alloc_setup, alloc_teardown);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,635 +0,0 @@
From 88e9daef8fdaea58da12044cd16f5c9c1f201bfa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Berkay=20Eren=20=C3=9Cr=C3=BCn?= <berkay.ueruen@siemens.com>
Date: Fri, 13 Mar 2026 13:26:45 +0100
Subject: [PATCH 1/6] Make "counting_start_element_handler" count default attrs
---
expat/tests/runtests.c | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/expat/tests/runtests.c b/expat/tests/runtests.c
index 4a0a2e3f..c66656e0 100644
--- a/expat/tests/runtests.c
+++ b/expat/tests/runtests.c
@@ -3045,6 +3045,7 @@ typedef struct attrInfo {
typedef struct elementInfo {
const XML_Char *name;
int attr_count;
+ int default_attr_count;
const XML_Char *id_name;
AttrInfo *attributes;
} ElementInfo;
@@ -3089,7 +3090,7 @@ counting_start_element_handler(void *userData, const XML_Char *name,
fail("ID does not have the correct name");
return;
}
- for (i = 0; i < info->attr_count; i++) {
+ for (i = 0; i < info->attr_count + info->default_attr_count; i++) {
attr = info->attributes;
while (attr->name != NULL) {
if (! xcstrcmp(atts[0], attr->name))
@@ -3122,9 +3123,9 @@ START_TEST(test_attributes) {
{XCS("id"), XCS("one")},
{NULL, NULL}};
AttrInfo tag_info[] = {{XCS("c"), XCS("3")}, {NULL, NULL}};
- ElementInfo info[] = {{XCS("doc"), 3, XCS("id"), NULL},
- {XCS("tag"), 1, NULL, NULL},
- {NULL, 0, NULL, NULL}};
+ ElementInfo info[] = {{XCS("doc"), 3, 0, XCS("id"), NULL},
+ {XCS("tag"), 1, 0, NULL, NULL},
+ {NULL, 0, 0, NULL, NULL}};
info[0].attributes = doc_info;
info[1].attributes = tag_info;
@@ -7111,7 +7112,7 @@ START_TEST(test_deep_nested_attribute_entity) {
(long unsigned)(N_LINES - 1));
AttrInfo doc_info[] = {{XCS("name"), XCS("deepText")}, {NULL, NULL}};
- ElementInfo info[] = {{XCS("foo"), 1, NULL, NULL}, {NULL, 0, NULL, NULL}};
+ ElementInfo info[] = {{XCS("foo"), 1, 0, NULL, NULL}, {NULL, 0, 0, NULL, NULL}};
info[0].attributes = doc_info;
XML_SetStartElementHandler(g_parser, counting_start_element_handler);
--
2.52.0
From 04796d5b0e71cc37a4bfa0031270d7bee6e0128a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Berkay=20Eren=20=C3=9Cr=C3=BCn?= <berkay.ueruen@siemens.com>
Date: Fri, 13 Mar 2026 13:27:31 +0100
Subject: [PATCH 2/6] test(attlist): Cover duplicate attribute names
Co-authored-by: Sebastian Pipping <sebastian@pipping.org>
---
expat/tests/runtests.c | 315 +++++++++++++++++++++++++++++++++++++++--
1 file changed, 302 insertions(+), 13 deletions(-)
diff --git a/expat/tests/runtests.c b/expat/tests/runtests.c
index c66656e0..13583e0a 100644
--- a/expat/tests/runtests.c
+++ b/expat/tests/runtests.c
@@ -3059,7 +3059,9 @@ typedef struct StructParserAndElementInfo {
static void XMLCALL
counting_start_element_handler(void *userData, const XML_Char *name,
const XML_Char **atts) {
- ElementInfo *info = (ElementInfo *)userData;
+ ParserAndElementInfo *const parserAndElementInfos
+ = (ParserAndElementInfo *)userData;
+ ElementInfo *info = parserAndElementInfos->info;
AttrInfo *attr;
int count, id, i;
@@ -3076,17 +3078,17 @@ counting_start_element_handler(void *userData, const XML_Char *name,
* is possibly a little unexpected, but it is what the
* documentation in expat.h tells us to expect.
*/
- count = XML_GetSpecifiedAttributeCount(g_parser);
+ count = XML_GetSpecifiedAttributeCount(parserAndElementInfos->parser);
if (info->attr_count * 2 != count) {
fail("Not got expected attribute count");
return;
}
- id = XML_GetIdAttributeIndex(g_parser);
+ id = XML_GetIdAttributeIndex(parserAndElementInfos->parser);
if (id == -1 && info->id_name != NULL) {
fail("ID not present");
return;
}
- if (id != -1 && xcstrcmp(atts[id], info->id_name)) {
+ if (id != -1 && xcstrcmp(atts[id], info->id_name) != 0) {
fail("ID does not have the correct name");
return;
}
@@ -3101,7 +3103,7 @@ counting_start_element_handler(void *userData, const XML_Char *name,
fail("Attribute not recognised");
return;
}
- if (xcstrcmp(atts[1], attr->value)) {
+ if (xcstrcmp(atts[1], attr->value) != 0) {
fail("Attribute has wrong value");
return;
}
@@ -3123,20 +3125,296 @@ START_TEST(test_attributes) {
{XCS("id"), XCS("one")},
{NULL, NULL}};
AttrInfo tag_info[] = {{XCS("c"), XCS("3")}, {NULL, NULL}};
- ElementInfo info[] = {{XCS("doc"), 3, 0, XCS("id"), NULL},
- {XCS("tag"), 1, 0, NULL, NULL},
+ ElementInfo info[] = {{XCS("doc"), 3, 0, XCS("id"), doc_info},
+ {XCS("tag"), 1, 0, NULL, tag_info},
{NULL, 0, 0, NULL, NULL}};
- info[0].attributes = doc_info;
- info[1].attributes = tag_info;
+
+ ParserAndElementInfo parserAndElementInfos = {
+ g_parser,
+ info,
+ };
XML_SetStartElementHandler(g_parser, counting_start_element_handler);
- XML_SetUserData(g_parser, info);
+ XML_SetUserData(g_parser, &parserAndElementInfos);
if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
== XML_STATUS_ERROR)
xml_failure(g_parser);
}
END_TEST
+START_TEST(test_duplicate_cdata_attribute) {
+ /*
+ https://www.w3.org/TR/xml/#attdecls
+
+ Test the following statement from the linked specification:
+ When more than one definition is provided for the same attribute of a given
+ element type, the first declaration is binding and later declarations are
+ ignored.
+ */
+
+ const char *text
+ = "<!DOCTYPE doc [\n"
+ " <!ATTLIST doc attribute CDATA 'expected' attribute CDATA 'ignored'>\n"
+ "]>\n"
+ "<doc/>\n";
+ AttrInfo doc_info[] = {{XCS("attribute"), XCS("expected")}, {NULL, NULL}};
+ ElementInfo info[]
+ = {{XCS("doc"), 0, 1, NULL, doc_info}, {NULL, 0, 0, NULL, NULL}};
+
+ XML_Parser parser = XML_ParserCreate(NULL);
+ assert_true(parser != NULL);
+
+ ParserAndElementInfo parserAndElementInfos = {
+ parser,
+ info,
+ };
+
+ XML_SetStartElementHandler(parser, counting_start_element_handler);
+ XML_SetUserData(parser, &parserAndElementInfos);
+
+ if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE)
+ != XML_STATUS_OK)
+ xml_failure(parser);
+
+ XML_ParserFree(parser);
+}
+END_TEST
+
+START_TEST(test_duplicate_id_attribute_1) {
+ /*
+ https://www.w3.org/TR/xml/#attdecls
+
+ Test the following statement from the linked specification:
+ When more than one definition is provided for the same attribute of a given
+ element type, the first declaration is binding and later declarations are
+ ignored.
+ */
+
+ const char *text
+ = "<!DOCTYPE doc [\n"
+ " <!ATTLIST doc identifier CDATA 'expected' identifier ID #REQUIRED>\n"
+ "]>\n"
+ "<doc/>\n";
+ AttrInfo doc_info[] = {{XCS("identifier"), XCS("expected")}, {NULL, NULL}};
+ ElementInfo info[]
+ = {{XCS("doc"), 0, 1, NULL, doc_info}, {NULL, 0, 0, NULL, NULL}};
+
+ XML_Parser parser = XML_ParserCreate(NULL);
+ assert_true(parser != NULL);
+
+ ParserAndElementInfo parserAndElementInfos = {
+ parser,
+ info,
+ };
+
+ XML_SetStartElementHandler(parser, counting_start_element_handler);
+ XML_SetUserData(parser, &parserAndElementInfos);
+
+ if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE)
+ != XML_STATUS_OK)
+ xml_failure(parser);
+
+ XML_ParserFree(parser);
+}
+END_TEST
+
+START_TEST(test_duplicate_id_attribute_2) {
+ /*
+ https://www.w3.org/TR/xml/#attdecls
+
+ Test the following statement from the linked specification:
+ When more than one definition is provided for the same attribute of a given
+ element type, the first declaration is binding and later declarations are
+ ignored.
+ */
+
+ const char *text
+ = "<!DOCTYPE doc [\n"
+ " <!ATTLIST doc identifier ID #REQUIRED identifier CDATA 'unexpected'>\n"
+ "]>\n"
+ "<doc/>\n";
+ AttrInfo doc_info[] = {{NULL, NULL}};
+
+ ElementInfo info[]
+ = {{XCS("doc"), 0, 0, NULL, doc_info}, {NULL, 0, 0, NULL, NULL}};
+
+ XML_Parser parser = XML_ParserCreate(NULL);
+ assert_true(parser != NULL);
+
+ ParserAndElementInfo parserAndElementInfos = {
+ parser,
+ info,
+ };
+
+ XML_SetStartElementHandler(parser, counting_start_element_handler);
+ XML_SetUserData(parser, &parserAndElementInfos);
+
+ if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE)
+ != XML_STATUS_OK)
+ xml_failure(parser);
+
+ XML_ParserFree(parser);
+}
+END_TEST
+
+START_TEST(test_duplicate_cdata_attribute_multiple_attlistdecl) {
+ /*
+ https://www.w3.org/TR/xml/#attdecls
+
+ Test the following statement from the linked specification:
+ When more than one AttlistDecl is provided for a given element type,
+ the contents of all those provided are merged.
+ */
+ const char *text = "<!DOCTYPE doc [\n"
+ " <!ATTLIST doc attribute CDATA 'expected'>\n"
+ " <!ATTLIST doc attribute CDATA 'ignored'>\n"
+ "]>\n"
+ "<doc/>\n";
+ AttrInfo doc_info[] = {{XCS("attribute"), XCS("expected")}, {NULL, NULL}};
+ ElementInfo info[]
+ = {{XCS("doc"), 0, 1, NULL, doc_info}, {NULL, 0, 0, NULL, NULL}};
+
+ XML_Parser parser = XML_ParserCreate(NULL);
+ assert_true(parser != NULL);
+
+ ParserAndElementInfo parserAndElementInfos = {
+ parser,
+ info,
+ };
+
+ XML_SetStartElementHandler(parser, counting_start_element_handler);
+ XML_SetUserData(parser, &parserAndElementInfos);
+
+ if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE)
+ != XML_STATUS_OK)
+ xml_failure(parser);
+
+ XML_ParserFree(parser);
+}
+END_TEST
+
+START_TEST(test_duplicate_cdata_attribute_multiple_attlistdecl_2) {
+ /*
+ https://www.w3.org/TR/xml/#attdecls
+
+ Test the following statement from the linked specification:
+ When more than one AttlistDecl is provided for a given element type,
+ the contents of all those provided are merged.
+ */
+ const char *text = "<!DOCTYPE doc [\n"
+ " <!ATTLIST doc attribute CDATA 'expected_doc'>\n"
+ " <!ATTLIST tag attribute CDATA 'expected_tag'>\n"
+ " <!ATTLIST doc attribute CDATA 'ignored_doc'>\n"
+ "]>\n"
+ "<doc><tag></tag></doc>\n";
+ AttrInfo doc_info[] = {{XCS("attribute"), XCS("expected_doc")}, {NULL, NULL}};
+ AttrInfo tag_info[] = {{XCS("attribute"), XCS("expected_tag")}, {NULL, NULL}};
+ ElementInfo info[] = {{XCS("doc"), 0, 1, NULL, doc_info},
+ {XCS("tag"), 0, 1, NULL, tag_info},
+ {NULL, 0, 0, NULL, NULL}};
+
+ XML_Parser parser = XML_ParserCreate(NULL);
+ assert_true(parser != NULL);
+
+ ParserAndElementInfo parserAndElementInfos = {
+ parser,
+ info,
+ };
+
+ XML_SetStartElementHandler(parser, counting_start_element_handler);
+ XML_SetUserData(parser, &parserAndElementInfos);
+
+ if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE)
+ != XML_STATUS_OK)
+ xml_failure(parser);
+
+ XML_ParserFree(parser);
+}
+END_TEST
+
+START_TEST(test_duplicate_cdata_attribute_multiple_attlistdecl_3) {
+ /*
+ https://www.w3.org/TR/xml/#attdecls
+
+ Test the following statement from the linked specification:
+ When more than one AttlistDecl is provided for a given element type,
+ the contents of all those provided are merged.
+ */
+ const char *text
+ = "<!DOCTYPE doc [\n"
+ " <!ATTLIST doc attribute CDATA 'expected_doc'>\n"
+ " <!ATTLIST tag attribute CDATA 'expected_tag'>\n"
+ " <!ATTLIST doc second_attribute CDATA 'second_expected_doc' attribute CDATA 'ignored_doc'>\n"
+ "]>\n"
+ "<doc><tag></tag></doc>\n";
+ AttrInfo doc_info[] = {{XCS("attribute"), XCS("expected_doc")},
+ {XCS("second_attribute"), XCS("second_expected_doc")},
+ {NULL, NULL}};
+ AttrInfo tag_info[] = {{XCS("attribute"), XCS("expected_tag")}, {NULL, NULL}};
+ ElementInfo info[] = {{XCS("doc"), 0, 2, NULL, doc_info},
+ {XCS("tag"), 0, 1, NULL, tag_info},
+ {NULL, 0, 0, NULL, NULL}};
+
+ XML_Parser parser = XML_ParserCreate(NULL);
+ assert_true(parser != NULL);
+
+ ParserAndElementInfo parserAndElementInfos = {
+ parser,
+ info,
+ };
+
+ XML_SetStartElementHandler(parser, counting_start_element_handler);
+ XML_SetUserData(parser, &parserAndElementInfos);
+
+ if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE)
+ != XML_STATUS_OK)
+ xml_failure(parser);
+
+ XML_ParserFree(parser);
+}
+END_TEST
+
+START_TEST(test_duplicate_id_attribute_multiple_attlistdecl) {
+ /*
+ https://www.w3.org/TR/xml/#attdecls
+
+ Test the following statement from the linked specification:
+ When more than one AttlistDecl is provided for a given element type,
+ the contents of all those provided are merged.
+ */
+ const char *text = "<!DOCTYPE doc [\n"
+ " <!ATTLIST doc identifier ID #REQUIRED>\n"
+ " <!ATTLIST tag identifier CDATA 'identifier_tag'>\n"
+ " <!ATTLIST doc identifier CDATA 'ignored'>\n"
+ "]>\n"
+ "<doc identifier='doc_identity'><tag></tag></doc>\n";
+ AttrInfo doc_info[]
+ = {{XCS("identifier"), XCS("doc_identity")}, {NULL, NULL}};
+ AttrInfo tag_info[]
+ = {{XCS("identifier"), XCS("identifier_tag")}, {NULL, NULL}};
+ ElementInfo info[] = {{XCS("doc"), 1, 0, XCS("identifier"), doc_info},
+ {XCS("tag"), 0, 1, NULL, tag_info},
+ {NULL, 0, 0, NULL, NULL}};
+
+ XML_Parser parser = XML_ParserCreate(NULL);
+ assert_true(parser != NULL);
+
+ ParserAndElementInfo parserAndElementInfos = {
+ parser,
+ info,
+ };
+
+ XML_SetStartElementHandler(parser, counting_start_element_handler);
+ XML_SetUserData(parser, &parserAndElementInfos);
+
+ if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE)
+ != XML_STATUS_OK)
+ xml_failure(parser);
+
+ XML_ParserFree(parser);
+}
+END_TEST
+
/* Test reset works correctly in the middle of processing an internal
* entity. Exercises some obscure code in XML_ParserReset().
*/
@@ -7112,11 +7390,13 @@ START_TEST(test_deep_nested_attribute_entity) {
(long unsigned)(N_LINES - 1));
AttrInfo doc_info[] = {{XCS("name"), XCS("deepText")}, {NULL, NULL}};
- ElementInfo info[] = {{XCS("foo"), 1, 0, NULL, NULL}, {NULL, 0, 0, NULL, NULL}};
- info[0].attributes = doc_info;
+ ElementInfo info[]
+ = {{XCS("foo"), 1, 0, NULL, doc_info}, {NULL, 0, 0, NULL, NULL}};
+
+ ParserAndElementInfo parserAndElementInfos = {g_parser, info};
XML_SetStartElementHandler(g_parser, counting_start_element_handler);
- XML_SetUserData(g_parser, &info);
+ XML_SetUserData(g_parser, &parserAndElementInfos);
if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
== XML_STATUS_ERROR)
@@ -13625,6 +13905,15 @@ make_suite(void) {
tcase_add_test__ifdef_xml_dtd(tc_basic, test_empty_foreign_dtd);
tcase_add_test(tc_basic, test_set_base);
tcase_add_test(tc_basic, test_attributes);
+ tcase_add_test(tc_basic, test_duplicate_cdata_attribute);
+ tcase_add_test(tc_basic, test_duplicate_id_attribute_1);
+ tcase_add_test(tc_basic, test_duplicate_id_attribute_2);
+ tcase_add_test(tc_basic, test_duplicate_cdata_attribute_multiple_attlistdecl);
+ tcase_add_test(tc_basic,
+ test_duplicate_cdata_attribute_multiple_attlistdecl_2);
+ tcase_add_test(tc_basic,
+ test_duplicate_cdata_attribute_multiple_attlistdecl_3);
+ tcase_add_test(tc_basic, test_duplicate_id_attribute_multiple_attlistdecl);
tcase_add_test(tc_basic, test_reset_in_entity);
tcase_add_test(tc_basic, test_resume_invalid_parse);
tcase_add_test(tc_basic, test_resume_resuspended);
--
2.52.0
From 58d4b742fd88c9789e4a5e68de02164145f9c593 Mon Sep 17 00:00:00 2001
From: Sebastian Pipping <sebastian@pipping.org>
Date: Mon, 13 Apr 2026 01:34:03 +0200
Subject: [PATCH 3/6] tests: Make counting_start_element_handler enforce
complete attribute lists
---
expat/tests/runtests.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/expat/tests/runtests.c b/expat/tests/runtests.c
index 13583e0a..659ed030 100644
--- a/expat/tests/runtests.c
+++ b/expat/tests/runtests.c
@@ -3110,6 +3110,9 @@ counting_start_element_handler(void *userData, const XML_Char *name,
/* Remember, two entries in atts per attribute (see above) */
atts += 2;
}
+
+ // Self-test that the test case's list of expected attributes is complete
+ assert_true(atts[0] == NULL);
}
START_TEST(test_attributes) {
--
2.52.0
From 2f13a612e82242af9954508240c6edfaf7cb0721 Mon Sep 17 00:00:00 2001
From: Sebastian Pipping <sebastian@pipping.org>
Date: Sun, 8 Mar 2026 22:14:41 +0100
Subject: [PATCH 4/6] lib: Extract a constant for upcoming reuse
---
expat/lib/xmlparse.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/expat/lib/xmlparse.c b/expat/lib/xmlparse.c
index cba41f4b..ac9512e9 100644
--- a/expat/lib/xmlparse.c
+++ b/expat/lib/xmlparse.c
@@ -7528,8 +7528,9 @@ dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd,
newE->prefix = (PREFIX *)lookup(oldParser, &(newDtd->prefixes),
oldE->prefix->name, 0);
for (i = 0; i < newE->nDefaultAtts; i++) {
+ const XML_Char *const attributeName = oldE->defaultAtts[i].id->name;
newE->defaultAtts[i].id = (ATTRIBUTE_ID *)lookup(
- oldParser, &(newDtd->attributeIds), oldE->defaultAtts[i].id->name, 0);
+ oldParser, &(newDtd->attributeIds), attributeName, 0);
newE->defaultAtts[i].isCdata = oldE->defaultAtts[i].isCdata;
if (oldE->defaultAtts[i].value) {
newE->defaultAtts[i].value
--
2.52.0
From 8211c5affe00c6d92025f34ae7ffbc0f78426692 Mon Sep 17 00:00:00 2001
From: Sebastian Pipping <sebastian@pipping.org>
Date: Sun, 8 Mar 2026 23:05:49 +0100
Subject: [PATCH 5/6] lib: Introduce ELEMENT_TYPE.defaultAttsNames
---
expat/lib/xmlparse.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/expat/lib/xmlparse.c b/expat/lib/xmlparse.c
index ac9512e9..d8e7335d 100644
--- a/expat/lib/xmlparse.c
+++ b/expat/lib/xmlparse.c
@@ -368,6 +368,7 @@ typedef struct {
int nDefaultAtts;
int allocDefaultAtts;
DEFAULT_ATTRIBUTE *defaultAtts;
+ HASH_TABLE defaultAttsNames;
} ELEMENT_TYPE;
typedef struct {
@@ -3756,6 +3757,8 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
sizeof(ELEMENT_TYPE));
if (! elementType)
return XML_ERROR_NO_MEMORY;
+ if (! elementType->defaultAttsNames.parser)
+ hashTableInit(&(elementType->defaultAttsNames), parser);
if (parser->m_ns && ! setElementTypePrefix(parser, elementType))
return XML_ERROR_NO_MEMORY;
}
@@ -7369,6 +7372,7 @@ dtdReset(DTD *p, XML_Parser parser) {
ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter);
if (! e)
break;
+ hashTableDestroy(&(e->defaultAttsNames));
if (e->allocDefaultAtts != 0)
FREE(parser, e->defaultAtts);
}
@@ -7410,6 +7414,7 @@ dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser) {
ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter);
if (! e)
break;
+ hashTableDestroy(&(e->defaultAttsNames));
if (e->allocDefaultAtts != 0)
FREE(parser, e->defaultAtts);
}
@@ -7503,6 +7508,10 @@ dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd,
sizeof(ELEMENT_TYPE));
if (! newE)
return 0;
+
+ if (! newE->defaultAttsNames.parser)
+ hashTableInit(&(newE->defaultAttsNames), parser);
+
if (oldE->nDefaultAtts) {
/* Detect and prevent integer overflow.
* The preprocessor guard addresses the "always false" warning
@@ -7539,6 +7548,12 @@ dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd,
return 0;
} else
newE->defaultAtts[i].value = NULL;
+
+ NAMED *const nameAddedOrFound = (NAMED *)lookup(
+ parser, &(newE->defaultAttsNames), attributeName, sizeof(NAMED));
+ if (! nameAddedOrFound) {
+ return 0;
+ }
}
}
@@ -8263,6 +8278,8 @@ getElementType(XML_Parser parser, const ENCODING *enc, const char *ptr,
sizeof(ELEMENT_TYPE));
if (! ret)
return NULL;
+ if (! ret->defaultAttsNames.parser)
+ hashTableInit(&(ret->defaultAttsNames), getRootParserOf(parser, NULL));
if (ret->name != name)
poolDiscard(&dtd->pool);
else {
--
2.52.0
From 756dfc0850eb52005366da45859b1bb80a80466e Mon Sep 17 00:00:00 2001
From: Sebastian Pipping <sebastian@pipping.org>
Date: Sun, 8 Mar 2026 23:06:29 +0100
Subject: [PATCH 6/6] lib: Leverage ELEMENT_TYPE.defaultAttsNames for attribute
collision detection
.. to resolve quadratic runtime behavior
---
expat/lib/xmlparse.c | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/expat/lib/xmlparse.c b/expat/lib/xmlparse.c
index d8e7335d..d7d815e8 100644
--- a/expat/lib/xmlparse.c
+++ b/expat/lib/xmlparse.c
@@ -7009,10 +7009,10 @@ defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, XML_Bool isCdata,
if (value || isId) {
/* The handling of default attributes gets messed up if we have
a default which duplicates a non-default. */
- int i;
- for (i = 0; i < type->nDefaultAtts; i++)
- if (attId == type->defaultAtts[i].id)
- return 1;
+ NAMED *const nameFound
+ = (NAMED *)lookup(parser, &(type->defaultAttsNames), attId->name, 0);
+ if (nameFound)
+ return 1;
if (isId && ! type->idAtt && ! attId->xmlns)
type->idAtt = attId;
}
@@ -7059,6 +7059,12 @@ defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, XML_Bool isCdata,
att->isCdata = isCdata;
if (! isCdata)
attId->maybeTokenized = XML_TRUE;
+
+ NAMED *const nameAddedOrFound = (NAMED *)lookup(
+ parser, &(type->defaultAttsNames), attId->name, sizeof(NAMED));
+ if (! nameAddedOrFound)
+ return 0;
+
type->nDefaultAtts += 1;
return 1;
}
--
2.52.0

File diff suppressed because it is too large Load Diff

View File

@ -1,288 +0,0 @@
From b5b73d23072fd277b3816b45c027238260467947 Mon Sep 17 00:00:00 2001
From: Matthew Fernandez <matthew.fernandez@gmail.com>
Date: Thu, 4 Jun 2026 17:01:02 -0700
Subject: [PATCH 1/5] lib: Remove reuse of `m_groupSize` to count
`m_scaffIndex` allocation
The sizes of the two arrays `m_groupConnector` and `scaffIndex` need to
vary independently. This change is a step towards allowing this.
Anthropic: ANT-2026-00037
Anthropic: ANT-2026-03621
Anthropic: ANT-2026-03867
Co-authored-by: Alessandro Gario <alessandro.gario@trailofbits.com>
---
expat/lib/xmlparse.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/expat/lib/xmlparse.c b/expat/lib/xmlparse.c
index d7d815e8..3b5e90b1 100644
--- a/expat/lib/xmlparse.c
+++ b/expat/lib/xmlparse.c
@@ -398,6 +398,7 @@ typedef struct {
unsigned scaffCount;
int scaffLevel;
int *scaffIndex;
+ size_t scaffIndexSize;
} DTD;
enum EntityType {
@@ -5812,6 +5813,7 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
if (new_scaff_index == NULL)
return XML_ERROR_NO_MEMORY;
dtd->scaffIndex = new_scaff_index;
+ dtd->scaffIndexSize = parser->m_groupSize;
}
} else {
parser->m_groupConnector
@@ -7358,6 +7360,7 @@ dtdCreate(XML_Parser parser) {
p->in_eldecl = XML_FALSE;
p->scaffIndex = NULL;
+ p->scaffIndexSize = 0;
p->scaffold = NULL;
p->scaffLevel = 0;
p->scaffSize = 0;
@@ -7399,6 +7402,7 @@ dtdReset(DTD *p, XML_Parser parser) {
FREE(parser, p->scaffIndex);
p->scaffIndex = NULL;
+ p->scaffIndexSize = 0;
FREE(parser, p->scaffold);
p->scaffold = NULL;
@@ -7586,6 +7590,7 @@ dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd,
newDtd->scaffSize = oldDtd->scaffSize;
newDtd->scaffLevel = oldDtd->scaffLevel;
newDtd->scaffIndex = oldDtd->scaffIndex;
+ newDtd->scaffIndexSize = oldDtd->scaffIndexSize;
return 1;
} /* End dtdCopy */
@@ -8090,6 +8095,7 @@ nextScaffoldPart(XML_Parser parser) {
dtd->scaffIndex = (int *)MALLOC(parser, parser->m_groupSize * sizeof(int));
if (! dtd->scaffIndex)
return -1;
+ dtd->scaffIndexSize = parser->m_groupSize;
dtd->scaffIndex[0] = 0;
}
From af2f86e32624be829ddc0ce7168d18f921bdef4e Mon Sep 17 00:00:00 2001
From: Matthew Fernandez <matthew.fernandez@gmail.com>
Date: Thu, 4 Jun 2026 17:01:02 -0700
Subject: [PATCH 2/5] lib: doProlog: Fix out-of-bound scaffolding index store
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The scaffold backing array is reallocated using the caller parsers
per-parser `m_groupSize`, but the DTD struct (which carries
`scaffIndex`) is shared between a parent parser and any external
parameter-entity sub-parser created via
`XML_ExternalEntityParserCreate(parent, NULL, …)`. A sub-parser whose
group nesting is shallower than the parents can `REALLOC` the shared
`scaffIndex` down to its own size; when the parent resumes and parses a
deeper element content model, its bounds check passes (its private
`m_groupSize` is still large enough), the doubling-grow path is skipped,
and the next write lands past the shrunken buffer.
Anthropic: ANT-2026-00037
Anthropic: ANT-2026-03621
Anthropic: ANT-2026-03867
Co-authored-by: Alessandro Gario <alessandro.gario@trailofbits.com>
Reported-by: Trail of Bits, in collaboration with Anthropic
---
expat/lib/xmlparse.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/expat/lib/xmlparse.c b/expat/lib/xmlparse.c
index 3b5e90b1..317b08e0 100644
--- a/expat/lib/xmlparse.c
+++ b/expat/lib/xmlparse.c
@@ -5830,6 +5830,21 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
if (myindex < 0)
return XML_ERROR_NO_MEMORY;
assert(dtd->scaffIndex != NULL);
+ if ((size_t)dtd->scaffLevel >= dtd->scaffIndexSize) {
+ /* Detect and prevent integer overflow */
+ if (dtd->scaffIndexSize > SIZE_MAX / 2 / sizeof(int)) {
+ return XML_ERROR_NO_MEMORY;
+ }
+ assert(dtd->scaffIndexSize > 0);
+ const size_t new_size = dtd->scaffIndexSize * 2;
+ int *const new_scaff_index
+ = REALLOC(parser, dtd->scaffIndex, new_size * sizeof(int));
+ if (new_scaff_index == NULL) {
+ return XML_ERROR_NO_MEMORY;
+ }
+ dtd->scaffIndex = new_scaff_index;
+ dtd->scaffIndexSize = new_size;
+ }
dtd->scaffIndex[dtd->scaffLevel] = myindex;
dtd->scaffLevel++;
dtd->scaffold[myindex].type = XML_CTYPE_SEQ;
From 8b1debf703fadb5e11074f98f33a8bd10b561f3e Mon Sep 17 00:00:00 2001
From: Matthew Fernandez <matthew.fernandez@gmail.com>
Date: Thu, 4 Jun 2026 17:01:02 -0700
Subject: [PATCH 3/5] tests: Add a test case for scaffolding array limits in
shared DTDs
This test case provokes the bug fixed in the previous commit.
Anthropic: ANT-2026-00037
Anthropic: ANT-2026-03621
Anthropic: ANT-2026-03867
Co-authored-by: Alessandro Gario <alessandro.gario@trailofbits.com>
Reported-by: Trail of Bits, in collaboration with Anthropic
---
expat/tests/runtests.c | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/expat/tests/runtests.c b/expat/tests/runtests.c
index 659ed030..c760733d 100644
--- a/expat/tests/runtests.c
+++ b/expat/tests/runtests.c
@@ -5681,6 +5681,34 @@ START_TEST(test_skipped_external_entity) {
}
END_TEST
+START_TEST(test_scaff_index_shared_across_external_entity_parser) {
+ const char text[]
+ = "<!DOCTYPE doc [\n"
+ "<!ELEMENT a "
+ "((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((b))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))>\n"
+ "<!ENTITY % e SYSTEM 'ext'>\n"
+ "%e;\n"
+ "<!ELEMENT c "
+ "(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((d)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))>\n"
+ "]>\n"
+ "<doc/>";
+ ExtOption options[]
+ = {{XCS("ext"),
+ "<!ELEMENT x "
+ "((((((((((((((((((((((((((((((((y))))))))))))))))))))))))))))))))>"},
+ {NULL, NULL}};
+
+ XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+ XML_SetUserData(g_parser, options);
+ XML_SetExternalEntityRefHandler(g_parser, external_entity_optioner);
+ XML_SetElementDeclHandler(g_parser, dummy_element_decl_handler);
+
+ if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+ == XML_STATUS_ERROR)
+ xml_failure(g_parser);
+}
+END_TEST
+
/* Test a different form of unknown external entity */
typedef struct ext_hdlr_data {
const char *parse_text;
@@ -13988,6 +14016,8 @@ make_suite(void) {
tcase_add_test(tc_basic, test_trailing_cr_in_att_value);
tcase_add_test(tc_basic, test_standalone_internal_entity);
tcase_add_test(tc_basic, test_skipped_external_entity);
+ tcase_add_test__ifdef_xml_dtd(
+ tc_basic, test_scaff_index_shared_across_external_entity_parser);
tcase_add_test(tc_basic, test_skipped_null_loaded_ext_entity);
tcase_add_test(tc_basic, test_skipped_unloaded_ext_entity);
tcase_add_test__ifdef_xml_dtd(tc_basic, test_param_entity_with_trailing_cr);
From 4ab78126be566310e293adef807a9b919e90f8df Mon Sep 17 00:00:00 2001
From: Matthew Fernandez <matthew.fernandez@gmail.com>
Date: Thu, 4 Jun 2026 17:01:02 -0700
Subject: [PATCH 4/5] lib: Remove unnecessary `scaffIndex` expansion
Following the previous changes, all locations that append entries to
`scaffIndex` handle expanding the array if it is not already large
enough. So this extra expansion code is no longer necessary. In some
cases such as processing siblings with alternating scaffolding counts,
this logic would actually _shrink_ the array only to then later
re-expand it.
Anthropic: ANT-2026-00037
Anthropic: ANT-2026-03621
Anthropic: ANT-2026-03867
Co-authored-by: Alessandro Gario <alessandro.gario@trailofbits.com>
---
expat/lib/xmlparse.c | 18 ------------------
1 file changed, 18 deletions(-)
diff --git a/expat/lib/xmlparse.c b/expat/lib/xmlparse.c
index 317b08e0..80fcb968 100644
--- a/expat/lib/xmlparse.c
+++ b/expat/lib/xmlparse.c
@@ -5797,24 +5797,6 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
parser->m_groupConnector = new_connector;
}
- if (dtd->scaffIndex) {
- /* Detect and prevent integer overflow.
- * The preprocessor guard addresses the "always false" warning
- * from -Wtype-limits on platforms where
- * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
-#if UINT_MAX >= SIZE_MAX
- if (parser->m_groupSize > (size_t)(-1) / sizeof(int)) {
- return XML_ERROR_NO_MEMORY;
- }
-#endif
-
- int *const new_scaff_index = (int *)REALLOC(
- parser, dtd->scaffIndex, parser->m_groupSize * sizeof(int));
- if (new_scaff_index == NULL)
- return XML_ERROR_NO_MEMORY;
- dtd->scaffIndex = new_scaff_index;
- dtd->scaffIndexSize = parser->m_groupSize;
- }
} else {
parser->m_groupConnector
= (char *)MALLOC(parser, parser->m_groupSize = 32);
From 9a4202e63b9a89e2e1ec14996934ff98697c6721 Mon Sep 17 00:00:00 2001
From: Matthew Fernandez <matthew.fernandez@gmail.com>
Date: Thu, 4 Jun 2026 17:01:02 -0700
Subject: [PATCH 5/5] lib: Remove indented scoping of `new_connector` local
Following the previous change, the lifetime of `new_connector` as
constrained by this introduced scope was identical to the parent scope.
---
expat/lib/xmlparse.c | 23 ++++++++++-------------
1 file changed, 10 insertions(+), 13 deletions(-)
diff --git a/expat/lib/xmlparse.c b/expat/lib/xmlparse.c
index 80fcb968..5c255c00 100644
--- a/expat/lib/xmlparse.c
+++ b/expat/lib/xmlparse.c
@@ -5782,21 +5782,18 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
case XML_ROLE_GROUP_OPEN:
if (parser->m_prologState.level >= parser->m_groupSize) {
if (parser->m_groupSize) {
- {
- /* Detect and prevent integer overflow */
- if (parser->m_groupSize > (unsigned int)(-1) / 2u) {
- return XML_ERROR_NO_MEMORY;
- }
-
- char *const new_connector = (char *)REALLOC(
- parser, parser->m_groupConnector, parser->m_groupSize *= 2);
- if (new_connector == NULL) {
- parser->m_groupSize /= 2;
- return XML_ERROR_NO_MEMORY;
- }
- parser->m_groupConnector = new_connector;
+ /* Detect and prevent integer overflow */
+ if (parser->m_groupSize > (unsigned int)(-1) / 2u) {
+ return XML_ERROR_NO_MEMORY;
}
+ char *const new_connector = (char *)REALLOC(
+ parser, parser->m_groupConnector, parser->m_groupSize *= 2);
+ if (new_connector == NULL) {
+ parser->m_groupSize /= 2;
+ return XML_ERROR_NO_MEMORY;
+ }
+ parser->m_groupConnector = new_connector;
} else {
parser->m_groupConnector
= (char *)MALLOC(parser, parser->m_groupSize = 32);

View File

@ -1,39 +1,27 @@
%global unversion 2_5_0 %global unversion 2_2_5
Summary: An XML parser library Summary: An XML parser library
Name: expat Name: expat
Version: %(echo %{unversion} | sed 's/_/./g') Version: %(echo %{unversion} | sed 's/_/./g')
Release: 2%{?dist}.2 Release: 11%{?dist}
Source: https://github.com/libexpat/libexpat/archive/R_%{unversion}.tar.gz#/expat-%{version}.tar.gz Source: https://github.com/libexpat/libexpat/archive/R_%{unversion}.tar.gz#/expat-%{version}.tar.gz
URL: https://libexpat.github.io/ URL: https://libexpat.github.io/
License: MIT License: MIT
BuildRequires: autoconf, libtool, xmlto, gcc-c++ BuildRequires: autoconf, libtool, xmlto, gcc-c++
BuildRequires: make Patch0: expat-2.2.5-doc2man.patch
# https://issues.redhat.com/browse/RHEL-24227 Patch1: expat-2.2.5-CVE-2018-20843.patch
Patch0: expat-2.5.0-CVE-2023-52425.patch Patch2: expat-2.2.5-CVE-2019-15903.patch
# https://issues.redhat.com/browse/RHEL-28700 Patch3: expat-2.2.5-Detect-and-prevent-integer-overflow-in-XML_GetBuffer.patch
Patch1: expat-2.5.0-CVE-2024-28757.patch Patch4: expat-2.2.5-Detect-and-prevent-troublesome-left-shifts.patch
# https://issues.redhat.com/browse/RHEL-56761 Patch5: expat-2.2.5-Prevent-integer-overflow-on-m_groupSize-in-function.patch
Patch2: expat-2.5.0-CVE-2024-45490.patch Patch6: expat-2.2.5-Prevent-more-integer-overflows.patch
# https://issues.redhat.com/browse/RHEL-57520 Patch7: expat-2.2.5-Protect-against-malicious-namespace-declarations.patch
Patch3: expat-2.5.0-CVE-2024-45491.patch Patch8: expat-2.2.5-Add-missing-validation-of-encoding.patch
# https://issues.redhat.com/browse/RHEL-57511 Patch9: expat-2.2.5-Prevent-integer-overflow-in-storeRawNames.patch
Patch4: expat-2.5.0-CVE-2024-45492.patch Patch10: expat-2.2.5-Prevent-integer-overflow-in-copyString.patch
# https://issues.redhat.com/browse/RHEL-65066 Patch11: expat-2.2.5-Prevent-stack-exhaustion-in-build_model.patch
Patch5: expat-2.5.0-CVE-2024-50602.patch Patch12: expat-2.2.5-Ensure-raw-tagnames-are-safe-exiting-internalEntityParser.patch
# https://issues.redhat.com/browse/RHEL-57489 Patch13: expat-2.2.5-CVE-2022-43680.patch
Patch6: expat-2.5.0-CVE-2024-8176.patch
# https://issues.redhat.com/browse/RHEL-114618
Patch7: expat-2.5.0-CVE-2025-59375.patch
# https://issues.redhat.com/browse/RHEL-177979
# https://github.com/libexpat/libexpat/pull/1216
Patch8: expat-2.5.0-CVE-2026-45186.patch
# https://issues.redhat.com/browse/RHEL-220979
# https://github.com/libexpat/libexpat/pull/1246
Patch9: expat-2.5.0-CVE-2026-50219.patch
# https://issues.redhat.com/browse/RHEL-221012
# https://github.com/libexpat/libexpat/pull/1272
Patch10: expat-2.5.0-CVE-2026-56132.patch
%description %description
This is expat, the C library for parsing XML, written by James Clark. Expat This is expat, the C library for parsing XML, written by James Clark. Expat
@ -61,19 +49,20 @@ Install it if you need to link statically with expat.
%prep %prep
%setup -q -n libexpat-R_%{unversion}/expat %setup -q -n libexpat-R_%{unversion}/expat
pushd .. %patch0 -p2 -b .doc2man
%patch0 -p1 -b .CVE-2023-52425 %patch1 -p2 -b .cve20843
%patch1 -p1 -b .CVE-2024-28757 %patch2 -p2 -b .cve15903
%patch2 -p1 -b .CVE-2024-45490 %patch3 -p1 -b .CVE-2022-23852
%patch3 -p1 -b .CVE-2024-45491 %patch4 -p1 -b .CVE-2021-45960
%patch4 -p1 -b .CVE-2024-45492 %patch5 -p1 -b .CVE-2021-46143
%patch5 -p1 -b .CVE-2024-50602 %patch6 -p1 -b .CVE-2022-22822-CVE-2022-22827
%patch6 -p1 -b .CVE-2024-8176 %patch7 -p1 -b .CVE-2022-25236
%patch7 -p1 -b .CVE-2025-59375 %patch8 -p1 -b .CVE-2022-25235
%patch8 -p1 -b .CVE-2026-45186 %patch9 -p1 -b .CVE-2022-25315
%patch9 -p1 -b .CVE-2026-50219 %patch10 -p1 -b .CVE-2022-25314
%patch10 -p1 -b .CVE-2026-56132 %patch11 -p1 -b .CVE-2022-25313
popd %patch12 -p1 -b .CVE-2022-40674
%patch13 -p1 -b .CVE-2022-43680
sed -i 's/install-data-hook/do-nothing-please/' lib/Makefile.am sed -i 's/install-data-hook/do-nothing-please/' lib/Makefile.am
./buildconf.sh ./buildconf.sh
@ -90,15 +79,6 @@ make install DESTDIR=$RPM_BUILD_ROOT
rm -f $RPM_BUILD_ROOT%{_libdir}/*.la rm -f $RPM_BUILD_ROOT%{_libdir}/*.la
%check %check
bash -c "for i in {1..500000}; do printf AAAAAAAAAAAAAAAAAAAA >> achars.txt; done"
for testfile in ../testdata/largefiles/aaaaaa_*; do
first_part="$(sed 's/\(.*\)ACHARS.*/\1/g' $testfile)"
second_part="$(sed 's/.*ACHARS\(.*\)/\1/g' $testfile)"
printf "$first_part" > "$testfile"
cat achars.txt >> "$testfile"
printf "$second_part" >> "$testfile"
done
make check make check
%ldconfig_scriptlets %ldconfig_scriptlets
@ -112,61 +92,15 @@ make check
%{_mandir}/*/* %{_mandir}/*/*
%files devel %files devel
%doc doc/reference.html doc/*.css examples/*.c %doc doc/reference.html doc/*.png doc/*.css examples/*.c
%{_libdir}/lib*.so %{_libdir}/lib*.so
%{_libdir}/pkgconfig/*.pc %{_libdir}/pkgconfig/*.pc
%{_includedir}/*.h %{_includedir}/*.h
%{_libdir}/cmake/expat-%{version}
%files static %files static
%{_libdir}/lib*.a %{_libdir}/lib*.a
%changelog %changelog
* Fri Jul 31 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 2.5.0-2.2
- Fix CVE-2026-56132: out-of-bound scaffolding index store in doProlog
- Resolves: RHEL-221012
* Fri Jul 31 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 2.5.0-2.1
- Fix CVE-2026-50219
- Resolves: RHEL-220979
* Thu May 28 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 2.5.0-2
- Fix CVE-2026-45186
- Resolves: RHEL-177979
* Wed Nov 19 2025 Tomas Korbar <tkorbar@redhat.com> - 2.5.0-1
- Rebase to version 2.5.0
- Fix CVE-2025-59375
- Resolves: RHEL-114618
* Mon Apr 07 2025 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-17
- Fix CVE-2024-8176
- Resolves: RHEL-57477
* Fri Nov 08 2024 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-16
- Fix CVE-2024-50602
- Resolves: RHEL-65062
* Wed Sep 11 2024 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-15
- Rebuild for test reconfiguration
* Wed Sep 11 2024 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-14
- Fix multiple CVEs
- Fix CVE-2024-45492 integer overflow
- Fix CVE-2024-45491 Integer Overflow or Wraparound
- Fix CVE-2024-45490 Negative Length Parsing Vulnerability
- Resolves: RHEL-57505
- Resolves: RHEL-57493
- Resolves: RHEL-56751
* Tue Mar 26 2024 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-13
- Fix wrongly exposed variables
- Resolves: RHEL-29321
* Thu Mar 21 2024 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-12
- CVE-2023-52425 expat: parsing large tokens can trigger a denial of service
- Resolves: RHEL-29321
* Mon Nov 14 2022 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-11 * Mon Nov 14 2022 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-11
- CVE-2022-43680 expat: use-after free caused by overeager destruction of a shared DTD in XML_ExternalEntityParserCreate - CVE-2022-43680 expat: use-after free caused by overeager destruction of a shared DTD in XML_ExternalEntityParserCreate
- Resolves: CVE-2022-43680 - Resolves: CVE-2022-43680