From ac83027de5a4c0f8aa0e9cfd2f1bcc7559746211 Mon Sep 17 00:00:00 2001 From: Tomas Korbar Date: Wed, 19 Nov 2025 10:34:09 +0100 Subject: [PATCH] Rebase to version 2.5.0 Fix CVE-2025-59375 Resolves: RHEL-114618 --- .gitignore | 1 + ...5-Add-missing-validation-of-encoding.patch | 200 --- expat-2.2.5-CVE-2018-20843.patch | 15 - expat-2.2.5-CVE-2019-15903.patch | 171 -- expat-2.2.5-CVE-2022-43680.patch | 89 - expat-2.2.5-CVE-2024-45491.patch | 29 - ...nt-integer-overflow-in-XML_GetBuffer.patch | 183 -- ...-and-prevent-troublesome-left-shifts.patch | 54 - ...re-safe-exiting-internalEntityParser.patch | 118 -- ...event-integer-overflow-in-copyString.patch | 19 - ...nt-integer-overflow-in-storeRawNames.patch | 31 - ...-overflow-on-m_groupSize-in-function.patch | 38 - ...2.2.5-Prevent-more-integer-overflows.patch | 238 --- ...vent-stack-exhaustion-in-build_model.patch | 228 --- ...nst-malicious-namespace-declarations.patch | 229 --- expat-2.2.5-doc2man.patch | 26 - ....patch => expat-2.5.0-CVE-2023-52425.patch | 562 +++--- expat-2.5.0-CVE-2024-28757.patch | 172 ++ ....patch => expat-2.5.0-CVE-2024-45490.patch | 38 +- expat-2.5.0-CVE-2024-45491.patch | 31 + ....patch => expat-2.5.0-CVE-2024-45492.patch | 22 +- ....patch => expat-2.5.0-CVE-2024-50602.patch | 66 +- ...6.patch => expat-2.5.0-CVE-2024-8176.patch | 1050 ++++++------ expat-2.5.0-CVE-2025-59375.patch | 1526 +++++++++++++++++ expat.spec | 77 +- sources | 2 +- 26 files changed, 2618 insertions(+), 2597 deletions(-) delete mode 100644 expat-2.2.5-Add-missing-validation-of-encoding.patch delete mode 100644 expat-2.2.5-CVE-2018-20843.patch delete mode 100644 expat-2.2.5-CVE-2019-15903.patch delete mode 100644 expat-2.2.5-CVE-2022-43680.patch delete mode 100644 expat-2.2.5-CVE-2024-45491.patch delete mode 100644 expat-2.2.5-Detect-and-prevent-integer-overflow-in-XML_GetBuffer.patch delete mode 100644 expat-2.2.5-Detect-and-prevent-troublesome-left-shifts.patch delete mode 100644 expat-2.2.5-Ensure-raw-tagnames-are-safe-exiting-internalEntityParser.patch delete mode 100644 expat-2.2.5-Prevent-integer-overflow-in-copyString.patch delete mode 100644 expat-2.2.5-Prevent-integer-overflow-in-storeRawNames.patch delete mode 100644 expat-2.2.5-Prevent-integer-overflow-on-m_groupSize-in-function.patch delete mode 100644 expat-2.2.5-Prevent-more-integer-overflows.patch delete mode 100644 expat-2.2.5-Prevent-stack-exhaustion-in-build_model.patch delete mode 100644 expat-2.2.5-Protect-against-malicious-namespace-declarations.patch delete mode 100644 expat-2.2.5-doc2man.patch rename expat-2.2.5-CVE-2023-52425.patch => expat-2.5.0-CVE-2023-52425.patch (75%) create mode 100644 expat-2.5.0-CVE-2024-28757.patch rename expat-2.2.5-CVE-2024-45490.patch => expat-2.5.0-CVE-2024-45490.patch (78%) create mode 100644 expat-2.5.0-CVE-2024-45491.patch rename expat-2.2.5-CVE-2024-45492.patch => expat-2.5.0-CVE-2024-45492.patch (54%) rename expat-2.2.5-CVE-2024-50602.patch => expat-2.5.0-CVE-2024-50602.patch (57%) rename expat-2.2.5-CVE-2024-8176.patch => expat-2.5.0-CVE-2024-8176.patch (60%) create mode 100644 expat-2.5.0-CVE-2025-59375.patch diff --git a/.gitignore b/.gitignore index 6b6e15c..7e115b3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ SOURCES/expat-2.2.5.tar.gz /expat-2.2.5.tar.gz +/expat-2.5.0.tar.gz diff --git a/expat-2.2.5-Add-missing-validation-of-encoding.patch b/expat-2.2.5-Add-missing-validation-of-encoding.patch deleted file mode 100644 index 8876367..0000000 --- a/expat-2.2.5-Add-missing-validation-of-encoding.patch +++ /dev/null @@ -1,200 +0,0 @@ -commit e8f285b522a907603501329e5b4212755f525fdf -Author: Tomas Korbar -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>a'>]>&e;\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" -+ "/>" -+ ""; -+ 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); diff --git a/expat-2.2.5-Prevent-integer-overflow-in-copyString.patch b/expat-2.2.5-Prevent-integer-overflow-in-copyString.patch deleted file mode 100644 index f67a898..0000000 --- a/expat-2.2.5-Prevent-integer-overflow-in-copyString.patch +++ /dev/null @@ -1,19 +0,0 @@ -commit e5b609876e5a266725fba1c377b0ac95c737e6ed -Author: Tomas Korbar -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 */ diff --git a/expat-2.2.5-Prevent-integer-overflow-in-storeRawNames.patch b/expat-2.2.5-Prevent-integer-overflow-in-storeRawNames.patch deleted file mode 100644 index be5927b..0000000 --- a/expat-2.2.5-Prevent-integer-overflow-in-storeRawNames.patch +++ /dev/null @@ -1,31 +0,0 @@ -commit 3a4141add108097fa548b196f5950c6663e1578e -Author: Tomas Korbar -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) diff --git a/expat-2.2.5-Prevent-integer-overflow-on-m_groupSize-in-function.patch b/expat-2.2.5-Prevent-integer-overflow-on-m_groupSize-in-function.patch deleted file mode 100644 index f7e76e6..0000000 --- a/expat-2.2.5-Prevent-integer-overflow-on-m_groupSize-in-function.patch +++ /dev/null @@ -1,38 +0,0 @@ -commit 835df27bc1a1eae1ec51b14122ea40c974dd7409 -Author: Tomas Korbar -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) diff --git a/expat-2.2.5-Prevent-more-integer-overflows.patch b/expat-2.2.5-Prevent-more-integer-overflows.patch deleted file mode 100644 index 45e3f8d..0000000 --- a/expat-2.2.5-Prevent-more-integer-overflows.patch +++ /dev/null @@ -1,238 +0,0 @@ -commit 0f920007dc157e052fed2fc66a83c6c23ccec0aa -Author: Tomas Korbar -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) diff --git a/expat-2.2.5-Prevent-stack-exhaustion-in-build_model.patch b/expat-2.2.5-Prevent-stack-exhaustion-in-build_model.patch deleted file mode 100644 index 52af442..0000000 --- a/expat-2.2.5-Prevent-stack-exhaustion-in-build_model.patch +++ /dev/null @@ -1,228 +0,0 @@ -commit f1b61e6fbaedbb2bbea736269a015d97d4df46ce -Author: Tomas Korbar -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 = "\n" -+ "]>\n" -+ ""; -+ -+ 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); diff --git a/expat-2.2.5-Protect-against-malicious-namespace-declarations.patch b/expat-2.2.5-Protect-against-malicious-namespace-declarations.patch deleted file mode 100644 index acd46fb..0000000 --- a/expat-2.2.5-Protect-against-malicious-namespace-declarations.patch +++ /dev/null @@ -1,229 +0,0 @@ -commit fd5473ef5873048eadef344a1f16f71ad8eefe99 -Author: Tomas Korbar -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, "", XCS('\n')}, -+ {XML_STATUS_ERROR, "", XCS('\n')}, -+ {XML_STATUS_OK, "", 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); diff --git a/expat-2.2.5-doc2man.patch b/expat-2.2.5-doc2man.patch deleted file mode 100644 index 59c7136..0000000 --- a/expat-2.2.5-doc2man.patch +++ /dev/null @@ -1,26 +0,0 @@ -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 diff --git a/expat-2.2.5-CVE-2023-52425.patch b/expat-2.5.0-CVE-2023-52425.patch similarity index 75% rename from expat-2.2.5-CVE-2023-52425.patch rename to expat-2.5.0-CVE-2023-52425.patch index 6e64eca..f561eaf 100644 --- a/expat-2.2.5-CVE-2023-52425.patch +++ b/expat-2.5.0-CVE-2023-52425.patch @@ -1,16 +1,14 @@ -commit 5948204b65267ef0c16c2181e4bd92c0ef50075b +commit 678a2f7efcaaa977886e055613f2332615aef82c Author: Tomas Korbar -Date: Tue Mar 26 10:34:14 2024 +0100 +Date: Tue Feb 13 13:52:28 2024 +0100 Fix CVE-2023-52425 - - upstream PR #789 diff --git a/expat/Makefile.am b/expat/Makefile.am -index 5ed9ac4..0e3181d 100644 +index 37ae373..cd0117f 100644 --- a/expat/Makefile.am +++ b/expat/Makefile.am -@@ -120,6 +120,11 @@ buildlib: +@@ -131,6 +131,11 @@ buildlib: run-benchmark: $(MAKE) -C tests/benchmark ./run.sh tests/benchmark/benchmark@EXEEXT@ -n $(top_srcdir)/../testdata/largefiles/recset.xml 65535 3 @@ -20,16 +18,42 @@ index 5ed9ac4..0e3181d 100644 + ./run.sh tests/benchmark/benchmark@EXEEXT@ -n $(top_srcdir)/../testdata/largefiles/aaaaaa_tag.xml 4096 3 + ./run.sh tests/benchmark/benchmark@EXEEXT@ -n $(top_srcdir)/../testdata/largefiles/aaaaaa_text.xml 4096 3 - tests/xmlts.zip: - if test "$(XMLTS_ZIP)" = ""; then \ + .PHONY: download-xmlts-zip + download-xmlts-zip: diff --git a/expat/doc/reference.html b/expat/doc/reference.html -index efc19f4..95c33c7 100644 +index 8b0d47d..a10f3cb 100644 --- a/expat/doc/reference.html +++ b/expat/doc/reference.html -@@ -1996,6 +1996,27 @@ parse position may be before the beginning of the buffer.

+@@ -151,10 +151,11 @@ interface.

+ + +
  • +- Billion Laughs Attack Protection ++ Attack Protection + +
  • +
  • Miscellaneous Functions +@@ -2096,11 +2097,7 @@ parse position may be before the beginning of the buffer.

    return NULL.

    +-

    Billion Laughs Attack Protection

    +- +-

    The functions in this section configure the built-in +- protection against various forms of +- billion laughs attacks.

    ++

    Attack Protection

    + +

    XML_SetBillionLaughsAttackProtectionMaximumAmplification

    +
    +@@ -2188,6 +2185,27 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold(XML_Parser p,
    +   

    + + +

    XML_SetReparseDeferralEnabled

    +
     +/* Added in Expat 2.6.0. */
    @@ -55,10 +79,10 @@ index efc19f4..95c33c7 100644
      
      

    The functions in this section either obtain state information from diff --git a/expat/doc/xmlwf.xml b/expat/doc/xmlwf.xml -index 5e2a4ae..6b719eb 100644 +index 9603abf..3d35393 100644 --- a/expat/doc/xmlwf.xml +++ b/expat/doc/xmlwf.xml -@@ -246,6 +246,16 @@ supports both. +@@ -313,6 +313,16 @@ supports both. @@ -75,57 +99,21 @@ index 5e2a4ae..6b719eb 100644 -diff --git a/expat/lib/.gitignore b/expat/lib/.gitignore -index 9c9cf88..cd5b24f 100644 ---- a/expat/lib/.gitignore -+++ b/expat/lib/.gitignore -@@ -1,7 +1,6 @@ - Makefile - .libs - *.lo --expat.h - Debug - Debug-w - Release -diff --git a/expat/lib/Makefile.am b/expat/lib/Makefile.am -index 5f2b71e..cef6558 100644 ---- a/expat/lib/Makefile.am -+++ b/expat/lib/Makefile.am -@@ -34,18 +34,25 @@ include_HEADERS = \ - expat_external.h - - lib_LTLIBRARIES = libexpat.la -+noinst_LTLIBRARIES = libexpatinternal.la - - libexpat_la_LDFLAGS = \ - -no-undefined \ - -version-info @LIBCURRENT@:@LIBREVISION@:@LIBAGE@ \ - -rpath $(libdir) - --libexpat_la_SOURCES = \ -- loadlibrary.c \ -+libexpat_la_SOURCES = -+ -+# This layer of indirection allows -+# the test suite to access internal symbols -+# despite compiling with -fvisibility=hidden -+libexpatinternal_la_SOURCES = \ - xmlparse.c \ - xmltok.c \ - xmlrole.c - -+libexpat_la_LIBADD = libexpatinternal.la -+ - doc_DATA = \ - ../AUTHORS \ - ../Changes diff --git a/expat/lib/expat.h b/expat/lib/expat.h -index 1f608c0..afe12c5 100644 +index 1c83563..842dd70 100644 --- a/expat/lib/expat.h +++ b/expat/lib/expat.h -@@ -1071,6 +1071,10 @@ XMLPARSEAPI(const XML_Feature *) - XML_GetFeatureList(void); +@@ -16,6 +16,7 @@ + Copyright (c) 2016 Thomas Beutlich + Copyright (c) 2017 Rhodri James + Copyright (c) 2022 Thijs Schreijer ++ Copyright (c) 2023 Sony Corporation / Snild Dolkow + Licensed under the MIT license: + Permission is hereby granted, free of charge, to any person obtaining +@@ -1050,6 +1051,10 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold( + XML_Parser parser, unsigned long long activationThresholdBytes); + #endif +/* Added in Expat 2.6.0. */ +XMLPARSEAPI(XML_Bool) @@ -135,63 +123,60 @@ index 1f608c0..afe12c5 100644 See http://semver.org. */ diff --git a/expat/lib/internal.h b/expat/lib/internal.h -index e33fdcb..8e3d566 100644 +index e09f533..e2709c8 100644 --- a/expat/lib/internal.h +++ b/expat/lib/internal.h -@@ -109,6 +109,7 @@ - # endif +@@ -31,6 +31,7 @@ + Copyright (c) 2016-2022 Sebastian Pipping + Copyright (c) 2018 Yury Gribov + Copyright (c) 2019 David Loffredo ++ Copyright (c) 2023 Sony Corporation / Snild Dolkow + Licensed under the MIT license: + + Permission is hereby granted, free of charge, to any person obtaining +@@ -160,6 +161,9 @@ unsigned long long testingAccountingGetCountBytesIndirect(XML_Parser parser); + const char *unsignedCharToPrintable(unsigned char c); #endif -+#include "expat.h" - - #ifdef __cplusplus - extern "C" { -@@ -119,6 +120,9 @@ void - _INTERNAL_trim_to_complete_utf8_characters(const char * from, const char ** fromLimRef); - - -+extern __attribute__ ((visibility ("hidden"))) XML_Bool g_reparseDeferralEnabledDefault; // written ONLY in runtests.c -+extern __attribute__ ((visibility ("hidden"))) unsigned int g_parseAttempts; // used for testing only ++extern XML_Bool g_reparseDeferralEnabledDefault; // written ONLY in runtests.c ++extern unsigned int g_parseAttempts; // used for testing only + #ifdef __cplusplus } #endif -diff --git a/expat/lib/libexpat.def b/expat/lib/libexpat.def -index d08f5b7..163870b 100644 ---- a/expat/lib/libexpat.def -+++ b/expat/lib/libexpat.def -@@ -75,4 +75,5 @@ EXPORTS - ; XML_GetAttributeInfo @66 - XML_SetHashSalt @67@ - ; added with version 2.2.5 -- _INTERNAL_trim_to_complete_utf8_characters @68@ -\ No newline at end of file -+ _INTERNAL_trim_to_complete_utf8_characters @68@ -+ XML_SetReparseDeferralEnabled @69 +diff --git a/expat/lib/libexpat.def.cmake b/expat/lib/libexpat.def.cmake +index cf434a2..3ff4d55 100644 +--- a/expat/lib/libexpat.def.cmake ++++ b/expat/lib/libexpat.def.cmake +@@ -77,3 +77,4 @@ EXPORTS + ; added with version 2.4.0 + @_EXPAT_COMMENT_DTD@ XML_SetBillionLaughsAttackProtectionActivationThreshold @69 + @_EXPAT_COMMENT_DTD@ XML_SetBillionLaughsAttackProtectionMaximumAmplification @70 ++XML_SetReparseDeferralEnabled @71 diff --git a/expat/lib/xmlparse.c b/expat/lib/xmlparse.c -index 3f765f7..488f63f 100644 +index b6c2eca..2ae64e9 100644 --- a/expat/lib/xmlparse.c +++ b/expat/lib/xmlparse.c -@@ -34,6 +34,7 @@ - # define _GNU_SOURCE 1 /* syscall prototype */ +@@ -73,6 +73,7 @@ + # endif #endif +#include #include - #include /* memset(), memcpy() */ + #include /* memset(), memcpy() */ #include -@@ -173,6 +174,8 @@ typedef char ICHAR; - #endif /* HAVE_BCOPY */ - #endif /* HAVE_MEMMOVE */ +@@ -196,6 +197,8 @@ typedef char ICHAR; + /* Do safe (NULL-aware) pointer arithmetic */ + #define EXPAT_SAFE_PTR_DIFF(p, q) (((p) && (q)) ? ((p) - (q)) : 0) +#define EXPAT_MIN(a, b) (((a) < (b)) ? (a) : (b)) + #include "internal.h" #include "xmltok.h" #include "xmlrole.h" -@@ -544,6 +547,9 @@ parserInit(XML_Parser parser, const XML_Char *encodingName); - ? 0 \ - : ((*((pool)->ptr)++ = c), 1)) +@@ -602,6 +605,9 @@ static unsigned long getDebugLevel(const char *variableName, + ? 0 \ + : ((*((pool)->ptr)++ = c), 1)) +XML_Bool g_reparseDeferralEnabledDefault = XML_TRUE; // write ONLY in runtests.c +unsigned int g_parseAttempts = 0; // used for testing only @@ -199,7 +184,7 @@ index 3f765f7..488f63f 100644 struct XML_ParserStruct { /* The first member must be m_userData so that the XML_GetUserData macro works. */ -@@ -559,6 +565,9 @@ struct XML_ParserStruct { +@@ -617,6 +623,9 @@ struct XML_ParserStruct { const char *m_bufferLim; XML_Index m_parseEndByteIndex; const char *m_parseEndPtr; @@ -209,7 +194,7 @@ index 3f765f7..488f63f 100644 XML_Char *m_dataBuf; XML_Char *m_dataBufEnd; XML_StartElementHandler m_startElementHandler; -@@ -892,6 +901,48 @@ get_hash_secret_salt(XML_Parser parser) { +@@ -948,6 +957,47 @@ get_hash_secret_salt(XML_Parser parser) { return parser->m_hash_secret_salt; } @@ -226,7 +211,7 @@ index 3f765f7..488f63f 100644 + // ...but *do* try anyway if we're close to causing a reallocation. + size_t available_buffer + = EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer); -+#ifdef XML_CONTEXT_BYTES ++#if XML_CONTEXT_BYTES > 0 + available_buffer -= EXPAT_MIN(available_buffer, XML_CONTEXT_BYTES); +#endif + available_buffer @@ -254,11 +239,10 @@ index 3f765f7..488f63f 100644 + return ret; +} + -+ - static XML_Bool /* only valid for root parser */ - startParsing(XML_Parser parser) - { -@@ -1078,6 +1129,9 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) + static XML_Bool /* only valid for root parser */ + startParsing(XML_Parser parser) { + /* hash functions must be initialized before setContext() is called */ +@@ -1129,6 +1179,9 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) { parser->m_bufferEnd = parser->m_buffer; parser->m_parseEndByteIndex = 0; parser->m_parseEndPtr = NULL; @@ -268,7 +252,7 @@ index 3f765f7..488f63f 100644 parser->m_declElementType = NULL; parser->m_declAttributeId = NULL; parser->m_declEntity = NULL; -@@ -1239,6 +1293,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, +@@ -1298,6 +1351,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context, to worry which hash secrets each table has. */ unsigned long oldhash_secret_salt; @@ -276,15 +260,15 @@ index 3f765f7..488f63f 100644 /* Validate the oldParser parameter before we pull everything out of it */ if (oldParser == NULL) -@@ -1283,6 +1338,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, +@@ -1342,6 +1396,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context, to worry which hash secrets each table has. */ oldhash_secret_salt = parser->m_hash_secret_salt; + oldReparseDeferralEnabled = parser->m_reparseDeferralEnabled; #ifdef XML_DTD - if (!context) -@@ -1336,6 +1392,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, + if (! context) +@@ -1394,6 +1449,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context, parser->m_defaultExpandInternalEntities = oldDefaultExpandInternalEntities; parser->m_ns_triplets = oldns_triplets; parser->m_hash_secret_salt = oldhash_secret_salt; @@ -292,13 +276,13 @@ index 3f765f7..488f63f 100644 parser->m_parentParser = oldParser; #ifdef XML_DTD parser->m_paramEntityParsing = oldParamEntityParsing; -@@ -1833,52 +1890,8 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) +@@ -1848,55 +1904,8 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) { parser->m_parsingStatus.parsing = XML_PARSING; } - if (len == 0) { - parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal; -- if (!isFinal) +- if (! isFinal) - return XML_STATUS_OK; - parser->m_positionPtr = parser->m_bufferPtr; - parser->m_parseEndPtr = parser->m_bufferEnd; @@ -307,7 +291,9 @@ index 3f765f7..488f63f 100644 - data are the final chunk of input, then we have to check them again - to detect errors based on that fact. - */ -- parser->m_errorCode = parser->m_processor(parser, parser->m_bufferPtr, parser->m_parseEndPtr, &parser->m_bufferPtr); +- parser->m_errorCode +- = parser->m_processor(parser, parser->m_bufferPtr, +- parser->m_parseEndPtr, &parser->m_bufferPtr); - - if (parser->m_errorCode == XML_ERROR_NONE) { - switch (parser->m_parsingStatus.parsing) { @@ -324,7 +310,8 @@ index 3f765f7..488f63f 100644 - * - * LCOV_EXCL_START - */ -- XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr, parser->m_bufferPtr, &parser->m_position); +- XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr, +- parser->m_bufferPtr, &parser->m_position); - parser->m_positionPtr = parser->m_bufferPtr; - return XML_STATUS_SUSPENDED; - /* LCOV_EXCL_STOP */ @@ -346,20 +333,29 @@ index 3f765f7..488f63f 100644 const char *end; int nLeftOver; enum XML_Status result; -@@ -1893,7 +1906,7 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) +@@ -1907,12 +1916,15 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) { + parser->m_processor = errorProcessor; + return XML_STATUS_ERROR; + } ++ // though this isn't a buffer request, we assume that `len` is the app's ++ // preferred buffer fill size, and therefore save it here. ++ parser->m_lastBufferRequestSize = len; + parser->m_parseEndByteIndex += len; parser->m_positionPtr = s; parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal; -- parser->m_errorCode = parser->m_processor(parser, s, parser->m_parseEndPtr = s + len, &end); -+ parser->m_errorCode = callProcessor(parser, s, parser->m_parseEndPtr = s + len, &end); + parser->m_errorCode +- = parser->m_processor(parser, s, parser->m_parseEndPtr = s + len, &end); ++ = callProcessor(parser, s, parser->m_parseEndPtr = s + len, &end); if (parser->m_errorCode != XML_ERROR_NONE) { parser->m_eventEndPtr = parser->m_eventPtr; -@@ -1920,22 +1933,25 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) - XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr, end, &parser->m_position); +@@ -1939,23 +1951,25 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) { + &parser->m_position); nLeftOver = s + len - end; if (nLeftOver) { -- if (parser->m_buffer == NULL || nLeftOver > parser->m_bufferLim - parser->m_buffer) { +- if (parser->m_buffer == NULL +- || nLeftOver > parser->m_bufferLim - parser->m_buffer) { - /* avoid _signed_ integer overflow */ - char *temp = NULL; - const int bytesToAllocate = (int)((unsigned)len * 2U); @@ -396,10 +392,10 @@ index 3f765f7..488f63f 100644 memcpy(parser->m_buffer, end, nLeftOver); } parser->m_bufferPtr = parser->m_buffer; -@@ -1947,15 +1963,14 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) +@@ -1967,15 +1981,14 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) { return result; } - #endif /* not defined XML_CONTEXT_BYTES */ + #endif /* not defined XML_CONTEXT_BYTES */ - else { - void *buff = XML_GetBuffer(parser, len); - if (buff == NULL) @@ -419,45 +415,61 @@ index 3f765f7..488f63f 100644 } enum XML_Status XMLCALL -@@ -1989,7 +2004,8 @@ XML_ParseBuffer(XML_Parser parser, int len, int isFinal) +@@ -2015,8 +2028,8 @@ XML_ParseBuffer(XML_Parser parser, int len, int isFinal) { parser->m_parseEndByteIndex += len; parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal; -- parser->m_errorCode = parser->m_processor(parser, start, parser->m_parseEndPtr, &parser->m_bufferPtr); +- parser->m_errorCode = parser->m_processor( +- parser, start, parser->m_parseEndPtr, &parser->m_bufferPtr); + parser->m_errorCode = callProcessor(parser, start, parser->m_parseEndPtr, + &parser->m_bufferPtr); if (parser->m_errorCode != XML_ERROR_NONE) { parser->m_eventEndPtr = parser->m_eventPtr; -@@ -2035,8 +2051,14 @@ XML_GetBuffer(XML_Parser parser, int len) - default: ; +@@ -2061,10 +2074,14 @@ XML_GetBuffer(XML_Parser parser, int len) { + default:; } - if (len > EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferEnd)) { +-#ifdef XML_CONTEXT_BYTES + // whether or not the request succeeds, `len` seems to be the app's preferred + // buffer fill size; remember it. + parser->m_lastBufferRequestSize = len; + if (len > EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferEnd) + || parser->m_buffer == NULL) { -+#ifdef XML_CONTEXT_BYTES ++#if XML_CONTEXT_BYTES > 0 int keep; +-#endif /* defined XML_CONTEXT_BYTES */ +#endif /* XML_CONTEXT_BYTES > 0 */ /* Do not invoke signed arithmetic overflow: */ int neededSize = (int)((unsigned)len + (unsigned)EXPAT_SAFE_PTR_DIFF( -@@ -2055,8 +2077,9 @@ XML_GetBuffer(XML_Parser parser, int len) +@@ -2073,7 +2090,7 @@ XML_GetBuffer(XML_Parser parser, int len) { + parser->m_errorCode = XML_ERROR_NO_MEMORY; + return NULL; + } +-#ifdef XML_CONTEXT_BYTES ++#if XML_CONTEXT_BYTES > 0 + keep = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer); + if (keep > XML_CONTEXT_BYTES) + keep = XML_CONTEXT_BYTES; +@@ -2083,10 +2100,11 @@ XML_GetBuffer(XML_Parser parser, int len) { return NULL; } neededSize += keep; +-#endif /* defined XML_CONTEXT_BYTES */ - if (neededSize - <= EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_buffer)) { +-#ifdef XML_CONTEXT_BYTES ++#endif /* XML_CONTEXT_BYTES > 0 */ + if (parser->m_buffer && parser->m_bufferPtr + && neededSize + <= EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_buffer)) { - #ifdef XML_CONTEXT_BYTES ++#if XML_CONTEXT_BYTES > 0 if (keep < EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer)) { int offset -@@ -2070,19 +2093,17 @@ XML_GetBuffer(XML_Parser parser, int len) + = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer) +@@ -2099,19 +2117,17 @@ XML_GetBuffer(XML_Parser parser, int len) { parser->m_bufferPtr -= offset; } #else @@ -485,7 +497,7 @@ index 3f765f7..488f63f 100644 if (bufferSize == 0) bufferSize = INIT_BUFFER_SIZE; do { -@@ -2099,7 +2120,7 @@ XML_GetBuffer(XML_Parser parser, int len) +@@ -2128,7 +2144,7 @@ XML_GetBuffer(XML_Parser parser, int len) { return NULL; } parser->m_bufferLim = newBuf + bufferSize; @@ -494,18 +506,27 @@ index 3f765f7..488f63f 100644 if (parser->m_bufferPtr) { memcpy(newBuf, &parser->m_bufferPtr[-keep], EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr) -@@ -2182,7 +2203,7 @@ XML_ResumeParser(XML_Parser parser) +@@ -2158,7 +2174,7 @@ XML_GetBuffer(XML_Parser parser, int len) { + parser->m_bufferEnd = newBuf; + } + parser->m_bufferPtr = parser->m_buffer = newBuf; +-#endif /* not defined XML_CONTEXT_BYTES */ ++#endif /* XML_CONTEXT_BYTES > 0 */ + } + parser->m_eventPtr = parser->m_eventEndPtr = NULL; + parser->m_positionPtr = NULL; +@@ -2208,7 +2224,7 @@ XML_ResumeParser(XML_Parser parser) { } parser->m_parsingStatus.parsing = XML_PARSING; -- parser->m_errorCode = parser->m_processor(parser, parser->m_bufferPtr, parser->m_parseEndPtr, &parser->m_bufferPtr); -+ parser->m_errorCode = callProcessor(parser, parser->m_bufferPtr, parser->m_parseEndPtr, &parser->m_bufferPtr); +- parser->m_errorCode = parser->m_processor( ++ parser->m_errorCode = callProcessor( + parser, parser->m_bufferPtr, parser->m_parseEndPtr, &parser->m_bufferPtr); if (parser->m_errorCode != XML_ERROR_NONE) { - parser->m_eventEndPtr = parser->m_eventPtr; -@@ -2504,6 +2525,15 @@ XML_GetFeatureList(void) - return features; +@@ -2561,6 +2577,15 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold( } + #endif /* XML_DTD */ +XML_Bool XMLCALL +XML_SetReparseDeferralEnabled(XML_Parser parser, XML_Bool enabled) { @@ -519,7 +540,7 @@ index 3f765f7..488f63f 100644 /* Initially tag->rawName always points into the parse buffer; for those TAG instances opened while the current parse buffer was processed, and not yet closed, we need to store tag->rawName in a more -@@ -4440,16 +4470,17 @@ entityValueInitProcessor(XML_Parser parser, +@@ -4482,15 +4507,15 @@ entityValueInitProcessor(XML_Parser parser, const char *s, const char *end, parser->m_processor = entityValueProcessor; return entityValueProcessor(parser, next, end, nextPtr); } @@ -529,8 +550,6 @@ index 3f765f7..488f63f 100644 - tokens, but not for the BOM - we would rather like to skip it; - then, when this routine is entered the next time, XmlPrologTok will - return XML_TOK_INVALID, since the BOM is still in the buffer -- */ -- else if (tok == XML_TOK_BOM && next == end && !parser->m_parsingStatus.finalBuffer) { + /* XmlPrologTok has now set the encoding based on the BOM it found, and we + must move s and nextPtr forward to consume the BOM. + @@ -538,35 +557,28 @@ index 3f765f7..488f63f 100644 + would leave the BOM in the buffer and return. On the next call to this + function, our XmlPrologTok call would return XML_TOK_INVALID, since it + is not valid to have multiple BOMs. -+ */ + */ +- else if (tok == XML_TOK_BOM && next == end +- && ! parser->m_parsingStatus.finalBuffer) { + else if (tok == XML_TOK_BOM) { + # ifdef XML_DTD + if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, + XML_ACCOUNT_DIRECT)) { +@@ -4500,7 +4525,7 @@ entityValueInitProcessor(XML_Parser parser, const char *s, const char *end, + # endif + *nextPtr = next; - return XML_ERROR_NONE; + s = next; } /* If we get this token, we have the start of what might be a normal tag, but not a declaration (i.e. it doesn't begin with -diff --git a/expat/tests/Makefile.am b/expat/tests/Makefile.am -index 742ed43..4fe0e23 100644 ---- a/expat/tests/Makefile.am -+++ b/expat/tests/Makefile.am -@@ -52,8 +52,8 @@ runtests_SOURCES = \ - runtestspp_SOURCES = \ - runtestspp.cpp - --runtests_LDADD = libruntests.a ../lib/libexpat.la --runtestspp_LDADD = libruntests.a ../lib/libexpat.la -+runtests_LDADD = libruntests.a ../lib/libexpatinternal.la -+runtestspp_LDADD = libruntests.a ../lib/libexpatinternal.la - - EXTRA_DIST = \ - chardata.h \ diff --git a/expat/tests/minicheck.c b/expat/tests/minicheck.c -index be1e37e..6c694a0 100644 +index 1c65748..f383380 100644 --- a/expat/tests/minicheck.c +++ b/expat/tests/minicheck.c -@@ -209,6 +209,21 @@ srunner_run_all(SRunner *runner, int verbosity) - } +@@ -208,6 +208,21 @@ srunner_run_all(SRunner *runner, int verbosity) { + } } +void @@ -585,17 +597,18 @@ index be1e37e..6c694a0 100644 +} + void - _fail_unless(int UNUSED_P(condition), const char *UNUSED_P(file), int UNUSED_P(line), const char *msg) - { + _fail_unless(int condition, const char *file, int line, const char *msg) { + /* Always print the error message so it isn't lost. In this case, diff --git a/expat/tests/minicheck.h b/expat/tests/minicheck.h -index a2f57dd..894895a 100644 +index cc1f835..032b54e 100644 --- a/expat/tests/minicheck.h +++ b/expat/tests/minicheck.h -@@ -60,7 +60,13 @@ extern "C" { - { - #define END_TEST } } +@@ -64,7 +64,14 @@ extern "C" { + } \ + } --#define fail(msg) _fail_unless(0, __FILE__, __LINE__, msg) +-#define fail(msg) _fail_unless(0, __FILE__, __LINE__, msg) ++ +# define fail(msg) _fail(__FILE__, __LINE__, msg) +# define assert_true(cond) \ + do { \ @@ -606,7 +619,7 @@ index a2f57dd..894895a 100644 typedef void (*tcase_setup_function)(void); typedef void (*tcase_teardown_function)(void); -@@ -101,6 +107,11 @@ void _check_set_test_info(char const *function, +@@ -103,6 +110,11 @@ void _check_set_test_info(char const *function, char const *filename, * Prototypes for the actual implementation. */ @@ -619,48 +632,48 @@ index a2f57dd..894895a 100644 Suite *suite_create(const char *name); TCase *tcase_create(const char *name); diff --git a/expat/tests/runtests.c b/expat/tests/runtests.c -index f58f794..486073f 100644 +index 915fa52..941f61d 100644 --- a/expat/tests/runtests.c +++ b/expat/tests/runtests.c -@@ -46,6 +46,7 @@ - #include /* ptrdiff_t */ +@@ -54,6 +54,7 @@ #include #include + #include /* intptr_t uint64_t */ +#include #if ! defined(__cplusplus) - # if defined(_MSC_VER) && (_MSC_VER <= 1700) -@@ -1112,7 +1113,7 @@ START_TEST(test_column_number_after_parse) - const char *text = ""; - XML_Size colno; + # include +@@ -1071,7 +1072,7 @@ START_TEST(test_column_number_after_parse) { + const char *text = ""; + XML_Size colno; -- if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_FALSE) == XML_STATUS_ERROR) -+ if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) - xml_failure(parser); - colno = XML_GetCurrentColumnNumber(parser); - if (colno != 11) { -@@ -2769,7 +2770,7 @@ START_TEST(test_default_current) - if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), - XML_TRUE) == XML_STATUS_ERROR) - xml_failure(parser); -- CharData_CheckXMLChars(&storage, XCS("DCDCDCDCDCDD")); -+ CharData_CheckXMLChars(&storage, XCS("DCDCDCDD")); +- if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_FALSE) ++ if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE) + == XML_STATUS_ERROR) + xml_failure(g_parser); + colno = XML_GetCurrentColumnNumber(g_parser); +@@ -2582,7 +2583,7 @@ START_TEST(test_default_current) { + if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE) + == XML_STATUS_ERROR) + xml_failure(g_parser); +- CharData_CheckXMLChars(&storage, XCS("DCDCDCDCDCDD")); ++ CharData_CheckXMLChars(&storage, XCS("DCDCDCDD")); - /* Again, without the defaulting */ - XML_ParserReset(parser, NULL); -@@ -2780,7 +2781,7 @@ START_TEST(test_default_current) - if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), - XML_TRUE) == XML_STATUS_ERROR) - xml_failure(parser); -- CharData_CheckXMLChars(&storage, XCS("DcccccD")); -+ CharData_CheckXMLChars(&storage, XCS("DcccD")); + /* Again, without the defaulting */ + XML_ParserReset(g_parser, NULL); +@@ -2593,7 +2594,7 @@ START_TEST(test_default_current) { + if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE) + == XML_STATUS_ERROR) + xml_failure(g_parser); +- CharData_CheckXMLChars(&storage, XCS("DcccccD")); ++ CharData_CheckXMLChars(&storage, XCS("DcccD")); - /* Now with an internal entity to complicate matters */ - XML_ParserReset(parser, NULL); -@@ -4216,6 +4217,19 @@ END_TEST + /* Now with an internal entity to complicate matters */ + XML_ParserReset(g_parser, NULL); +@@ -3946,6 +3947,19 @@ START_TEST(test_get_buffer_3_overflow) { + END_TEST #endif // defined(XML_CONTEXT_BYTES) - +START_TEST(test_getbuffer_allocates_on_zero_len) { + for (int first_len = 1; first_len >= 0; first_len--) { + XML_Parser parser = XML_ParserCreate(NULL); @@ -675,9 +688,9 @@ index f58f794..486073f 100644 +END_TEST + /* Test position information macros */ - START_TEST(test_byte_info_at_end) - { -@@ -6747,6 +6761,12 @@ START_TEST(test_utf8_in_start_tags) { + START_TEST(test_byte_info_at_end) { + const char *text = ""; +@@ -6205,6 +6219,12 @@ START_TEST(test_utf8_in_start_tags) { char doc[1024]; size_t failCount = 0; @@ -690,7 +703,7 @@ index f58f794..486073f 100644 for (; i < sizeof(cases) / sizeof(cases[0]); i++) { size_t j = 0; for (; j < sizeof(atNameStart) / sizeof(atNameStart[0]); j++) { -@@ -7352,6 +7372,609 @@ START_TEST(test_empty_element_abort) +@@ -6830,6 +6850,613 @@ START_TEST(test_nested_entity_suspend) { } END_TEST @@ -871,7 +884,8 @@ index f58f794..486073f 100644 +}; + +static void -+element_decl_counter(void *userData, const XML_Char *UNUSED_P(name), XML_Content *model) { ++element_decl_counter(void *userData, const XML_Char *name, XML_Content *model) { ++ UNUSED_P(name); + struct element_decl_data *testdata = (struct element_decl_data *)userData; + testdata->count += 1; + XML_FreeContentModel(testdata->parser, model); @@ -879,8 +893,11 @@ index f58f794..486073f 100644 + +static int +external_inherited_parser(XML_Parser p, const XML_Char *context, -+ const XML_Char *UNUSED_P(base), const XML_Char *UNUSED_P(systemId), -+ const XML_Char *UNUSED_P(publicId)) { ++ const XML_Char *base, const XML_Char *systemId, ++ const XML_Char *publicId) { ++ UNUSED_P(base); ++ UNUSED_P(systemId); ++ UNUSED_P(publicId); + const char *const pre = "\n"; + const char *const start = "\n"; @@ -1300,79 +1317,73 @@ index f58f794..486073f 100644 /* * Namespaces tests. */ -@@ -7435,13 +8058,13 @@ START_TEST(test_return_ns_triplet) - if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), - XML_FALSE) == XML_STATUS_ERROR) - xml_failure(parser); -- if (!triplet_start_flag) -- fail("triplet_start_checker not invoked"); - /* Check that unsetting "return triplets" fails while still parsing */ - XML_SetReturnNSTriplet(parser, XML_FALSE); - if (_XML_Parse_SINGLE_BYTES(parser, epilog, strlen(epilog), - XML_TRUE) == XML_STATUS_ERROR) - xml_failure(parser); -+ if (!triplet_start_flag) -+ fail("triplet_start_checker not invoked"); - if (!triplet_end_flag) - fail("triplet_end_checker not invoked"); - if (dummy_handler_flags != (DUMMY_START_NS_DECL_HANDLER_FLAG | -@@ -12476,6 +13099,7 @@ make_suite(void) +@@ -6902,13 +7529,13 @@ START_TEST(test_return_ns_triplet) { + if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_FALSE) + == XML_STATUS_ERROR) + xml_failure(g_parser); +- if (! triplet_start_flag) +- fail("triplet_start_checker not invoked"); + /* Check that unsetting "return triplets" fails while still parsing */ + XML_SetReturnNSTriplet(g_parser, XML_FALSE); + if (_XML_Parse_SINGLE_BYTES(g_parser, epilog, (int)strlen(epilog), XML_TRUE) + == XML_STATUS_ERROR) + xml_failure(g_parser); ++ if (! triplet_start_flag) ++ fail("triplet_start_checker not invoked"); + if (! triplet_end_flag) + fail("triplet_end_checker not invoked"); + if (dummy_handler_flags +@@ -12219,6 +12846,7 @@ make_suite(void) { #if defined(XML_CONTEXT_BYTES) - tcase_add_test(tc_basic, test_get_buffer_3_overflow); + tcase_add_test(tc_basic, test_get_buffer_3_overflow); #endif -+ tcase_add_test(tc_basic, test_getbuffer_allocates_on_zero_len); - 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); -@@ -12588,6 +13212,14 @@ make_suite(void) - tcase_add_test(tc_basic, test_bad_notation); - tcase_add_test(tc_basic, test_default_doctype_handler); - tcase_add_test(tc_basic, test_empty_element_abort); -+ tcase_add_test(tc_basic, test_big_tokens_take_linear_time); -+ tcase_add_test(tc_basic, test_set_reparse_deferral); -+ tcase_add_test(tc_basic, test_reparse_deferral_is_inherited); -+ tcase_add_test(tc_basic, test_set_reparse_deferral_on_null_parser); -+ tcase_add_test(tc_basic, test_set_reparse_deferral_on_the_fly); -+ tcase_add_test(tc_basic, test_set_bad_reparse_option); -+ tcase_add_test(tc_basic, test_bypass_heuristic_when_close_to_bufsize); -+ tcase_add_test(tc_basic, test_varying_buffer_fills); - - suite_add_tcase(s, tc_namespace); - tcase_add_checked_fixture(tc_namespace, ++ tcase_add_test(tc_basic, test_getbuffer_allocates_on_zero_len); + 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); +@@ -12337,7 +12965,14 @@ make_suite(void) { + tcase_add_test__ifdef_xml_dtd(tc_basic, + test_pool_integrity_with_unfinished_attr); + tcase_add_test(tc_basic, test_nested_entity_suspend); +- ++ tcase_add_test(tc_basic, test_big_tokens_take_linear_time); ++ tcase_add_test(tc_basic, test_set_reparse_deferral); ++ tcase_add_test(tc_basic, test_reparse_deferral_is_inherited); ++ tcase_add_test(tc_basic, test_set_reparse_deferral_on_null_parser); ++ tcase_add_test(tc_basic, test_set_reparse_deferral_on_the_fly); ++ tcase_add_test(tc_basic, test_set_bad_reparse_option); ++ tcase_add_test(tc_basic, test_bypass_heuristic_when_close_to_bufsize); ++ tcase_add_test(tc_basic, test_varying_buffer_fills); + suite_add_tcase(s, tc_namespace); + tcase_add_checked_fixture(tc_namespace, namespace_setup, namespace_teardown); + tcase_add_test(tc_namespace, test_return_ns_triplet); diff --git a/expat/xmlwf/xmlwf.c b/expat/xmlwf/xmlwf.c -index 82d028e..cd26919 100644 +index 471f2a2..7c62919 100644 --- a/expat/xmlwf/xmlwf.c +++ b/expat/xmlwf/xmlwf.c -@@ -35,6 +35,7 @@ - #include - #include - #include -+#include +@@ -914,6 +914,9 @@ usage(const XML_Char *prog, int rc) { + T(" -a FACTOR set maximum tolerated [a]mplification factor (default: 100.0)\n") + T(" -b BYTES set number of output [b]ytes needed to activate (default: 8 MiB)\n") + T("\n") ++ T("reparse deferral:\n") ++ T(" -q disable reparse deferral, and allow [q]uadratic parse runtime with large tokens\n") ++ T("\n") + T("info arguments:\n") + T(" -h show this [h]elp message and exit\n") + T(" -v show program's [v]ersion number and exit\n") +@@ -967,6 +970,8 @@ tmain(int argc, XML_Char **argv) { + unsigned long long attackThresholdBytes; + XML_Bool attackThresholdGiven = XML_FALSE; - #include "expat.h" - #include "codepage.h" -@@ -892,7 +893,7 @@ static void - usage(const XML_Char *prog, int rc) - { - ftprintf(stderr, -- T("usage: %s [-s] [-n] [-p] [-x] [-e encoding] [-w] [-d output-dir] [-c] [-m] [-r] [-t] [-N] [file ...]\n"), prog); -+ T("usage: %s [-s] [-n] [-p] [-x] [-e encoding] [-w] [-d output-dir] [-c] [-m] [-r] [-t] [-N] [-q] [file ...]\n"), prog); - exit(rc); - } - -@@ -917,6 +918,8 @@ tmain(int argc, XML_Char **argv) - XML_PARAM_ENTITY_PARSING_NEVER; - int useStdin = 0; - XmlwfUserData userData = { NULL, NULL, NULL }; + XML_Bool disableDeferral = XML_FALSE; + - - #ifdef _MSC_VER - _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_LEAK_CHECK_DF); -@@ -1003,6 +1006,11 @@ tmain(int argc, XML_Char **argv) - case T('v'): - showVersion(argv[0]); - return 0; + int exitCode = XMLWF_EXIT_SUCCESS; + enum XML_ParamEntityParsing paramEntityParsing + = XML_PARAM_ENTITY_PARSING_NEVER; +@@ -1089,6 +1094,11 @@ tmain(int argc, XML_Char **argv) { + #endif + break; + } + case T('q'): { + disableDeferral = XML_TRUE; + j++; @@ -1381,8 +1392,8 @@ index 82d028e..cd26919 100644 case T('\0'): if (j > 1) { i++; -@@ -1033,6 +1041,16 @@ tmain(int argc, XML_Char **argv) - exit(1); +@@ -1134,6 +1144,16 @@ tmain(int argc, XML_Char **argv) { + #endif } + if (disableDeferral) { @@ -1391,13 +1402,28 @@ index 82d028e..cd26919 100644 + // This prevents tperror(..) from reporting misleading "[..]: Success" + errno = EINVAL; + tperror(T("Failed to disable reparse deferral")); -+ exit(1); ++ exit(XMLWF_EXIT_INTERNAL_ERROR); + } + } + if (requireStandalone) XML_SetNotStandaloneHandler(parser, notStandalone); XML_SetParamEntityParsing(parser, paramEntityParsing); +diff --git a/expat/xmlwf/xmlwf_helpgen.py b/expat/xmlwf/xmlwf_helpgen.py +index c2a527f..1bd0a0a 100755 +--- a/expat/xmlwf/xmlwf_helpgen.py ++++ b/expat/xmlwf/xmlwf_helpgen.py +@@ -81,6 +81,10 @@ billion_laughs.add_argument('-a', metavar='FACTOR', + help='set maximum tolerated [a]mplification factor (default: 100.0)') + billion_laughs.add_argument('-b', metavar='BYTES', help='set number of output [b]ytes needed to activate (default: 8 MiB)') + ++reparse_deferral = parser.add_argument_group('reparse deferral') ++reparse_deferral.add_argument('-q', metavar='FACTOR', ++ help='disable reparse deferral, and allow [q]uadratic parse runtime with large tokens') ++ + parser.add_argument('files', metavar='FILE', nargs='*', help='file to process (default: STDIN)') + + info = parser.add_argument_group('info arguments') diff --git a/testdata/largefiles/aaaaaa_attr.xml b/testdata/largefiles/aaaaaa_attr.xml new file mode 100644 index 0000000..66e3d25 diff --git a/expat-2.5.0-CVE-2024-28757.patch b/expat-2.5.0-CVE-2024-28757.patch new file mode 100644 index 0000000..0ac0f9e --- /dev/null +++ b/expat-2.5.0-CVE-2024-28757.patch @@ -0,0 +1,172 @@ +commit cd3b344e0dbd19a812d0b4f34f9d089ed7c5c411 +Author: Tomas Korbar +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("") - 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[] = { ++ {"", XML_STATUS_ERROR}, ++ {"" ++ "", ++ XML_STATUS_ERROR}, ++ {"" ++ "", ++ XML_STATUS_OK}, ++ {"", 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 = "" ++ // (22) that is used in function accountingGetCurrentAmplification in ++ // xmlparse.c. ++ // 1.........1.........1.........1.........1..4 => 44 ++ const char doc[] = ""; ++ 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; diff --git a/expat-2.2.5-CVE-2024-45490.patch b/expat-2.5.0-CVE-2024-45490.patch similarity index 78% rename from expat-2.2.5-CVE-2024-45490.patch rename to expat-2.5.0-CVE-2024-45490.patch index 384797b..044f6c5 100644 --- a/expat-2.2.5-CVE-2024-45490.patch +++ b/expat-2.5.0-CVE-2024-45490.patch @@ -1,16 +1,16 @@ -commit 3c1a64705b5662c5b78f4aa5a5acc7a59c477094 +commit 05d87eb116ddde35bfa4e4c1d2ec7bcbda38c09b Author: Tomas Korbar -Date: Wed Sep 11 15:03:05 2024 +0200 +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 95c33c7..08cf9b0 100644 +index a10f3cb..d618bd8 100644 --- a/expat/doc/reference.html +++ b/expat/doc/reference.html -@@ -1039,7 +1039,9 @@ containing part (or perhaps all) of the document. The number of bytes of s +@@ -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 len. This means that s doesn't have to be null terminated. It also means that if len is larger than the number of bytes in the block of @@ -21,7 +21,7 @@ index 95c33c7..08cf9b0 100644 isFinal parameter informs the parser that this is the last piece of the document. Frequently, the last piece is empty (i.e. len is zero.) -@@ -1054,11 +1056,17 @@ XML_ParseBuffer(XML_Parser p, +@@ -1114,11 +1116,17 @@ XML_ParseBuffer(XML_Parser p, int isFinal);

    @@ -38,12 +38,12 @@ index 95c33c7..08cf9b0 100644 +

    -
    + 

    XML_GetBuffer

    diff --git a/expat/lib/xmlparse.c b/expat/lib/xmlparse.c -index 488f63f..c3c1af9 100644 +index 0896b16..f54e258 100644 --- a/expat/lib/xmlparse.c +++ b/expat/lib/xmlparse.c -@@ -1981,6 +1981,12 @@ XML_ParseBuffer(XML_Parser parser, int len, int isFinal) +@@ -1998,6 +1998,12 @@ XML_ParseBuffer(XML_Parser parser, int len, int isFinal) { if (parser == NULL) return XML_STATUS_ERROR; @@ -57,10 +57,10 @@ index 488f63f..c3c1af9 100644 case XML_SUSPENDED: parser->m_errorCode = XML_ERROR_SUSPENDED; diff --git a/expat/tests/runtests.c b/expat/tests/runtests.c -index 486073f..6a3e09a 100644 +index 93adc45..ed88f9f 100644 --- a/expat/tests/runtests.c +++ b/expat/tests/runtests.c -@@ -4083,6 +4083,57 @@ START_TEST(test_empty_parse) +@@ -3856,6 +3856,57 @@ START_TEST(test_empty_parse) { } END_TEST @@ -117,13 +117,13 @@ index 486073f..6a3e09a 100644 + /* Test odd corners of the XML_GetBuffer interface */ static enum XML_Status - get_feature(enum XML_FeatureEnum feature_id, long *presult) -@@ -13094,6 +13145,8 @@ make_suite(void) - tcase_add_test(tc_basic, test_user_parameters); - tcase_add_test(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); + 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) diff --git a/expat-2.5.0-CVE-2024-45491.patch b/expat-2.5.0-CVE-2024-45491.patch new file mode 100644 index 0000000..0f69dd7 --- /dev/null +++ b/expat-2.5.0-CVE-2024-45491.patch @@ -0,0 +1,31 @@ +From 8e439a9947e9dc80a395c0c7456545d8d9d9e421 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping +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) { diff --git a/expat-2.2.5-CVE-2024-45492.patch b/expat-2.5.0-CVE-2024-45492.patch similarity index 54% rename from expat-2.2.5-CVE-2024-45492.patch rename to expat-2.5.0-CVE-2024-45492.patch index 137911f..8950cdf 100644 --- a/expat-2.2.5-CVE-2024-45492.patch +++ b/expat-2.5.0-CVE-2024-45492.patch @@ -1,19 +1,21 @@ -commit 6fd04be3c2f7a2730c85b0eaf061549953161da3 -Author: Tomas Korbar -Date: Wed Sep 11 15:12:38 2024 +0200 +From 9bf0f2c16ee86f644dd1432507edff94c08dc232 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping +Date: Mon, 19 Aug 2024 22:37:16 +0200 +Subject: [PATCH] lib: Detect integer overflow in function nextScaffoldPart - Fix CVE-2024-45492 - - https://github.com/libexpat/libexpat/pull/892 +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 6818c4e..698e907 100644 +index 91682c188..f737575ea 100644 --- a/expat/lib/xmlparse.c +++ b/expat/lib/xmlparse.c -@@ -7426,6 +7426,15 @@ nextScaffoldPart(XML_Parser parser) +@@ -7558,6 +7558,15 @@ nextScaffoldPart(XML_Parser parser) { int next; - if (!dtd->scaffIndex) { + if (! dtd->scaffIndex) { + /* Detect and prevent integer overflow. + * The preprocessor guard addresses the "always false" warning + * from -Wtype-limits on platforms where @@ -24,5 +26,5 @@ index 6818c4e..698e907 100644 + } +#endif dtd->scaffIndex = (int *)MALLOC(parser, parser->m_groupSize * sizeof(int)); - if (!dtd->scaffIndex) + if (! dtd->scaffIndex) return -1; diff --git a/expat-2.2.5-CVE-2024-50602.patch b/expat-2.5.0-CVE-2024-50602.patch similarity index 57% rename from expat-2.2.5-CVE-2024-50602.patch rename to expat-2.5.0-CVE-2024-50602.patch index 3886c7d..1a06e2e 100644 --- a/expat-2.2.5-CVE-2024-50602.patch +++ b/expat-2.5.0-CVE-2024-50602.patch @@ -1,31 +1,31 @@ -commit c84ad1507fa42c25937af06e349c8f2f9bc34c11 +commit 38905b99bb78a6a691ed8358f30030116783656c Author: Tomas Korbar -Date: Fri Nov 8 11:18:42 2024 +0100 +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 afe12c5..157953c 100644 +index 842dd70..69b0ba1 100644 --- a/expat/lib/expat.h +++ b/expat/lib/expat.h -@@ -124,7 +124,9 @@ enum XML_Error { - XML_ERROR_RESERVED_PREFIX_XMLNS, - XML_ERROR_RESERVED_NAMESPACE_URI, - /* Added in 2.2.1. */ -- XML_ERROR_INVALID_ARGUMENT -+ XML_ERROR_INVALID_ARGUMENT, +@@ -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 ++ XML_ERROR_NOT_STARTED, }; enum XML_Content_Type { diff --git a/expat/lib/xmlparse.c b/expat/lib/xmlparse.c -index 698e907..ed079a5 100644 +index e0c2873..8b2af91 100644 --- a/expat/lib/xmlparse.c +++ b/expat/lib/xmlparse.c -@@ -2170,6 +2170,9 @@ XML_StopParser(XML_Parser parser, XML_Bool resumable) +@@ -2193,6 +2193,9 @@ XML_StopParser(XML_Parser parser, XML_Bool resumable) { if (parser == NULL) return XML_STATUS_ERROR; switch (parser->m_parsingStatus.parsing) { @@ -35,7 +35,7 @@ index 698e907..ed079a5 100644 case XML_SUSPENDED: if (resumable) { parser->m_errorCode = XML_ERROR_SUSPENDED; -@@ -2180,7 +2183,7 @@ XML_StopParser(XML_Parser parser, XML_Bool resumable) +@@ -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; @@ -44,9 +44,9 @@ index 698e907..ed079a5 100644 if (resumable) { #ifdef XML_DTD if (parser->m_isParamEntity) { -@@ -2192,6 +2195,9 @@ XML_StopParser(XML_Parser parser, XML_Bool resumable) - } - else +@@ -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: @@ -54,10 +54,10 @@ index 698e907..ed079a5 100644 } return XML_STATUS_OK; } -@@ -2456,6 +2462,9 @@ XML_ErrorString(enum XML_Error code) - /* Added in 2.2.5. */ - case XML_ERROR_INVALID_ARGUMENT: /* Constant added in 2.2.1, already */ - return XML_L("invalid argument"); +@@ -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"); @@ -65,13 +65,13 @@ index 698e907..ed079a5 100644 return NULL; } diff --git a/expat/tests/runtests.c b/expat/tests/runtests.c -index 6a3e09a..7b6d9fb 100644 +index ed88f9f..5769aa0 100644 --- a/expat/tests/runtests.c +++ b/expat/tests/runtests.c -@@ -9162,6 +9162,28 @@ START_TEST(test_misc_utf16le) +@@ -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); @@ -95,14 +95,14 @@ index 6a3e09a..7b6d9fb 100644 +END_TEST + static void - alloc_setup(void) - { -@@ -13325,6 +13347,8 @@ make_suite(void) - tcase_add_test(tc_misc, - test_misc_deny_internal_entity_closing_doctype_issue_317); - #endif -+ tcase_add_test(tc_misc, test_misc_resumeparser_not_crashing); -+ tcase_add_test(tc_misc, test_misc_stopparser_rejects_unstarted_parser); + 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); + suite_add_tcase(s, tc_alloc); + tcase_add_checked_fixture(tc_alloc, alloc_setup, alloc_teardown); diff --git a/expat-2.2.5-CVE-2024-8176.patch b/expat-2.5.0-CVE-2024-8176.patch similarity index 60% rename from expat-2.2.5-CVE-2024-8176.patch rename to expat-2.5.0-CVE-2024-8176.patch index 44e1af8..9006d10 100644 --- a/expat-2.2.5-CVE-2024-8176.patch +++ b/expat-2.5.0-CVE-2024-8176.patch @@ -1,17 +1,14 @@ -commit 9f90e0bc7b061a12dbe8b5d1c6ef389cf61e0614 +commit c0de4903900004dd3ca91f246e5f6489a49a132b Author: Tomas Korbar -Date: Fri Apr 4 10:19:58 2025 +0200 +Date: Mon Mar 24 10:04:33 2025 +0100 Fix CVE-2024-8176 - - Backport of upstream commit e113a4667258fd2328659f35ed9c29b64731edab - Pull Request #298 diff --git a/expat/lib/xmlparse.c b/expat/lib/xmlparse.c -index ed079a5..d00d23a 100644 +index 8b2af91..d68d2c8 100644 --- a/expat/lib/xmlparse.c +++ b/expat/lib/xmlparse.c -@@ -284,6 +284,10 @@ typedef struct { +@@ -305,6 +305,10 @@ typedef struct { const XML_Char *publicId; const XML_Char *notation; XML_Bool open; @@ -22,7 +19,7 @@ index ed079a5..d00d23a 100644 XML_Bool is_param; XML_Bool is_internal; /* true if declared in internal subset outside PE */ } ENTITY; -@@ -374,6 +378,12 @@ typedef struct { +@@ -395,6 +399,12 @@ typedef struct { int *scaffIndex; } DTD; @@ -35,51 +32,53 @@ index ed079a5..d00d23a 100644 typedef struct open_internal_entity { const char *internalEventPtr; const char *internalEventEndPtr; -@@ -381,6 +391,7 @@ typedef struct open_internal_entity { +@@ -402,6 +412,7 @@ typedef struct open_internal_entity { ENTITY *entity; int startTagLevel; XML_Bool betweenDecl; /* WFC: PE Between Declarations */ + enum EntityType type; } OPEN_INTERNAL_ENTITY; - typedef enum XML_Error PTRCALL Processor(XML_Parser parser, -@@ -418,9 +429,8 @@ 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 allowClosingDoctype); --static enum XML_Error --processInternalEntity(XML_Parser parser, ENTITY *entity, -- XML_Bool betweenDecl); + enum XML_Account { +@@ -461,8 +472,8 @@ static enum XML_Error doProlog(XML_Parser parser, const ENCODING *enc, + const char *next, const char **nextPtr, + XML_Bool haveMore, XML_Bool allowClosingDoctype, + enum XML_Account account); +-static enum XML_Error processInternalEntity(XML_Parser parser, ENTITY *entity, +- XML_Bool betweenDecl); +static enum XML_Error processEntity(XML_Parser parser, ENTITY *entity, + XML_Bool betweenDecl, enum EntityType type); - static enum XML_Error - doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, - const char *start, const char *end, const char **endPtr, -@@ -449,8 +459,9 @@ static enum XML_Error - storeAttributeValue(XML_Parser parser, const ENCODING *, XML_Bool isCdata, - const char *, const char *, STRING_POOL *); - static enum XML_Error --appendAttributeValue(XML_Parser parser, const ENCODING *, XML_Bool isCdata, -- const char *, const char *, STRING_POOL *); + static enum XML_Error doContent(XML_Parser parser, int startTagLevel, + const ENCODING *enc, const char *start, + const char *end, const char **endPtr, +@@ -492,16 +503,21 @@ static enum XML_Error storeAttributeValue(XML_Parser parser, const ENCODING *, + XML_Bool isCdata, const char *, + const char *, STRING_POOL *, + enum XML_Account account); +-static enum XML_Error appendAttributeValue(XML_Parser parser, const ENCODING *, +- XML_Bool isCdata, const char *, +- const char *, STRING_POOL *, +- enum XML_Account account); ++static enum XML_Error +appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, -+ const char *ptr, const char *end, STRING_POOL *pool, -+ const char **nextPtr); - static ATTRIBUTE_ID * - getAttributeId(XML_Parser parser, const ENCODING *enc, const char *start, - const char *end); -@@ -458,7 +469,10 @@ static int - setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *); - static enum XML_Error - storeEntityValue(XML_Parser parser, const ENCODING *enc, const char *start, -- const char *end); -+ const char *end, const char **nextPtr); ++ const char *ptr, const char *end, STRING_POOL *pool, ++ enum XML_Account account, const char **nextPtr); + static ATTRIBUTE_ID *getAttributeId(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end); + static int setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *); + static enum XML_Error storeEntityValue(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end, +- enum XML_Account account); ++ enum XML_Account account, ++ const char **nextPtr); +static enum XML_Error callStoreEntityValue(XML_Parser parser, + const ENCODING *enc, -+ const char *start, const char *end); - static int - reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, - const char *start, const char *end); -@@ -611,6 +625,10 @@ struct XML_ParserStruct { ++ const char *start, const char *end, ++ enum XML_Account account); + static int reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end); + static int reportComment(XML_Parser parser, const ENCODING *enc, +@@ -669,6 +685,10 @@ struct XML_ParserStruct { const char *m_positionPtr; OPEN_INTERNAL_ENTITY *m_openInternalEntities; OPEN_INTERNAL_ENTITY *m_freeInternalEntities; @@ -90,15 +89,15 @@ index ed079a5..d00d23a 100644 XML_Bool m_defaultExpandInternalEntities; int m_tagLevel; ENTITY *m_declEntity; -@@ -654,6 +672,7 @@ struct XML_ParserStruct { - enum XML_ParamEntityParsing m_paramEntityParsing; +@@ -716,6 +736,7 @@ struct XML_ParserStruct { + ACCOUNTING m_accounting; + ENTITY_STATS m_entity_stats; #endif - unsigned long m_hash_secret_salt; + XML_Bool m_reenter; }; - #define MALLOC(parser, s) (parser->m_mem.malloc_fcn((s))) -@@ -930,7 +949,29 @@ callProcessor(XML_Parser parser, const char *start, const char *end, + #define MALLOC(parser, s) (parser->m_mem.malloc_fcn((s))) +@@ -986,7 +1007,29 @@ callProcessor(XML_Parser parser, const char *start, const char *end, } } g_parseAttempts += 1; @@ -129,7 +128,7 @@ index ed079a5..d00d23a 100644 if (ret == XML_ERROR_NONE) { // if we consumed nothing, remember what we had on this parse attempt. if (*endPtr == start) { -@@ -1045,6 +1086,8 @@ parserCreate(const XML_Char *encodingName, +@@ -1097,6 +1140,8 @@ parserCreate(const XML_Char *encodingName, parser->m_freeBindingList = NULL; parser->m_freeTagList = NULL; parser->m_freeInternalEntities = NULL; @@ -138,7 +137,7 @@ index ed079a5..d00d23a 100644 parser->m_groupSize = 0; parser->m_groupConnector = NULL; -@@ -1149,6 +1192,8 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) +@@ -1199,6 +1244,8 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) { parser->m_eventEndPtr = NULL; parser->m_positionPtr = NULL; parser->m_openInternalEntities = NULL; @@ -147,7 +146,7 @@ index ed079a5..d00d23a 100644 parser->m_defaultExpandInternalEntities = XML_TRUE; parser->m_tagLevel = 0; parser->m_tagStack = NULL; -@@ -1159,6 +1204,8 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) +@@ -1209,6 +1256,8 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) { parser->m_unknownEncodingData = NULL; parser->m_parentParser = NULL; parser->m_parsingStatus.parsing = XML_INITIALIZED; @@ -156,7 +155,7 @@ index ed079a5..d00d23a 100644 #ifdef XML_DTD parser->m_isParamEntity = XML_FALSE; parser->m_useForeignDTD = XML_FALSE; -@@ -1208,6 +1255,24 @@ XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) +@@ -1268,6 +1317,24 @@ XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) { openEntity->next = parser->m_freeInternalEntities; parser->m_freeInternalEntities = openEntity; } @@ -181,7 +180,7 @@ index ed079a5..d00d23a 100644 moveToFreeBindingList(parser, parser->m_inheritedBindings); FREE(parser, parser->m_unknownEncodingMem); if (parser->m_unknownEncodingRelease) -@@ -1221,6 +1286,19 @@ XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) +@@ -1281,6 +1348,19 @@ XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) { return XML_TRUE; } @@ -199,18 +198,19 @@ index ed079a5..d00d23a 100644 +} + enum XML_Status XMLCALL - XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName) - { -@@ -1230,7 +1308,7 @@ XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName) + XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName) { + if (parser == NULL) +@@ -1289,8 +1369,7 @@ XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName) { XXX There's no way for the caller to determine which of the XXX possible error cases caused the XML_STATUS_ERROR return. */ -- if (parser->m_parsingStatus.parsing == XML_PARSING || parser->m_parsingStatus.parsing == XML_SUSPENDED) +- if (parser->m_parsingStatus.parsing == XML_PARSING +- || parser->m_parsingStatus.parsing == XML_SUSPENDED) + if (parserBusy(parser)) return XML_STATUS_ERROR; /* Get rid of any previous encoding name */ -@@ -1473,7 +1551,34 @@ XML_ParserFree(XML_Parser parser) +@@ -1527,7 +1606,34 @@ XML_ParserFree(XML_Parser parser) { entityList = entityList->next; FREE(parser, openEntity); } @@ -246,43 +246,47 @@ index ed079a5..d00d23a 100644 destroyBindings(parser->m_freeBindingList, parser); destroyBindings(parser->m_inheritedBindings, parser); poolDestroy(&parser->m_tempPool); -@@ -1516,7 +1621,7 @@ XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD) +@@ -1569,8 +1675,7 @@ XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD) { return XML_ERROR_INVALID_ARGUMENT; #ifdef XML_DTD /* block after XML_Parse()/XML_ParseBuffer() has been called */ -- if (parser->m_parsingStatus.parsing == XML_PARSING || parser->m_parsingStatus.parsing == XML_SUSPENDED) +- if (parser->m_parsingStatus.parsing == XML_PARSING +- || parser->m_parsingStatus.parsing == XML_SUSPENDED) + if (parserBusy(parser)) return XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING; parser->m_useForeignDTD = useDTD; return XML_ERROR_NONE; -@@ -1531,7 +1636,7 @@ XML_SetReturnNSTriplet(XML_Parser parser, int do_nst) +@@ -1585,8 +1690,7 @@ XML_SetReturnNSTriplet(XML_Parser parser, int do_nst) { if (parser == NULL) return; /* block after XML_Parse()/XML_ParseBuffer() has been called */ -- if (parser->m_parsingStatus.parsing == XML_PARSING || parser->m_parsingStatus.parsing == XML_SUSPENDED) +- if (parser->m_parsingStatus.parsing == XML_PARSING +- || parser->m_parsingStatus.parsing == XML_SUSPENDED) + if (parserBusy(parser)) return; parser->m_ns_triplets = do_nst ? XML_TRUE : XML_FALSE; } -@@ -1841,7 +1946,7 @@ XML_SetParamEntityParsing(XML_Parser parser, +@@ -1855,8 +1959,7 @@ XML_SetParamEntityParsing(XML_Parser parser, if (parser == NULL) return 0; /* block after XML_Parse()/XML_ParseBuffer() has been called */ -- if (parser->m_parsingStatus.parsing == XML_PARSING || parser->m_parsingStatus.parsing == XML_SUSPENDED) +- if (parser->m_parsingStatus.parsing == XML_PARSING +- || parser->m_parsingStatus.parsing == XML_SUSPENDED) + if (parserBusy(parser)) return 0; #ifdef XML_DTD parser->m_paramEntityParsing = peParsing; -@@ -1860,7 +1965,7 @@ XML_SetHashSalt(XML_Parser parser, +@@ -1873,8 +1976,7 @@ XML_SetHashSalt(XML_Parser parser, unsigned long hash_salt) { if (parser->m_parentParser) return XML_SetHashSalt(parser->m_parentParser, hash_salt); /* block after XML_Parse()/XML_ParseBuffer() has been called */ -- if (parser->m_parsingStatus.parsing == XML_PARSING || parser->m_parsingStatus.parsing == XML_SUSPENDED) +- if (parser->m_parsingStatus.parsing == XML_PARSING +- || parser->m_parsingStatus.parsing == XML_SUSPENDED) + if (parserBusy(parser)) return 0; parser->m_hash_secret_salt = hash_salt; return 1; -@@ -2164,6 +2269,11 @@ XML_GetBuffer(XML_Parser parser, int len) +@@ -2188,6 +2290,11 @@ XML_GetBuffer(XML_Parser parser, int len) { return parser->m_bufferEnd; } @@ -292,76 +296,64 @@ index ed079a5..d00d23a 100644 +} + enum XML_Status XMLCALL - XML_StopParser(XML_Parser parser, XML_Bool resumable) - { -@@ -2610,8 +2720,8 @@ contentProcessor(XML_Parser parser, - const char *end, - const char **endPtr) - { -- enum XML_Error result = doContent(parser, 0, parser->m_encoding, start, end, -- endPtr, (XML_Bool)!parser->m_parsingStatus.finalBuffer); -+ enum XML_Error result = doContent(parser, parser->m_parentParser ? 1 : 0, parser->m_encoding, start, end, -+ endPtr, (XML_Bool)! parser->m_parsingStatus.finalBuffer); + XML_StopParser(XML_Parser parser, XML_Bool resumable) { + if (parser == NULL) +@@ -2659,8 +2766,9 @@ static enum XML_Error PTRCALL + contentProcessor(XML_Parser parser, const char *start, const char *end, + const char **endPtr) { + enum XML_Error result = doContent( +- parser, 0, parser->m_encoding, start, end, endPtr, +- (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_ACCOUNT_DIRECT); ++ parser, parser->m_parentParser ? 1 : 0, parser->m_encoding, start, end, ++ endPtr, (XML_Bool)! parser->m_parsingStatus.finalBuffer, ++ XML_ACCOUNT_DIRECT); if (result == XML_ERROR_NONE) { - if (!storeRawNames(parser)) + if (! storeRawNames(parser)) return XML_ERROR_NO_MEMORY; -@@ -2697,6 +2807,11 @@ externalEntityInitProcessor3(XML_Parser parser, - return XML_ERROR_NONE; - case XML_FINISHED: - return XML_ERROR_ABORTED; -+ case XML_PARSING: -+ if (parser->m_reenter) { -+ return XML_ERROR_UNEXPECTED_STATE; // LCOV_EXCL_LINE -+ } -+ /* Fall through */ - default: - start = next; - } -@@ -2863,7 +2978,7 @@ doContent(XML_Parser parser, - reportDefault(parser, enc, s, next); - break; - } -- result = processInternalEntity(parser, entity, XML_FALSE); -+ result = processEntity(parser, entity, XML_FALSE, ENTITY_INTERNAL); - if (result != XML_ERROR_NONE) - return result; +@@ -2748,6 +2856,11 @@ externalEntityInitProcessor3(XML_Parser parser, const char *start, + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; ++ case XML_PARSING: ++ if (parser->m_reenter) { ++ return XML_ERROR_UNEXPECTED_STATE; // LCOV_EXCL_LINE ++ } ++ /* Fall through */ + default: + start = next; + } +@@ -2921,7 +3034,7 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, + reportDefault(parser, enc, s, next); + break; } -@@ -2990,9 +3105,13 @@ doContent(XML_Parser parser, - poolClear(&parser->m_tempPool); - freeBindings(parser, bindings); - } -- if ((parser->m_tagLevel == 0) && -- !((parser->m_parsingStatus.parsing == XML_FINISHED) || (parser->m_parsingStatus.parsing == XML_SUSPENDED))) { -- return epilogProcessor(parser, next, end, nextPtr); -+ if ((parser->m_tagLevel == 0) && (parser->m_parsingStatus.parsing != XML_FINISHED)) { +- result = processInternalEntity(parser, entity, XML_FALSE); ++ result = processEntity(parser, entity, XML_FALSE, ENTITY_INTERNAL); + if (result != XML_ERROR_NONE) + return result; + } else if (parser->m_externalEntityRefHandler) { +@@ -3047,7 +3160,9 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, + } + if ((parser->m_tagLevel == 0) + && (parser->m_parsingStatus.parsing != XML_FINISHED)) { +- if (parser->m_parsingStatus.parsing == XML_SUSPENDED) ++ if (parser->m_parsingStatus.parsing == XML_SUSPENDED ++ || (parser->m_parsingStatus.parsing == XML_PARSING ++ && parser->m_reenter)) + parser->m_processor = epilogProcessor; + else + return epilogProcessor(parser, next, end, nextPtr); +@@ -3108,7 +3223,9 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, + } + if ((parser->m_tagLevel == 0) + && (parser->m_parsingStatus.parsing != XML_FINISHED)) { +- if (parser->m_parsingStatus.parsing == XML_SUSPENDED) + if (parser->m_parsingStatus.parsing == XML_SUSPENDED + || (parser->m_parsingStatus.parsing == XML_PARSING + && parser->m_reenter)) -+ parser->m_processor = epilogProcessor; -+ else -+ return epilogProcessor(parser, next, end, nextPtr); - } - break; - case XML_TOK_END_TAG: -@@ -3046,8 +3165,15 @@ doContent(XML_Parser parser, - parser->m_freeBindingList = b; - b->prefix->binding = b->prevPrefixBinding; - } -- if (parser->m_tagLevel == 0) -- return epilogProcessor(parser, next, end, nextPtr); -+ if ((parser->m_tagLevel == 0) -+ && (parser->m_parsingStatus.parsing != XML_FINISHED)) { -+ if (parser->m_parsingStatus.parsing == XML_SUSPENDED -+ || (parser->m_parsingStatus.parsing == XML_PARSING -+ && parser->m_reenter)) -+ parser->m_processor = epilogProcessor; -+ else -+ return epilogProcessor(parser, next, end, nextPtr); -+ } - } - break; - case XML_TOK_CHAR_REF: -@@ -3183,14 +3309,22 @@ doContent(XML_Parser parser, + parser->m_processor = epilogProcessor; + else + return epilogProcessor(parser, next, end, nextPtr); +@@ -3241,14 +3358,22 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, break; /* LCOV_EXCL_STOP */ } @@ -380,12 +372,12 @@ index ed079a5..d00d23a 100644 + return XML_ERROR_NONE; + } + /* Fall through */ - default: ; + default:; + *eventPP = s = next; } } /* not reached */ -@@ -4112,14 +4246,21 @@ doCdataSection(XML_Parser parser, +@@ -4165,14 +4290,21 @@ doCdataSection(XML_Parser parser, const ENCODING *enc, const char **startPtr, /* LCOV_EXCL_STOP */ } @@ -403,51 +395,52 @@ index ed079a5..d00d23a 100644 + return XML_ERROR_UNEXPECTED_STATE; // LCOV_EXCL_LINE + } + /* Fall through */ - default: ; + default:; + *eventPP = s = next; } } /* not reached */ -@@ -4466,7 +4607,7 @@ entityValueInitProcessor(XML_Parser parser, - break; +@@ -4504,7 +4636,7 @@ entityValueInitProcessor(XML_Parser parser, const char *s, const char *end, } /* found end of entity value - can store it now */ -- return storeEntityValue(parser, parser->m_encoding, s, end); -+ return storeEntityValue(parser, parser->m_encoding, s, end, NULL); - } - else if (tok == XML_TOK_XML_DECL) { + return storeEntityValue(parser, parser->m_encoding, s, end, +- XML_ACCOUNT_DIRECT); ++ XML_ACCOUNT_DIRECT, NULL); + } else if (tok == XML_TOK_XML_DECL) { enum XML_Error result; -@@ -4581,7 +4722,7 @@ entityValueProcessor(XML_Parser parser, + result = processXmlDecl(parser, 0, start, next); +@@ -4631,7 +4763,7 @@ entityValueProcessor(XML_Parser parser, const char *s, const char *end, break; } /* found end of entity value - can store it now */ -- return storeEntityValue(parser, enc, s, end); -+ return storeEntityValue(parser, enc, s, end, NULL); +- return storeEntityValue(parser, enc, s, end, XML_ACCOUNT_DIRECT); ++ return storeEntityValue(parser, enc, s, end, XML_ACCOUNT_DIRECT, NULL); } start = next; } -@@ -5012,9 +5153,8 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, +@@ -5069,9 +5201,9 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, break; case XML_ROLE_ENTITY_VALUE: if (dtd->keepProcessing) { -- enum XML_Error result = storeEntityValue(parser, enc, -- s + enc->minBytesPerChar, -- next - enc->minBytesPerChar); +- enum XML_Error result +- = storeEntityValue(parser, enc, s + enc->minBytesPerChar, +- next - enc->minBytesPerChar, XML_ACCOUNT_NONE); + enum XML_Error result = callStoreEntityValue( -+ parser, enc, s + enc->minBytesPerChar, next - enc->minBytesPerChar); ++ parser, enc, s + enc->minBytesPerChar, next - enc->minBytesPerChar, ++ XML_ACCOUNT_NONE); if (parser->m_declEntity) { parser->m_declEntity->textPtr = poolStart(&dtd->entityValuePool); - parser->m_declEntity->textLen = (int)(poolLength(&dtd->entityValuePool)); -@@ -5429,7 +5569,7 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, + parser->m_declEntity->textLen +@@ -5467,7 +5599,7 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, enum XML_Error result; - XML_Bool betweenDecl = - (role == XML_ROLE_PARAM_ENTITY_REF ? XML_TRUE : XML_FALSE); + XML_Bool betweenDecl + = (role == XML_ROLE_PARAM_ENTITY_REF ? XML_TRUE : XML_FALSE); - result = processInternalEntity(parser, entity, betweenDecl); + result = processEntity(parser, entity, betweenDecl, ENTITY_INTERNAL); if (result != XML_ERROR_NONE) return result; handleDefault = XML_FALSE; -@@ -5627,6 +5767,12 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, +@@ -5672,6 +5804,12 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, return XML_ERROR_NONE; case XML_FINISHED: return XML_ERROR_ABORTED; @@ -460,7 +453,7 @@ index ed079a5..d00d23a 100644 default: s = next; tok = XmlPrologTok(enc, s, end, &next); -@@ -5690,75 +5836,80 @@ epilogProcessor(XML_Parser parser, +@@ -5739,28 +5877,58 @@ epilogProcessor(XML_Parser parser, const char *s, const char *end, default: return XML_ERROR_JUNK_AFTER_DOC_ELEMENT; } @@ -478,64 +471,64 @@ index ed079a5..d00d23a 100644 + return XML_ERROR_UNEXPECTED_STATE; // LCOV_EXCL_LINE + } + /* Fall through */ - default: ; + default:; + parser->m_eventPtr = s = next; } } } static enum XML_Error --processInternalEntity(XML_Parser parser, ENTITY *entity, -- XML_Bool betweenDecl) --{ +-processInternalEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl) { - const char *textStart, *textEnd; - const char *next; - enum XML_Error result; - OPEN_INTERNAL_ENTITY *openEntity; -- ++processEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl, ++ enum EntityType type) { ++ OPEN_INTERNAL_ENTITY *openEntity, **openEntityList, **freeEntityList; ++ switch (type) { ++ case ENTITY_INTERNAL: ++ parser->m_processor = internalEntityProcessor; ++ openEntityList = &parser->m_openInternalEntities; ++ freeEntityList = &parser->m_freeInternalEntities; ++ break; ++ case ENTITY_ATTRIBUTE: ++ openEntityList = &parser->m_openAttributeEntities; ++ freeEntityList = &parser->m_freeAttributeEntities; ++ break; ++ case ENTITY_VALUE: ++ openEntityList = &parser->m_openValueEntities; ++ freeEntityList = &parser->m_freeValueEntities; ++ break; ++ /* default case serves merely as a safety net in case of a ++ * wrong entityType. Therefore we exclude the following lines ++ * from the test coverage. ++ * ++ * LCOV_EXCL_START ++ */ ++ default: ++ // Should not reach here ++ assert(0); ++ /* LCOV_EXCL_STOP */ ++ } + - if (parser->m_freeInternalEntities) { - openEntity = parser->m_freeInternalEntities; - parser->m_freeInternalEntities = openEntity->next; -+processEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl, -+ enum EntityType type) { -+ OPEN_INTERNAL_ENTITY *openEntity, **openEntityList, **freeEntityList; -+ switch (type) { -+ case ENTITY_INTERNAL: -+ parser->m_processor = internalEntityProcessor; -+ openEntityList = &parser->m_openInternalEntities; -+ freeEntityList = &parser->m_freeInternalEntities; -+ break; -+ case ENTITY_ATTRIBUTE: -+ openEntityList = &parser->m_openAttributeEntities; -+ freeEntityList = &parser->m_freeAttributeEntities; -+ break; -+ case ENTITY_VALUE: -+ openEntityList = &parser->m_openValueEntities; -+ freeEntityList = &parser->m_freeValueEntities; -+ break; -+ /* default case serves merely as a safety net in case of a -+ * wrong entityType. Therefore we exclude the following lines -+ * from the test coverage. -+ * -+ * LCOV_EXCL_START -+ */ -+ default: -+ // Should not reach here -+ assert(0); -+ /* LCOV_EXCL_STOP */ - } -- else { -+ + if (*freeEntityList) { -+ openEntity = *freeEntityList; -+ *freeEntityList = openEntity->next; -+ } else { - openEntity = (OPEN_INTERNAL_ENTITY *)MALLOC(parser, sizeof(OPEN_INTERNAL_ENTITY)); - if (!openEntity) ++ openEntity = *freeEntityList; ++ *freeEntityList = openEntity->next; + } else { + openEntity + = (OPEN_INTERNAL_ENTITY *)MALLOC(parser, sizeof(OPEN_INTERNAL_ENTITY)); +@@ -5768,56 +5936,33 @@ processInternalEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl) { return XML_ERROR_NO_MEMORY; } entity->open = XML_TRUE; + entity->hasMore = XML_TRUE; + #ifdef XML_DTD + entityTrackingOnOpen(parser, entity, __LINE__); + #endif entity->processed = 0; - openEntity->next = parser->m_openInternalEntities; - parser->m_openInternalEntities = openEntity; @@ -547,28 +540,32 @@ index ed079a5..d00d23a 100644 openEntity->betweenDecl = betweenDecl; openEntity->internalEventPtr = NULL; openEntity->internalEventEndPtr = NULL; -- textStart = (char *)entity->textPtr; -- textEnd = (char *)(entity->textPtr + entity->textLen); +- textStart = (const char *)entity->textPtr; +- textEnd = (const char *)(entity->textPtr + entity->textLen); - /* Set a safe default value in case 'next' does not get set */ - next = textStart; - -#ifdef XML_DTD - 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, XML_FALSE); -- } -- else +- int tok +- = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next); +- result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd, +- tok, next, &next, XML_FALSE, XML_FALSE, +- XML_ACCOUNT_ENTITY_EXPANSION); +- } else -#endif /* XML_DTD */ -- result = doContent(parser, parser->m_tagLevel, parser->m_internalEncoding, textStart, -- textEnd, &next, XML_FALSE); +- result = doContent(parser, parser->m_tagLevel, parser->m_internalEncoding, +- textStart, textEnd, &next, XML_FALSE, +- XML_ACCOUNT_ENTITY_EXPANSION); - - if (result == XML_ERROR_NONE) { - if (textEnd != next && parser->m_parsingStatus.parsing == XML_SUSPENDED) { - entity->processed = (int)(next - textStart); - parser->m_processor = internalEntityProcessor; -- } -- else { +- } else { +-#ifdef XML_DTD +- entityTrackingOnClose(parser, entity, __LINE__); +-#endif /* XML_DTD */ - entity->open = XML_FALSE; - parser->m_openInternalEntities = openEntity->next; - /* put openEntity back in list of free instances */ @@ -585,128 +582,147 @@ index ed079a5..d00d23a 100644 } static enum XML_Error PTRCALL -@@ -5776,58 +5927,59 @@ internalEntityProcessor(XML_Parser parser, + internalEntityProcessor(XML_Parser parser, const char *s, const char *end, + const char **nextPtr) { ++ UNUSED_P(s); ++ UNUSED_P(end); ++ UNUSED_P(nextPtr); + ENTITY *entity; + const char *textStart, *textEnd; + const char *next; +@@ -5827,72 +5972,63 @@ internalEntityProcessor(XML_Parser parser, const char *s, const char *end, return XML_ERROR_UNEXPECTED_STATE; entity = openEntity->entity; -- textStart = ((char *)entity->textPtr) + entity->processed; -- textEnd = (char *)(entity->textPtr + entity->textLen); +- textStart = ((const char *)entity->textPtr) + entity->processed; +- textEnd = (const char *)(entity->textPtr + entity->textLen); - /* Set a safe default value in case 'next' does not get set */ - next = textStart; - -#ifdef XML_DTD - 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, XML_TRUE); -- } -- else +- int tok +- = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next); +- result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd, +- tok, next, &next, XML_FALSE, XML_TRUE, +- XML_ACCOUNT_ENTITY_EXPANSION); +- } else -#endif /* XML_DTD */ -- result = doContent(parser, openEntity->startTagLevel, parser->m_internalEncoding, -- textStart, textEnd, &next, XML_FALSE); +- result = doContent(parser, openEntity->startTagLevel, +- parser->m_internalEncoding, textStart, textEnd, &next, +- XML_FALSE, XML_ACCOUNT_ENTITY_EXPANSION); - - if (result != XML_ERROR_NONE) - return result; -- else if (textEnd != next && parser->m_parsingStatus.parsing == XML_SUSPENDED) { -- entity->processed = (int)(next - (char *)entity->textPtr); -- return result; -- } -- else { -- entity->open = XML_FALSE; -- parser->m_openInternalEntities = openEntity->next; -- /* put openEntity back in list of free instances */ -- openEntity->next = parser->m_freeInternalEntities; -- parser->m_freeInternalEntities = openEntity; -- } +- +- if (textEnd != next && parser->m_parsingStatus.parsing == XML_SUSPENDED) { +- entity->processed = (int)(next - (const char *)entity->textPtr); + // This will return early + if (entity->hasMore) { -+ textStart = ((const char *)entity->textPtr) + entity->processed; -+ textEnd = (const char *)(entity->textPtr + entity->textLen); -+ /* Set a safe default value in case 'next' does not get set */ -+ next = textStart; -+ -+ 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, XML_FALSE); -+ } else { -+ result = doContent(parser, openEntity->startTagLevel, -+ parser->m_internalEncoding, textStart, textEnd, &next, -+ XML_FALSE); -+ } ++ textStart = ((const char *)entity->textPtr) + entity->processed; ++ textEnd = (const char *)(entity->textPtr + entity->textLen); ++ /* Set a safe default value in case 'next' does not get set */ ++ next = textStart; ++ ++ 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, XML_FALSE, ++ XML_ACCOUNT_ENTITY_EXPANSION); ++ } else { ++ result = doContent(parser, openEntity->startTagLevel, ++ parser->m_internalEncoding, textStart, textEnd, &next, ++ XML_FALSE, XML_ACCOUNT_ENTITY_EXPANSION); ++ } ++ ++ if (result != XML_ERROR_NONE) ++ return result; ++ // Check if entity is complete, if not, mark down how much of it is ++ // processed ++ if (textEnd != next ++ && (parser->m_parsingStatus.parsing == XML_SUSPENDED ++ || (parser->m_parsingStatus.parsing == XML_PARSING ++ && parser->m_reenter))) { ++ entity->processed = (int)(next - (const char *)entity->textPtr); ++ return result; ++ } ++ // Entity is complete. We cannot close it here since we need to first ++ // process its possible inner entities (which are added to the ++ // m_openInternalEntities during doProlog or doContent calls above) ++ entity->hasMore = XML_FALSE; ++ triggerReenter(parser); + return result; +- } +- ++ } // End of entity processing, "if" block will return here ++ // Remove fully processed openEntity from open entity list. + #ifdef XML_DTD + entityTrackingOnClose(parser, entity, __LINE__); + #endif ++ // openEntity is m_openInternalEntities' head, as we set it at the start of ++ // this function and we skipped doProlog and doContent calls with hasMore set ++ // to false. This means we can directly remove the head of ++ // m_openInternalEntities ++ assert(parser->m_openInternalEntities == openEntity); + entity->open = XML_FALSE; +- parser->m_openInternalEntities = openEntity->next; ++ parser->m_openInternalEntities = parser->m_openInternalEntities->next; + /* put openEntity back in list of free instances */ + openEntity->next = parser->m_freeInternalEntities; + parser->m_freeInternalEntities = openEntity; +- // If there are more open entities we want to stop right here and have the +- // upcoming call to XML_ResumeParser continue with entity content, or it would +- // be ignored altogether. +- if (parser->m_openInternalEntities != NULL +- && parser->m_parsingStatus.parsing == XML_SUSPENDED) { +- return XML_ERROR_NONE; +- } +- -#ifdef XML_DTD - if (entity->is_param) { - int tok; - 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_TRUE); -- } -- else +- (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE, +- XML_ACCOUNT_DIRECT); +- } else -#endif /* XML_DTD */ - { - parser->m_processor = contentProcessor; - /* see externalEntityContentProcessor vs contentProcessor */ -- result = 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, +- XML_ACCOUNT_DIRECT); - if (result == XML_ERROR_NONE) { - if (! storeRawNames(parser)) - return XML_ERROR_NO_MEMORY; -+ if (result != XML_ERROR_NONE) -+ return result; -+ // Check if entity is complete, if not, mark down how much of it is -+ // processed -+ if (textEnd != next -+ && (parser->m_parsingStatus.parsing == XML_SUSPENDED -+ || (parser->m_parsingStatus.parsing == XML_PARSING -+ && parser->m_reenter))) { -+ entity->processed = (int)(next - (const char *)entity->textPtr); -+ return result; - } -+ // Entity is complete. We cannot close it here since we need to first -+ // process its possible inner entities (which are added to the -+ // m_openInternalEntities during doProlog or doContent calls above) -+ entity->hasMore = XML_FALSE; -+ triggerReenter(parser); - return result; -- } -+ } // End of entity processing, "if" block will return here -+ // Remove fully processed openEntity from open entity list. -+ -+ // openEntity is m_openInternalEntities' head, as we set it at the start of -+ // this function and we skipped doProlog and doContent calls with hasMore set -+ // to false. This means we can directly remove the head of -+ // m_openInternalEntities -+ assert(parser->m_openInternalEntities == openEntity); -+ entity->open = XML_FALSE; -+ parser->m_openInternalEntities = parser->m_openInternalEntities->next; -+ /* put openEntity back in list of free instances */ -+ openEntity->next = parser->m_freeInternalEntities; -+ parser->m_freeInternalEntities = openEntity; +- } +- return result; + if (parser->m_openInternalEntities == NULL) { + parser->m_processor = entity->is_param ? prologProcessor : contentProcessor; -+ } + } + triggerReenter(parser); + return XML_ERROR_NONE; } static enum XML_Error PTRCALL -@@ -5844,8 +5996,66 @@ storeAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, - const char *ptr, const char *end, - STRING_POOL *pool) - { -- enum XML_Error result = appendAttributeValue(parser, enc, isCdata, ptr, -- end, pool); +@@ -5908,8 +6044,70 @@ static enum XML_Error + storeAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, + const char *ptr, const char *end, STRING_POOL *pool, + enum XML_Account account) { +- enum XML_Error result +- = appendAttributeValue(parser, enc, isCdata, ptr, end, pool, account); + const char *next = ptr; + enum XML_Error result = XML_ERROR_NONE; + + while (1) { + if (! parser->m_openAttributeEntities) { + result = appendAttributeValue(parser, enc, isCdata, next, end, pool, -+ &next); ++ account, &next); + } else { + OPEN_INTERNAL_ENTITY *const openEntity = parser->m_openAttributeEntities; + if (! openEntity) @@ -722,7 +738,7 @@ index ed079a5..d00d23a 100644 + if (entity->hasMore) { + result = appendAttributeValue( + parser, parser->m_internalEncoding, isCdata, textStart, textEnd, -+ pool, &nextInEntity); ++ pool, XML_ACCOUNT_ENTITY_EXPANSION, &nextInEntity); + if (result != XML_ERROR_NONE) + break; + // Check if entity is complete, if not, mark down how much of it is @@ -741,6 +757,10 @@ index ed079a5..d00d23a 100644 + continue; + } // End of entity processing, "if" block skips the rest + ++ // Remove fully processed openEntity from open entity list. ++#if XML_DTD == 1 ++ entityTrackingOnClose(parser, entity, __LINE__); ++#endif + // openEntity is m_openAttributeEntities' head, since we set it at the + // start of this function and because we skipped appendAttributeValue call + // with hasMore set to false. This means we can directly remove the head @@ -759,22 +779,21 @@ index ed079a5..d00d23a 100644 + break; + } + } -+ ++ if (result) return result; - if (!isCdata && poolLength(pool) && poolLastChar(pool) == 0x20) -@@ -5858,14 +6068,17 @@ storeAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, + if (! isCdata && poolLength(pool) && poolLastChar(pool) == 0x20) +@@ -5922,7 +6120,7 @@ storeAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, static enum XML_Error appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, - const char *ptr, const char *end, -- STRING_POOL *pool) -+ STRING_POOL *pool, const char **nextPtr) - { - DTD * const dtd = parser->m_dtd; /* save one level of indirection */ - for (;;) { -- const char *next; -+ const char *next = ptr; /* XmlAttributeValueTok doesn't always set the last arg */ - int tok = XmlAttributeValueTok(enc, ptr, end, &next); + const char *ptr, const char *end, STRING_POOL *pool, +- enum XML_Account account) { ++ enum XML_Account account, const char **nextPtr) { + DTD *const dtd = parser->m_dtd; /* save one level of indirection */ + #ifndef XML_DTD + UNUSED_P(account); +@@ -5940,6 +6138,9 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, + #endif switch (tok) { case XML_TOK_NONE: + if (nextPtr) { @@ -783,57 +802,64 @@ index ed079a5..d00d23a 100644 return XML_ERROR_NONE; case XML_TOK_INVALID: if (enc == parser->m_encoding) -@@ -6006,14 +6219,11 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, - } - else { - enum XML_Error result; -- const XML_Char *textEnd = entity->textPtr + entity->textLen; -- entity->open = XML_TRUE; -- result = appendAttributeValue(parser, parser->m_internalEncoding, isCdata, -- (char *)entity->textPtr, -- (char *)textEnd, pool); -- entity->open = XML_FALSE; -- if (result) -- return result; -+ result = processEntity(parser, entity, XML_FALSE, ENTITY_ATTRIBUTE); -+ if ((result == XML_ERROR_NONE) && (nextPtr != NULL)) { -+ *nextPtr = next; -+ } -+ return result; - } +@@ -6080,21 +6281,11 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, + return XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF; + } else { + enum XML_Error result; +- const XML_Char *textEnd = entity->textPtr + entity->textLen; +- entity->open = XML_TRUE; +-#ifdef XML_DTD +- entityTrackingOnOpen(parser, entity, __LINE__); +-#endif +- result = appendAttributeValue(parser, parser->m_internalEncoding, +- isCdata, (const char *)entity->textPtr, +- (const char *)textEnd, pool, +- XML_ACCOUNT_ENTITY_EXPANSION); +-#ifdef XML_DTD +- entityTrackingOnClose(parser, entity, __LINE__); +-#endif +- entity->open = XML_FALSE; +- if (result) +- return result; ++ result = processEntity(parser, entity, XML_FALSE, ENTITY_ATTRIBUTE); ++ if ((result == XML_ERROR_NONE) && (nextPtr != NULL)) { ++ *nextPtr = next; ++ } ++ return result; } - break; -@@ -6043,7 +6253,8 @@ static enum XML_Error - storeEntityValue(XML_Parser parser, - const ENCODING *enc, - const char *entityTextPtr, -- const char *entityTextEnd) -+ const char *entityTextEnd, -+ const char **nextPtr) - { - DTD * const dtd = parser->m_dtd; /* save one level of indirection */ + } break; + default: +@@ -6122,7 +6313,7 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, + static enum XML_Error + storeEntityValue(XML_Parser parser, const ENCODING *enc, + const char *entityTextPtr, const char *entityTextEnd, +- enum XML_Account account) { ++ enum XML_Account account, const char **nextPtr) { + DTD *const dtd = parser->m_dtd; /* save one level of indirection */ STRING_POOL *pool = &(dtd->entityValuePool); -@@ -6060,8 +6271,9 @@ storeEntityValue(XML_Parser parser, + enum XML_Error result = XML_ERROR_NONE; +@@ -6140,8 +6331,9 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc, return XML_ERROR_NO_MEMORY; } + const char *next; for (;;) { -- const char *next; -+ next = entityTextPtr; /* XmlEntityValueTok doesn't always set the last arg */ +- const char *next ++ next + = entityTextPtr; /* XmlEntityValueTok doesn't always set the last arg */ int tok = XmlEntityValueTok(enc, entityTextPtr, entityTextEnd, &next); - switch (tok) { - case XML_TOK_PARAM_ENTITY_REF: -@@ -6115,15 +6327,8 @@ storeEntityValue(XML_Parser parser, + +@@ -6205,16 +6397,8 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc, + } else dtd->keepProcessing = dtd->standalone; - } - else { + } else { - entity->open = XML_TRUE; -- result = storeEntityValue(parser, -- parser->m_internalEncoding, -- (char *)entity->textPtr, -- (char *)(entity->textPtr -- + entity->textLen)); +- entityTrackingOnOpen(parser, entity, __LINE__); +- result = storeEntityValue( +- parser, parser->m_internalEncoding, (const char *)entity->textPtr, +- (const char *)(entity->textPtr + entity->textLen), +- XML_ACCOUNT_ENTITY_EXPANSION); +- entityTrackingOnClose(parser, entity, __LINE__); - entity->open = XML_FALSE; - if (result) - goto endEntityValue; @@ -842,7 +868,7 @@ index ed079a5..d00d23a 100644 } break; } -@@ -6213,9 +6418,79 @@ endEntityValue: +@@ -6302,9 +6486,84 @@ endEntityValue: #ifdef XML_DTD parser->m_prologState.inEntityValue = oldInEntityValue; #endif /* XML_DTD */ @@ -855,13 +881,14 @@ index ed079a5..d00d23a 100644 +static enum XML_Error +callStoreEntityValue(XML_Parser parser, const ENCODING *enc, -+ const char *entityTextPtr, const char *entityTextEnd) { ++ const char *entityTextPtr, const char *entityTextEnd, ++ enum XML_Account account) { + const char *next = entityTextPtr; + enum XML_Error result = XML_ERROR_NONE; + while (1) { + if (! parser->m_openValueEntities) { + result -+ = storeEntityValue(parser, enc, next, entityTextEnd, &next); ++ = storeEntityValue(parser, enc, next, entityTextEnd, account, &next); + } else { + OPEN_INTERNAL_ENTITY *const openEntity = parser->m_openValueEntities; + if (! openEntity) @@ -876,7 +903,8 @@ index ed079a5..d00d23a 100644 + const char *nextInEntity = textStart; + if (entity->hasMore) { + result = storeEntityValue(parser, parser->m_internalEncoding, textStart, -+ textEnd, &nextInEntity); ++ textEnd, XML_ACCOUNT_ENTITY_EXPANSION, ++ &nextInEntity); + if (result != XML_ERROR_NONE) + break; + // Check if entity is complete, if not, mark down how much of it is @@ -895,6 +923,10 @@ index ed079a5..d00d23a 100644 + continue; + } // End of entity processing, "if" block skips the rest + ++ // Remove fully processed openEntity from open entity list. ++# if XML_DTD == 1 ++ entityTrackingOnClose(parser, entity, __LINE__); ++# endif + // openEntity is m_openValueEntities' head, since we set it at the + // start of this function and because we skipped storeEntityValue call + // with hasMore set to false. This means we can directly remove the head @@ -917,16 +949,15 @@ index ed079a5..d00d23a 100644 + + return result; +} -+ + static void FASTCALL - normalizeLines(XML_Char *s) - { + normalizeLines(XML_Char *s) { + XML_Char *p; diff --git a/expat/tests/runtests.c b/expat/tests/runtests.c -index 7b6d9fb..0b5bcf1 100644 +index 5769aa0..5db384d 100644 --- a/expat/tests/runtests.c +++ b/expat/tests/runtests.c -@@ -39,6 +39,8 @@ +@@ -47,6 +47,8 @@ #endif #include @@ -935,7 +966,7 @@ index 7b6d9fb..0b5bcf1 100644 #include #include #include -@@ -1915,6 +1917,87 @@ START_TEST(test_wfc_no_recursive_entity_refs) +@@ -1788,6 +1790,89 @@ START_TEST(test_wfc_no_recursive_entity_refs) { } END_TEST @@ -993,6 +1024,8 @@ index 7b6d9fb..0b5bcf1 100644 + XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS) + == 1); + } ++#else ++ UNUSED_P(usesParameterEntities); +#endif // XML_DTD + + const enum XML_Status status @@ -1020,72 +1053,50 @@ index 7b6d9fb..0b5bcf1 100644 +} +END_TEST + - /* Test incomplete external entities are faulted */ - START_TEST(test_ext_entity_invalid_parse) - { -@@ -5352,6 +5435,34 @@ START_TEST(test_suspend_epilog) - } - END_TEST + START_TEST(test_recursive_external_parameter_entity_2) { + struct TestCase { + const char *doc; +@@ -2958,6 +3043,12 @@ typedef struct elementInfo { + AttrInfo *attributes; + } ElementInfo; -+static void XMLCALL -+suspending_end_handler(void *userData, -+ const XML_Char *UNUSED_P(s)) -+{ -+ XML_StopParser((XML_Parser)userData, 1); -+} + -+START_TEST(test_suspend_in_sole_empty_tag) -+{ -+ const char *text = ""; -+ enum XML_Status rc; ++typedef struct StructParserAndElementInfo { ++ XML_Parser parser; ++ ElementInfo *info; ++} ParserAndElementInfo; + -+ XML_SetEndElementHandler(parser, suspending_end_handler); -+ XML_SetUserData(parser, parser); -+ rc = _XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), -+ XML_TRUE); -+ if (rc == XML_STATUS_ERROR) -+ xml_failure(parser); -+ else if (rc != XML_STATUS_SUSPENDED) -+ fail("Suspend not triggered"); -+ rc = XML_ResumeParser(parser); -+ if (rc == XML_STATUS_ERROR) -+ xml_failure(parser); -+ else if (rc != XML_STATUS_OK) -+ fail("Resume failed"); -+} -+END_TEST -+ - START_TEST(test_unfinished_epilog) - { - const char *text = "<"; -@@ -5671,6 +5782,7 @@ END_TEST + static void XMLCALL + counting_start_element_handler(void *userData, const XML_Char *name, + const XML_Char **atts) { +@@ -5306,6 +5397,7 @@ END_TEST typedef struct ext_hdlr_data { - const char *parse_text; - XML_ExternalEntityRefHandler handler; -+ CharData *storage; + const char *parse_text; + XML_ExternalEntityRefHandler handler; ++ CharData *storage; } ExtHdlrData; static int XMLCALL -@@ -5708,7 +5820,8 @@ START_TEST(test_skipped_null_loaded_ext_entity) - "\n" - "\n" - "%pe2;\n", -- external_entity_null_loader -+ external_entity_null_loader, -+ NULL - }; +@@ -5340,7 +5432,7 @@ START_TEST(test_skipped_null_loaded_ext_entity) { + = {"\n" + "\n" + "%pe2;\n", +- external_entity_null_loader}; ++ external_entity_null_loader, NULL}; - XML_SetUserData(parser, &test_data); -@@ -5729,6 +5842,7 @@ START_TEST(test_skipped_unloaded_ext_entity) - "\n" - "\n" - "%pe2;\n", -+ NULL, - NULL - }; + XML_SetUserData(g_parser, &test_data); + XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS); +@@ -5358,7 +5450,7 @@ START_TEST(test_skipped_unloaded_ext_entity) { + = {"\n" + "\n" + "%pe2;\n", +- NULL}; ++ NULL, NULL}; -@@ -6957,6 +7071,24 @@ accumulate_entity_decl(void *userData, - CharData_AppendXMLChars(storage, XCS("\n"), 1); + XML_SetUserData(g_parser, &test_data); + XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS); +@@ -6448,6 +6540,25 @@ accumulate_entity_decl(void *userData, const XML_Char *entityName, + CharData_AppendXMLChars(storage, XCS("\n"), 1); } +typedef struct { @@ -1106,13 +1117,26 @@ index 7b6d9fb..0b5bcf1 100644 + } + } +} - - START_TEST(test_utf16_pe) - { -@@ -7423,6 +7555,176 @@ START_TEST(test_empty_element_abort) ++ + START_TEST(test_utf16_pe) { + /* '> +@@ -6901,11 +7012,6 @@ START_TEST(test_pool_integrity_with_unfinished_attr) { } END_TEST +-typedef struct { +- XML_Parser parser; +- CharData *storage; +-} ParserPlusStorage; +- + static void XMLCALL + accumulate_and_suspend_comment_handler(void *userData, const XML_Char *data) { + ParserPlusStorage *const parserPlusStorage = (ParserPlusStorage *)userData; +@@ -6913,6 +7019,147 @@ accumulate_and_suspend_comment_handler(void *userData, const XML_Char *data) { + XML_StopParser(parserPlusStorage->parser, XML_TRUE); + } + +/* Test a possible early return location in internalEntityProcessor */ +START_TEST(test_entity_ref_no_elements) { + const char *const text = "'>\n" +@@ -6943,6 +7190,35 @@ START_TEST(test_nested_entity_suspend) { + } + END_TEST + +START_TEST(test_nested_entity_suspend_2) { + const char *const text = "\n" @@ -1286,78 +1317,8 @@ index 7b6d9fb..0b5bcf1 100644 /* Regression test for quadratic parsing on large tokens */ START_TEST(test_big_tokens_take_linear_time) { const char *const too_slow_failure_message -@@ -7599,6 +7901,69 @@ struct element_decl_data { - int count; - }; - -+typedef struct { -+ XML_Parser parser; -+ int deep; -+} DataIssue240; -+ -+static void -+start_element_issue_240(void *userData, const XML_Char *UNUSED_P(name), -+ const XML_Char **UNUSED_P(atts)) { -+ DataIssue240 *mydata = (DataIssue240 *)userData; -+ mydata->deep++; -+} -+ -+static void -+end_element_issue_240(void *userData, const XML_Char *UNUSED_P(name)) { -+ DataIssue240 *mydata = (DataIssue240 *)userData; -+ mydata->deep--; -+ if (mydata->deep == 0) { -+ XML_StopParser(mydata->parser, 0); -+ } -+} -+ -+START_TEST(test_misc_stop_during_end_handler_issue_240_1) { -+ XML_Parser parser; -+ DataIssue240 *mydata; -+ enum XML_Status result; -+ const char *const doc1 = ""; -+ -+ parser = XML_ParserCreate(NULL); -+ XML_SetElementHandler(parser, start_element_issue_240, end_element_issue_240); -+ mydata = (DataIssue240 *)malloc(sizeof(DataIssue240)); -+ mydata->parser = parser; -+ mydata->deep = 0; -+ XML_SetUserData(parser, mydata); -+ -+ result = XML_Parse(parser, doc1, (int)strlen(doc1), 1); -+ XML_ParserFree(parser); -+ free(mydata); -+ if (result != XML_STATUS_ERROR) -+ fail("Stopping the parser did not work as expected"); -+} -+END_TEST -+ -+START_TEST(test_misc_stop_during_end_handler_issue_240_2) { -+ XML_Parser parser; -+ DataIssue240 *mydata; -+ enum XML_Status result; -+ const char *const doc2 = ""; -+ -+ parser = XML_ParserCreate(NULL); -+ XML_SetElementHandler(parser, start_element_issue_240, end_element_issue_240); -+ mydata = (DataIssue240 *)malloc(sizeof(DataIssue240)); -+ mydata->parser = parser; -+ mydata->deep = 0; -+ XML_SetUserData(parser, mydata); -+ -+ result = XML_Parse(parser, doc2, (int)strlen(doc2), 1); -+ XML_ParserFree(parser); -+ free(mydata); -+ if (result != XML_STATUS_ERROR) -+ fail("Stopping the parser did not work as expected"); -+} -+END_TEST -+ - static void - element_decl_counter(void *userData, const XML_Char *UNUSED_P(name), XML_Content *model) { - struct element_decl_data *testdata = (struct element_decl_data *)userData; -@@ -8939,6 +9304,29 @@ static void *duff_reallocator(void *ptr, size_t size) - return realloc(ptr, size); +@@ -8349,6 +8625,29 @@ duff_reallocator(void *ptr, size_t size) { + return realloc(ptr, size); } +// Portable remake of strndup(3) for C99; does not care about space efficiency @@ -1384,9 +1345,9 @@ index 7b6d9fb..0b5bcf1 100644 +} + /* Test that a failure to allocate the parser structure fails gracefully */ - START_TEST(test_misc_alloc_create_parser) - { -@@ -9174,7 +9562,7 @@ END_TEST + START_TEST(test_misc_alloc_create_parser) { + XML_Memory_Handling_Suite memsuite = {duff_allocator, realloc, free}; +@@ -8723,7 +9022,7 @@ 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++) { @@ -1395,7 +1356,7 @@ index 7b6d9fb..0b5bcf1 100644 XML_Parser parser = XML_ParserCreate(NULL); assert_true(XML_GetErrorCode(parser) == XML_ERROR_NONE); assert_true(XML_StopParser(parser, resumable) == XML_STATUS_ERROR); -@@ -9184,6 +9572,105 @@ START_TEST(test_misc_stopparser_rejects_unstarted_parser) { +@@ -8733,6 +9032,105 @@ START_TEST(test_misc_stopparser_rejects_unstarted_parser) { } END_TEST @@ -1499,12 +1460,12 @@ index 7b6d9fb..0b5bcf1 100644 +END_TEST + static void - alloc_setup(void) - { -@@ -9809,6 +10296,31 @@ START_TEST(test_alloc_internal_entity) + alloc_setup(void) { + XML_Memory_Handling_Suite memsuite = {duff_allocator, duff_reallocator, free}; +@@ -9311,6 +9709,31 @@ START_TEST(test_alloc_internal_entity) { + } END_TEST - +START_TEST(test_alloc_parameter_entity) { + const char *text = "\">" @@ -1515,8 +1476,8 @@ index 7b6d9fb..0b5bcf1 100644 + + for (i = 0; i < alloc_test_max_repeats; i++) { + allocation_count = i; -+ XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); -+ if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE) ++ XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS); ++ if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE) + != XML_STATUS_ERROR) + break; + alloc_teardown(); @@ -1533,63 +1494,42 @@ index 7b6d9fb..0b5bcf1 100644 /* Test the robustness against allocation failure of element handling * Based on test_dtd_default_handling(). */ -@@ -13114,6 +13626,7 @@ make_suite(void) - tcase_add_test(tc_basic, - test_entity_with_external_subset_unless_standalone); - tcase_add_test(tc_basic, test_wfc_no_recursive_entity_refs); -+ tcase_add_test(tc_basic, test_no_indirectly_recursive_entity_refs); - tcase_add_test(tc_basic, test_ext_entity_set_encoding); - tcase_add_test(tc_basic, test_ext_entity_no_handler); - tcase_add_test(tc_basic, test_ext_entity_set_bom); -@@ -13203,6 +13716,7 @@ make_suite(void) - tcase_add_test(tc_basic, test_abort_epilog); - tcase_add_test(tc_basic, test_abort_epilog_2); - tcase_add_test(tc_basic, test_suspend_epilog); -+ tcase_add_test(tc_basic, test_suspend_in_sole_empty_tag); - tcase_add_test(tc_basic, test_unfinished_epilog); - tcase_add_test(tc_basic, test_partial_char_in_epilog); - tcase_add_test(tc_basic, test_hash_collision); -@@ -13287,6 +13801,14 @@ make_suite(void) - tcase_add_test(tc_basic, test_bad_notation); - tcase_add_test(tc_basic, test_default_doctype_handler); - tcase_add_test(tc_basic, test_empty_element_abort); -+#ifdef XML_DTD -+ tcase_add_test(tc_basic, test_entity_ref_no_elements); -+ tcase_add_test(tc_basic, test_deep_nested_entity); -+ tcase_add_test(tc_basic, test_deep_nested_attribute_entity); -+ tcase_add_test(tc_basic, -+ test_deep_nested_entity_delayed_interpretation); -+ tcase_add_test(tc_basic, test_nested_entity_suspend_2); -+#endif - tcase_add_test(tc_basic, test_big_tokens_take_linear_time); - tcase_add_test(tc_basic, test_set_reparse_deferral); - tcase_add_test(tc_basic, test_reparse_deferral_is_inherited); -@@ -13343,12 +13865,18 @@ 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); -+ tcase_add_test(tc_misc, test_misc_stop_during_end_handler_issue_240_1); -+ tcase_add_test(tc_misc, test_misc_stop_during_end_handler_issue_240_2); - #ifdef XML_DTD - tcase_add_test(tc_misc, - test_misc_deny_internal_entity_closing_doctype_issue_317); - #endif - tcase_add_test(tc_misc, test_misc_resumeparser_not_crashing); - tcase_add_test(tc_misc, test_misc_stopparser_rejects_unstarted_parser); -+#ifdef XML_DTD -+ tcase_add_test(tc_misc, test_renter_loop_finite_content); -+#endif -+ tcase_add_test(tc_misc, test_misc_expected_event_ptr_issue_980); +@@ -12955,6 +13378,7 @@ make_suite(void) { + test_wfc_undeclared_entity_with_external_subset_standalone); + tcase_add_test(tc_basic, test_entity_with_external_subset_unless_standalone); + tcase_add_test(tc_basic, test_wfc_no_recursive_entity_refs); ++ tcase_add_test(tc_basic, test_no_indirectly_recursive_entity_refs); + tcase_add_test__ifdef_xml_dtd(tc_basic, test_ext_entity_set_encoding); + tcase_add_test__ifdef_xml_dtd(tc_basic, test_ext_entity_no_handler); + tcase_add_test__ifdef_xml_dtd(tc_basic, test_ext_entity_set_bom); +@@ -13137,7 +13561,13 @@ make_suite(void) { + tcase_add_test(tc_basic, test_empty_element_abort); + tcase_add_test__ifdef_xml_dtd(tc_basic, + test_pool_integrity_with_unfinished_attr); ++ tcase_add_test__ifdef_xml_dtd(tc_basic, test_entity_ref_no_elements); ++ tcase_add_test__ifdef_xml_dtd(tc_basic, test_deep_nested_entity); ++ tcase_add_test__ifdef_xml_dtd(tc_basic, test_deep_nested_attribute_entity); ++ tcase_add_test__ifdef_xml_dtd(tc_basic, ++ test_deep_nested_entity_delayed_interpretation); + tcase_add_test(tc_basic, test_nested_entity_suspend); ++ tcase_add_test__ifdef_xml_dtd(tc_basic, test_nested_entity_suspend_2); + tcase_add_test(tc_basic, test_big_tokens_take_linear_time); + tcase_add_test(tc_basic, test_set_reparse_deferral); + tcase_add_test(tc_basic, test_reparse_deferral_is_inherited); +@@ -13200,6 +13630,8 @@ make_suite(void) { + 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); ++ tcase_add_test__ifdef_xml_dtd(tc_misc, test_renter_loop_finite_content); ++ tcase_add_test(tc_misc, test_misc_expected_event_ptr_issue_980); - suite_add_tcase(s, tc_alloc); - tcase_add_checked_fixture(tc_alloc, alloc_setup, alloc_teardown); -@@ -13365,6 +13893,9 @@ make_suite(void) - tcase_add_test(tc_alloc, test_alloc_external_entity); - tcase_add_test(tc_alloc, test_alloc_ext_entity_set_encoding); - tcase_add_test(tc_alloc, test_alloc_internal_entity); -+#ifdef XML_DTD -+ tcase_add_test(tc_alloc, test_alloc_parameter_entity); -+#endif - tcase_add_test(tc_alloc, test_alloc_dtd_default_handling); - tcase_add_test(tc_alloc, test_alloc_explicit_encoding); - tcase_add_test(tc_alloc, test_alloc_set_base); + suite_add_tcase(s, tc_alloc); + tcase_add_checked_fixture(tc_alloc, alloc_setup, alloc_teardown); +@@ -13216,6 +13648,7 @@ make_suite(void) { + tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_external_entity); + tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_ext_entity_set_encoding); + tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_internal_entity); ++ tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_parameter_entity); + tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_dtd_default_handling); + tcase_add_test(tc_alloc, test_alloc_explicit_encoding); + tcase_add_test(tc_alloc, test_alloc_set_base); diff --git a/expat-2.5.0-CVE-2025-59375.patch b/expat-2.5.0-CVE-2025-59375.patch new file mode 100644 index 0000000..644cd09 --- /dev/null +++ b/expat-2.5.0-CVE-2025-59375.patch @@ -0,0 +1,1526 @@ +commit e16f2e3d9c3fa181dcaeed312ea9bc7de8257942 +Author: Tomas Korbar +Date: Tue Nov 18 13:01:31 2025 +0100 + + Fix CVE-2025-59375 + +diff --git a/expat/doc/reference.html b/expat/doc/reference.html +index d618bd8..3358ecc 100644 +--- a/expat/doc/reference.html ++++ b/expat/doc/reference.html +@@ -155,6 +155,8 @@ interface.

    + +
  • +@@ -1831,7 +1833,7 @@ struct XML_cp { +

    Sets a handler for element declarations in a DTD. The handler gets + called with the name of the element in the declaration and a pointer + to a structure that contains the element model. It's the user code's +-responsibility to free model when finished with it. See ++responsibility to free model when finished with via a call to + XML_FreeContentModel. + There is no need to free the model from the handler, it can be kept + around and freed at a later stage.

    +@@ -2193,6 +2195,120 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold(XML_Parser p, +

    + + ++

    XML_SetAllocTrackerMaximumAmplification

    ++
    ++/* Added in Expat 2.7.2. */
    ++XML_Bool
    ++XML_SetAllocTrackerMaximumAmplification(XML_Parser p,
    ++                                        float maximumAmplificationFactor);
    ++
    ++
    ++

    ++ Sets the maximum tolerated amplification factor ++ between direct input and bytes of dynamic memory allocated ++ (default: 100.0) ++ of parser p to maximumAmplificationFactor, and ++ returns XML_TRUE upon success and XML_FALSE upon error. ++

    ++ ++

    ++ Note: ++ There are three types of allocations that intentionally bypass tracking and limiting: ++

    ++ ++ ++

    The amplification factor is calculated as ..

    ++
    amplification := allocated / direct
    ++

    ++ .. while parsing, whereas ++ direct is the number of bytes read from the primary document in parsing and ++ allocated is the number of bytes of dynamic memory allocated in the parser hierarchy. ++

    ++ ++

    For a call to XML_SetAllocTrackerMaximumAmplification to succeed:

    ++
      ++
    • parser p must be a non-NULL root parser (without any parent parsers) and
    • ++
    • maximumAmplificationFactor must be non-NaN and greater than or equal to 1.0.
    • ++
    ++ ++

    ++ Note: ++ If you ever need to increase this value for non-attack payload, ++ please file a bug report. ++

    ++ ++

    ++ Note: ++ Amplifications factors greater than 100 can been observed near the start of parsing ++ even with benign files in practice. ++ ++ So if you do reduce the maximum allowed amplification, ++ please make sure that the activation threshold is still big enough ++ to not end up with undesired false positives (i.e. benign files being rejected). ++

    ++
    ++ ++

    XML_SetAllocTrackerActivationThreshold

    ++
    ++/* Added in Expat 2.7.2. */
    ++XML_Bool
    ++XML_SetAllocTrackerActivationThreshold(XML_Parser p,
    ++                                       unsigned long long activationThresholdBytes);
    ++
    ++
    ++

    ++ Sets number of allocated bytes of dynamic memory ++ needed to activate protection against disproportionate use of RAM ++ (default: 64 MiB) ++ of parser p to activationThresholdBytes, and ++ returns XML_TRUE upon success and XML_FALSE upon error. ++

    ++ ++

    ++ Note: ++ For types of allocations that intentionally bypass tracking and limiting, please see ++ XML_SetAllocTrackerMaximumAmplification ++ above. ++

    ++ ++

    For a call to XML_SetAllocTrackerActivationThreshold to succeed:

    ++
      ++
    • parser p must be a non-NULL root parser (without any parent parsers).
    • ++
    ++ ++

    ++ Note: ++ If you ever need to increase this value for non-attack payload, ++ please file a bug report. ++

    ++
    ++ +

    XML_SetReparseDeferralEnabled

    +
    + /* Added in Expat 2.6.0. */
    +diff --git a/expat/doc/xmlwf.xml b/expat/doc/xmlwf.xml
    +index 3d35393..aeb2dc0 100644
    +--- a/expat/doc/xmlwf.xml
    ++++ b/expat/doc/xmlwf.xml
    +@@ -152,19 +152,31 @@ supports both.
    +         
    +           
    +             Sets the maximum tolerated amplification factor
    +-            for protection against billion laughs attacks (default: 100.0).
    ++            for protection against amplification attacks
    ++            like the billion laughs attack
    ++            (default: 100.0
    ++            for the sum of direct and indirect output and also
    ++            for allocations of dynamic memory).
    +             The amplification factor is calculated as ..
    +           
    +           
    +             amplification := (direct + indirect) / direct
    +           
    +           
    +-            .. while parsing, whereas
    ++            .. with regard to use of entities and ..
    ++          
    ++          
    ++            amplification := allocated / direct
    ++          
    ++          
    ++            .. with regard to dynamic memory while parsing.
    +             <direct> is the number of bytes read
    +-              from the primary document in parsing and
    ++              from the primary document in parsing,
    +             <indirect> is the number of bytes
    +               added by expanding entities and reading of external DTD files,
    +-              combined.
    ++              combined, and
    ++            <allocated> is the total number of bytes of dynamic memory
    ++              allocated (and not freed) per hierarchy of parsers.
    +           
    +           
    +             NOTE:
    +@@ -179,8 +191,10 @@ supports both.
    +         
    +           
    +             Sets the number of output bytes (including amplification)
    +-            needed to activate protection against billion laughs attacks
    +-            (default: 8 MiB).
    ++            needed to activate protection against amplification attacks
    ++            like billion laughs
    ++            (default: 8 MiB for the sum of direct and indirect output,
    ++            and 64 MiB for allocations of dynamic memory).
    +             This can be thought of as an "activation threshold".
    +           
    +           
    +diff --git a/expat/lib/expat.h b/expat/lib/expat.h
    +index 69b0ba1..6eae1d6 100644
    +--- a/expat/lib/expat.h
    ++++ b/expat/lib/expat.h
    +@@ -1028,8 +1028,11 @@ enum XML_FeatureEnum {
    +   XML_FEATURE_ATTR_INFO,
    +   /* Added in Expat 2.4.0. */
    +   XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT,
    +-  XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT
    ++  XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT,
    +   /* Additional features must be added to the end of this enum. */
    ++  /* Added in Expat 2.7.2. */
    ++  XML_FEATURE_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT,
    ++  XML_FEATURE_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT,
    + };
    + 
    + typedef struct {
    +@@ -1051,6 +1054,16 @@ XML_SetBillionLaughsAttackProtectionMaximumAmplification(
    + XMLPARSEAPI(XML_Bool)
    + XML_SetBillionLaughsAttackProtectionActivationThreshold(
    +     XML_Parser parser, unsigned long long activationThresholdBytes);
    ++
    ++/* Added in Expat 2.7.2. */
    ++XMLPARSEAPI(XML_Bool)
    ++XML_SetAllocTrackerMaximumAmplification(XML_Parser parser,
    ++                                        float maximumAmplificationFactor);
    ++
    ++/* Added in Expat 2.7.2. */
    ++XMLPARSEAPI(XML_Bool)
    ++XML_SetAllocTrackerActivationThreshold(
    ++    XML_Parser parser, unsigned long long activationThresholdBytes);
    + #endif
    + 
    + /* Added in Expat 2.6.0. */
    +diff --git a/expat/lib/internal.h b/expat/lib/internal.h
    +index e2709c8..1d1e7da 100644
    +--- a/expat/lib/internal.h
    ++++ b/expat/lib/internal.h
    +@@ -107,6 +107,7 @@
    + #endif
    + 
    + #include  // ULONG_MAX
    ++#include  // size_t
    + 
    + #if defined(_WIN32)                                                            \
    +     && (! defined(__USE_MINGW_ANSI_STDIO)                                      \
    +@@ -144,6 +145,16 @@
    +   100.0f
    + #define EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT    \
    +   8388608 // 8 MiB, 2^23
    ++
    ++#define EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT 100.0f
    ++#define EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT                       \
    ++  67108864 // 64 MiB, 2^26
    ++
    ++// NOTE: If function expat_alloc was user facing, EXPAT_MALLOC_ALIGNMENT would
    ++//       have to take sizeof(long double) into account
    ++#define EXPAT_MALLOC_ALIGNMENT sizeof(long long) // largest parser (sub)member
    ++#define EXPAT_MALLOC_PADDING ((EXPAT_MALLOC_ALIGNMENT) - sizeof(size_t))
    ++
    + /* NOTE END */
    + 
    + #include "expat.h" // so we can use type XML_Parser below
    +@@ -163,6 +174,9 @@ const char *unsignedCharToPrintable(unsigned char c);
    + 
    + extern XML_Bool g_reparseDeferralEnabledDefault; // written ONLY in runtests.c
    + extern unsigned int g_parseAttempts;             // used for testing only
    ++void *expat_malloc(XML_Parser parser, size_t size, int sourceLine);
    ++void expat_free(XML_Parser parser, void *ptr, int sourceLine);
    ++void *expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine);
    + 
    + #ifdef __cplusplus
    + }
    +diff --git a/expat/lib/xmlparse.c b/expat/lib/xmlparse.c
    +index d68d2c8..cba41f4 100644
    +--- a/expat/lib/xmlparse.c
    ++++ b/expat/lib/xmlparse.c
    +@@ -214,7 +214,7 @@ typedef struct {
    +   unsigned char power;
    +   size_t size;
    +   size_t used;
    +-  const XML_Memory_Handling_Suite *mem;
    ++  XML_Parser parser;
    + } HASH_TABLE;
    + 
    + static size_t keylen(KEY s);
    +@@ -337,7 +337,7 @@ typedef struct {
    +   const XML_Char *end;
    +   XML_Char *ptr;
    +   XML_Char *start;
    +-  const XML_Memory_Handling_Suite *mem;
    ++  XML_Parser parser;
    + } STRING_POOL;
    + 
    + /* The XML_Char before the name is used to determine whether
    +@@ -432,6 +432,14 @@ typedef struct accounting {
    +   unsigned long long activationThresholdBytes;
    + } ACCOUNTING;
    + 
    ++typedef struct MALLOC_TRACKER {
    ++  XmlBigCount bytesAllocated;
    ++  XmlBigCount peakBytesAllocated; // updated live only for debug level >=2
    ++  unsigned long debugLevel;
    ++  float maximumAmplificationFactor; // >=1.0
    ++  XmlBigCount activationThresholdBytes;
    ++} MALLOC_TRACKER;
    ++
    + typedef struct entity_stats {
    +   unsigned int countEverOpened;
    +   unsigned int currentDepth;
    +@@ -530,26 +538,23 @@ static XML_Bool setContext(XML_Parser parser, const XML_Char *context);
    + 
    + static void FASTCALL normalizePublicId(XML_Char *s);
    + 
    +-static DTD *dtdCreate(const XML_Memory_Handling_Suite *ms);
    ++static DTD *dtdCreate(XML_Parser parser);
    + /* do not call if m_parentParser != NULL */
    +-static void dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms);
    +-static void dtdDestroy(DTD *p, XML_Bool isDocEntity,
    +-                       const XML_Memory_Handling_Suite *ms);
    ++static void dtdReset(DTD *p, XML_Parser parser);
    ++static void dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser);
    + static int dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd,
    +-                   const XML_Memory_Handling_Suite *ms);
    ++                   XML_Parser parser);
    + static int copyEntityTable(XML_Parser oldParser, HASH_TABLE *, STRING_POOL *,
    +                            const HASH_TABLE *);
    + static NAMED *lookup(XML_Parser parser, HASH_TABLE *table, KEY name,
    +                      size_t createSize);
    +-static void FASTCALL hashTableInit(HASH_TABLE *,
    +-                                   const XML_Memory_Handling_Suite *ms);
    ++static void FASTCALL hashTableInit(HASH_TABLE *table, XML_Parser parser);
    + static void FASTCALL hashTableClear(HASH_TABLE *);
    + static void FASTCALL hashTableDestroy(HASH_TABLE *);
    + static void FASTCALL hashTableIterInit(HASH_TABLE_ITER *, const HASH_TABLE *);
    + static NAMED *FASTCALL hashTableIterNext(HASH_TABLE_ITER *);
    + 
    +-static void FASTCALL poolInit(STRING_POOL *,
    +-                              const XML_Memory_Handling_Suite *ms);
    ++static void FASTCALL poolInit(STRING_POOL *pool, XML_Parser parser);
    + static void FASTCALL poolClear(STRING_POOL *);
    + static void FASTCALL poolDestroy(STRING_POOL *);
    + static XML_Char *poolAppend(STRING_POOL *pool, const ENCODING *enc,
    +@@ -569,15 +574,15 @@ static XML_Content *build_model(XML_Parser parser);
    + static ELEMENT_TYPE *getElementType(XML_Parser parser, const ENCODING *enc,
    +                                     const char *ptr, const char *end);
    + 
    +-static XML_Char *copyString(const XML_Char *s,
    +-                            const XML_Memory_Handling_Suite *memsuite);
    ++static XML_Char *copyString(const XML_Char *s, XML_Parser parser);
    + 
    + static unsigned long generate_hash_secret_salt(XML_Parser parser);
    + static XML_Bool startParsing(XML_Parser parser);
    + 
    + static XML_Parser parserCreate(const XML_Char *encodingName,
    +                                const XML_Memory_Handling_Suite *memsuite,
    +-                               const XML_Char *nameSep, DTD *dtd);
    ++                               const XML_Char *nameSep, DTD *dtd,
    ++                               XML_Parser parentParser);
    + 
    + static void parserInit(XML_Parser parser, const XML_Char *encodingName);
    + 
    +@@ -734,14 +739,215 @@ struct XML_ParserStruct {
    +   unsigned long m_hash_secret_salt;
    + #ifdef XML_DTD
    +   ACCOUNTING m_accounting;
    ++  MALLOC_TRACKER m_alloc_tracker;
    +   ENTITY_STATS m_entity_stats;
    + #endif
    +   XML_Bool m_reenter;
    + };
    + 
    +-#define MALLOC(parser, s) (parser->m_mem.malloc_fcn((s)))
    +-#define REALLOC(parser, p, s) (parser->m_mem.realloc_fcn((p), (s)))
    +-#define FREE(parser, p) (parser->m_mem.free_fcn((p)))
    ++#  define MALLOC(parser, s) (expat_malloc((parser), (s), __LINE__))
    ++#  define REALLOC(parser, p, s) (expat_realloc((parser), (p), (s), __LINE__))
    ++#  define FREE(parser, p) (expat_free((parser), (p), __LINE__))
    ++
    ++static void
    ++expat_heap_stat(XML_Parser rootParser, char operator, XmlBigCount absDiff,
    ++                XmlBigCount newTotal, XmlBigCount peakTotal, int sourceLine) {
    ++  // NOTE: This can be +infinity or -nan
    ++  const float amplification
    ++      = (float)newTotal / (float)rootParser->m_accounting.countBytesDirect;
    ++  fprintf(
    ++      stderr,
    ++      "expat: Allocations(%p): Direct " EXPAT_FMT_ULL("10") ", allocated %c" EXPAT_FMT_ULL(
    ++          "10") " to " EXPAT_FMT_ULL("10") " (" EXPAT_FMT_ULL("10") " peak), amplification %8.2f (xmlparse.c:%d)\n",
    ++      (void *)rootParser, rootParser->m_accounting.countBytesDirect, operator,
    ++      absDiff, newTotal, peakTotal, (double)amplification, sourceLine);
    ++}
    ++
    ++static bool
    ++expat_heap_increase_tolerable(XML_Parser rootParser, XmlBigCount increase,
    ++                              int sourceLine) {
    ++  assert(rootParser != NULL);
    ++  assert(increase > 0);
    ++
    ++  XmlBigCount newTotal = 0;
    ++  bool tolerable = true;
    ++
    ++  // Detect integer overflow
    ++  if ((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated < increase) {
    ++    tolerable = false;
    ++  } else {
    ++    newTotal = rootParser->m_alloc_tracker.bytesAllocated + increase;
    ++
    ++    if (newTotal >= rootParser->m_alloc_tracker.activationThresholdBytes) {
    ++      assert(newTotal > 0);
    ++      // NOTE: This can be +infinity when dividing by zero but not -nan
    ++      const float amplification
    ++          = (float)newTotal / (float)rootParser->m_accounting.countBytesDirect;
    ++      if (amplification
    ++          > rootParser->m_alloc_tracker.maximumAmplificationFactor) {
    ++        tolerable = false;
    ++      }
    ++    }
    ++  }
    ++
    ++  if (! tolerable && (rootParser->m_alloc_tracker.debugLevel >= 1)) {
    ++    expat_heap_stat(rootParser, '+', increase, newTotal, newTotal, sourceLine);
    ++  }
    ++
    ++  return tolerable;
    ++}
    ++
    ++void *expat_malloc(XML_Parser parser, size_t size, int sourceLine) {
    ++  // Detect integer overflow
    ++  if (SIZE_MAX - size < sizeof(size_t) + EXPAT_MALLOC_PADDING) {
    ++    return NULL;
    ++  }
    ++
    ++  const XML_Parser rootParser = getRootParserOf(parser, NULL);
    ++  assert(rootParser->m_parentParser == NULL);
    ++
    ++  const size_t bytesToAllocate = sizeof(size_t) + EXPAT_MALLOC_PADDING + size;
    ++
    ++  if ((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated
    ++      < bytesToAllocate) {
    ++    return NULL; // i.e. signal integer overflow as out-of-memory
    ++  }
    ++
    ++  if (! expat_heap_increase_tolerable(rootParser, bytesToAllocate,
    ++                                      sourceLine)) {
    ++    return NULL; // i.e. signal violation as out-of-memory
    ++  }
    ++
    ++  // Actually allocate
    ++  void *const mallocedPtr = parser->m_mem.malloc_fcn(bytesToAllocate);
    ++
    ++  if (mallocedPtr == NULL) {
    ++    return NULL;
    ++  }
    ++
    ++  // Update in-block recorded size
    ++  *(size_t *)mallocedPtr = size;
    ++
    ++  // Update accounting
    ++  rootParser->m_alloc_tracker.bytesAllocated += bytesToAllocate;
    ++
    ++  // Report as needed
    ++  if (rootParser->m_alloc_tracker.debugLevel >= 2) {
    ++    if (rootParser->m_alloc_tracker.bytesAllocated
    ++        > rootParser->m_alloc_tracker.peakBytesAllocated) {
    ++      rootParser->m_alloc_tracker.peakBytesAllocated
    ++          = rootParser->m_alloc_tracker.bytesAllocated;
    ++    }
    ++    expat_heap_stat(rootParser, '+', bytesToAllocate,
    ++                    rootParser->m_alloc_tracker.bytesAllocated,
    ++                    rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine);
    ++  }
    ++
    ++  return (char *)mallocedPtr + sizeof(size_t) + EXPAT_MALLOC_PADDING;
    ++}
    ++
    ++void expat_free(XML_Parser parser, void *ptr, int sourceLine) {
    ++  assert(parser != NULL);
    ++
    ++  if (ptr == NULL) {
    ++    return;
    ++  }
    ++
    ++  const XML_Parser rootParser = getRootParserOf(parser, NULL);
    ++  assert(rootParser->m_parentParser == NULL);
    ++
    ++  // Extract size (to the eyes of malloc_fcn/realloc_fcn) and
    ++  // the original pointer returned by malloc/realloc
    ++  void *const mallocedPtr = (char *)ptr - EXPAT_MALLOC_PADDING - sizeof(size_t);
    ++  const size_t bytesAllocated
    ++      = sizeof(size_t) + EXPAT_MALLOC_PADDING + *(size_t *)mallocedPtr;
    ++
    ++  // Update accounting
    ++  assert(rootParser->m_alloc_tracker.bytesAllocated >= bytesAllocated);
    ++  rootParser->m_alloc_tracker.bytesAllocated -= bytesAllocated;
    ++
    ++  // Report as needed
    ++  if (rootParser->m_alloc_tracker.debugLevel >= 2) {
    ++    expat_heap_stat(rootParser, '-', bytesAllocated,
    ++                    rootParser->m_alloc_tracker.bytesAllocated,
    ++                    rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine);
    ++  }
    ++
    ++  // NOTE: This may be freeing rootParser, so freeing has to come last
    ++  parser->m_mem.free_fcn(mallocedPtr);
    ++}
    ++
    ++void *expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine) {
    ++  assert(parser != NULL);
    ++
    ++  if (ptr == NULL) {
    ++    return expat_malloc(parser, size, sourceLine);
    ++  }
    ++
    ++  if (size == 0) {
    ++    expat_free(parser, ptr, sourceLine);
    ++    return NULL;
    ++  }
    ++
    ++  const XML_Parser rootParser = getRootParserOf(parser, NULL);
    ++  assert(rootParser->m_parentParser == NULL);
    ++
    ++  // Extract original size (to the eyes of the caller) and the original
    ++  // pointer returned by malloc/realloc
    ++  void *mallocedPtr = (char *)ptr - EXPAT_MALLOC_PADDING - sizeof(size_t);
    ++  const size_t prevSize = *(size_t *)mallocedPtr;
    ++
    ++  // Classify upcoming change
    ++  const bool isIncrease = (size > prevSize);
    ++  const size_t absDiff
    ++      = (size > prevSize) ? (size - prevSize) : (prevSize - size);
    ++
    ++  // Ask for permission from accounting
    ++  if (isIncrease) {
    ++    if (! expat_heap_increase_tolerable(rootParser, absDiff, sourceLine)) {
    ++      return NULL; // i.e. signal violation as out-of-memory
    ++    }
    ++  }
    ++
    ++  // NOTE: Integer overflow detection has already been done for us
    ++  //       by expat_heap_increase_tolerable(..) above
    ++  assert(SIZE_MAX - sizeof(size_t) - EXPAT_MALLOC_PADDING >= size);
    ++
    ++  // Actually allocate
    ++  mallocedPtr = parser->m_mem.realloc_fcn(
    ++      mallocedPtr, sizeof(size_t) + EXPAT_MALLOC_PADDING + size);
    ++
    ++  if (mallocedPtr == NULL) {
    ++    return NULL;
    ++  }
    ++
    ++  // Update accounting
    ++  if (isIncrease) {
    ++    assert((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated
    ++           >= absDiff);
    ++    rootParser->m_alloc_tracker.bytesAllocated += absDiff;
    ++  } else { // i.e. decrease
    ++    assert(rootParser->m_alloc_tracker.bytesAllocated >= absDiff);
    ++    rootParser->m_alloc_tracker.bytesAllocated -= absDiff;
    ++  }
    ++
    ++  // Report as needed
    ++  if (rootParser->m_alloc_tracker.debugLevel >= 2) {
    ++    if (rootParser->m_alloc_tracker.bytesAllocated
    ++        > rootParser->m_alloc_tracker.peakBytesAllocated) {
    ++      rootParser->m_alloc_tracker.peakBytesAllocated
    ++          = rootParser->m_alloc_tracker.bytesAllocated;
    ++    }
    ++    expat_heap_stat(rootParser, isIncrease ? '+' : '-', absDiff,
    ++                    rootParser->m_alloc_tracker.bytesAllocated,
    ++                    rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine);
    ++  }
    ++
    ++  // Update in-block recorded size
    ++  *(size_t *)mallocedPtr = size;
    ++
    ++  return (char *)mallocedPtr + sizeof(size_t) + EXPAT_MALLOC_PADDING;
    ++}
    + 
    + XML_Parser XMLCALL
    + XML_ParserCreate(const XML_Char *encodingName) {
    +@@ -1059,19 +1265,34 @@ XML_Parser XMLCALL
    + XML_ParserCreate_MM(const XML_Char *encodingName,
    +                     const XML_Memory_Handling_Suite *memsuite,
    +                     const XML_Char *nameSep) {
    +-  return parserCreate(encodingName, memsuite, nameSep, NULL);
    ++  return parserCreate(encodingName, memsuite, nameSep, NULL, NULL);
    + }
    + 
    + static XML_Parser
    + parserCreate(const XML_Char *encodingName,
    +              const XML_Memory_Handling_Suite *memsuite, const XML_Char *nameSep,
    +-             DTD *dtd) {
    +-  XML_Parser parser;
    ++             DTD *dtd, XML_Parser parentParser) {
    ++  XML_Parser parser = NULL;
    ++
    ++  const size_t increase
    ++      = sizeof(size_t) + EXPAT_MALLOC_PADDING + sizeof(struct XML_ParserStruct);
    ++
    ++  if (parentParser != NULL) {
    ++    const XML_Parser rootParser = getRootParserOf(parentParser, NULL);
    ++    if (! expat_heap_increase_tolerable(rootParser, increase, __LINE__)) {
    ++      return NULL;
    ++    }
    ++  }
    + 
    +   if (memsuite) {
    +     XML_Memory_Handling_Suite *mtemp;
    +-    parser = memsuite->malloc_fcn(sizeof(struct XML_ParserStruct));
    +-    if (parser != NULL) {
    ++    void *const sizeAndParser
    ++        = memsuite->malloc_fcn(sizeof(size_t) + EXPAT_MALLOC_PADDING
    ++                               + sizeof(struct XML_ParserStruct));
    ++    if (sizeAndParser != NULL) {
    ++      *(size_t *)sizeAndParser = sizeof(struct XML_ParserStruct);
    ++      parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t)
    ++                            + EXPAT_MALLOC_PADDING);
    +       mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem);
    +       mtemp->malloc_fcn = memsuite->malloc_fcn;
    +       mtemp->realloc_fcn = memsuite->realloc_fcn;
    +@@ -1079,18 +1300,59 @@ parserCreate(const XML_Char *encodingName,
    +     }
    +   } else {
    +     XML_Memory_Handling_Suite *mtemp;
    +-    parser = (XML_Parser)malloc(sizeof(struct XML_ParserStruct));
    +-    if (parser != NULL) {
    ++    void *const sizeAndParser = malloc(sizeof(size_t) + EXPAT_MALLOC_PADDING
    ++                                       + sizeof(struct XML_ParserStruct));
    ++    if (sizeAndParser != NULL) {
    ++      *(size_t *)sizeAndParser = sizeof(struct XML_ParserStruct);
    ++      parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t)
    ++                            + EXPAT_MALLOC_PADDING);
    +       mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem);
    +       mtemp->malloc_fcn = malloc;
    +       mtemp->realloc_fcn = realloc;
    +       mtemp->free_fcn = free;
    +     }
    +-  }
    ++  } // cppcheck-suppress[memleak symbolName=sizeAndParser] // Cppcheck >=2.18.0
    + 
    +   if (! parser)
    +     return parser;
    + 
    ++  // Initialize .m_alloc_tracker
    ++  memset(&parser->m_alloc_tracker, 0, sizeof(MALLOC_TRACKER));
    ++  if (parentParser == NULL) {
    ++    parser->m_alloc_tracker.debugLevel
    ++        = getDebugLevel("EXPAT_MALLOC_DEBUG", 0u);
    ++    parser->m_alloc_tracker.maximumAmplificationFactor
    ++        = EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT;
    ++    parser->m_alloc_tracker.activationThresholdBytes
    ++        = EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT;
    ++
    ++    // NOTE: This initialization needs to come this early because these fields
    ++    //       are read by allocation tracking code
    ++    parser->m_parentParser = NULL;
    ++    parser->m_accounting.countBytesDirect = 0;
    ++  } else {
    ++    parser->m_parentParser = parentParser;
    ++  }
    ++
    ++  // Record XML_ParserStruct allocation we did a few lines up before
    ++  const XML_Parser rootParser = getRootParserOf(parser, NULL);
    ++  assert(rootParser->m_parentParser == NULL);
    ++  assert(SIZE_MAX - rootParser->m_alloc_tracker.bytesAllocated >= increase);
    ++  rootParser->m_alloc_tracker.bytesAllocated += increase;
    ++
    ++  // Report on allocation
    ++  if (rootParser->m_alloc_tracker.debugLevel >= 2) {
    ++    if (rootParser->m_alloc_tracker.bytesAllocated
    ++        > rootParser->m_alloc_tracker.peakBytesAllocated) {
    ++      rootParser->m_alloc_tracker.peakBytesAllocated
    ++          = rootParser->m_alloc_tracker.bytesAllocated;
    ++    }
    ++
    ++    expat_heap_stat(rootParser, '+', increase,
    ++                    rootParser->m_alloc_tracker.bytesAllocated,
    ++                    rootParser->m_alloc_tracker.peakBytesAllocated, __LINE__);
    ++  }
    ++
    +   parser->m_buffer = NULL;
    +   parser->m_bufferLim = NULL;
    + 
    +@@ -1125,7 +1387,7 @@ parserCreate(const XML_Char *encodingName,
    +   if (dtd)
    +     parser->m_dtd = dtd;
    +   else {
    +-    parser->m_dtd = dtdCreate(&parser->m_mem);
    ++    parser->m_dtd = dtdCreate(parser);
    +     if (parser->m_dtd == NULL) {
    +       FREE(parser, parser->m_dataBuf);
    +       FREE(parser, parser->m_atts);
    +@@ -1159,8 +1421,8 @@ parserCreate(const XML_Char *encodingName,
    + 
    +   parser->m_protocolEncodingName = NULL;
    + 
    +-  poolInit(&parser->m_tempPool, &(parser->m_mem));
    +-  poolInit(&parser->m_temp2Pool, &(parser->m_mem));
    ++  poolInit(&parser->m_tempPool, parser);
    ++  poolInit(&parser->m_temp2Pool, parser);
    +   parserInit(parser, encodingName);
    + 
    +   if (encodingName && ! parser->m_protocolEncodingName) {
    +@@ -1192,7 +1454,7 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) {
    +   parser->m_processor = prologInitProcessor;
    +   XmlPrologStateInit(&parser->m_prologState);
    +   if (encodingName != NULL) {
    +-    parser->m_protocolEncodingName = copyString(encodingName, &(parser->m_mem));
    ++    parser->m_protocolEncodingName = copyString(encodingName, parser);
    +   }
    +   parser->m_curBase = NULL;
    +   XmlInitEncoding(&parser->m_initEncoding, &parser->m_encoding, 0);
    +@@ -1254,7 +1516,6 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) {
    +   parser->m_unknownEncodingMem = NULL;
    +   parser->m_unknownEncodingRelease = NULL;
    +   parser->m_unknownEncodingData = NULL;
    +-  parser->m_parentParser = NULL;
    +   parser->m_parsingStatus.parsing = XML_INITIALIZED;
    +   // Reentry can only be triggered inside m_processor calls
    +   parser->m_reenter = XML_FALSE;
    +@@ -1344,7 +1605,7 @@ XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) {
    +   FREE(parser, (void *)parser->m_protocolEncodingName);
    +   parser->m_protocolEncodingName = NULL;
    +   parserInit(parser, encodingName);
    +-  dtdReset(parser->m_dtd, &parser->m_mem);
    ++  dtdReset(parser->m_dtd, parser);
    +   return XML_TRUE;
    + }
    + 
    +@@ -1380,7 +1641,7 @@ XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName) {
    +     parser->m_protocolEncodingName = NULL;
    +   else {
    +     /* Copy the new encoding name into allocated memory */
    +-    parser->m_protocolEncodingName = copyString(encodingName, &(parser->m_mem));
    ++    parser->m_protocolEncodingName = copyString(encodingName, parser);
    +     if (! parser->m_protocolEncodingName)
    +       return XML_STATUS_ERROR;
    +   }
    +@@ -1489,9 +1750,10 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context,
    +   */
    +   if (parser->m_ns) {
    +     XML_Char tmp[2] = {parser->m_namespaceSeparator, 0};
    +-    parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd);
    ++    parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd, oldParser);
    +   } else {
    +-    parser = parserCreate(encodingName, &parser->m_mem, NULL, newDtd);
    ++    parser
    ++        = parserCreate(encodingName, &parser->m_mem, NULL, newDtd, oldParser);
    +   }
    + 
    +   if (! parser)
    +@@ -1535,7 +1797,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context,
    +   parser->m_prologState.inEntityValue = oldInEntityValue;
    +   if (context) {
    + #endif /* XML_DTD */
    +-    if (! dtdCopy(oldParser, parser->m_dtd, oldDtd, &parser->m_mem)
    ++    if (! dtdCopy(oldParser, parser->m_dtd, oldDtd, parser)
    +         || ! setContext(parser, context)) {
    +       XML_ParserFree(parser);
    +       return NULL;
    +@@ -1647,14 +1909,16 @@ XML_ParserFree(XML_Parser parser) {
    + #else
    +   if (parser->m_dtd)
    + #endif /* XML_DTD */
    +-    dtdDestroy(parser->m_dtd, (XML_Bool)! parser->m_parentParser,
    +-               &parser->m_mem);
    ++    dtdDestroy(parser->m_dtd, (XML_Bool)! parser->m_parentParser, parser);
    +   FREE(parser, (void *)parser->m_atts);
    + #ifdef XML_ATTR_INFO
    +   FREE(parser, (void *)parser->m_attInfo);
    + #endif
    +   FREE(parser, parser->m_groupConnector);
    +-  FREE(parser, parser->m_buffer);
    ++  // NOTE: We are avoiding FREE(..) here because parser->m_buffer
    ++  //       is not being allocated with MALLOC(..) but with plain
    ++  //       .malloc_fcn(..).
    ++  parser->m_mem.free_fcn(parser->m_buffer);
    +   FREE(parser, parser->m_dataBuf);
    +   FREE(parser, parser->m_nsAtts);
    +   FREE(parser, parser->m_unknownEncodingMem);
    +@@ -2246,7 +2510,9 @@ XML_GetBuffer(XML_Parser parser, int len) {
    +         parser->m_errorCode = XML_ERROR_NO_MEMORY;
    +         return NULL;
    +       }
    +-      newBuf = (char *)MALLOC(parser, bufferSize);
    ++      // NOTE: We are avoiding MALLOC(..) here to leave limiting
    ++      //       the input size to the application using Expat.
    ++      newBuf = (char *)parser->m_mem.malloc_fcn(bufferSize);
    +       if (newBuf == 0) {
    +         parser->m_errorCode = XML_ERROR_NO_MEMORY;
    +         return NULL;
    +@@ -2257,7 +2523,10 @@ XML_GetBuffer(XML_Parser parser, int len) {
    +         memcpy(newBuf, &parser->m_bufferPtr[-keep],
    +                EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr)
    +                    + keep);
    +-        FREE(parser, parser->m_buffer);
    ++        // NOTE: We are avoiding FREE(..) here because parser->m_buffer
    ++        //       is not being allocated with MALLOC(..) but with plain
    ++        //       .malloc_fcn(..).
    ++        parser->m_mem.free_fcn(parser->m_buffer);
    +         parser->m_buffer = newBuf;
    +         parser->m_bufferEnd
    +             = parser->m_buffer
    +@@ -2273,7 +2542,10 @@ XML_GetBuffer(XML_Parser parser, int len) {
    +       if (parser->m_bufferPtr) {
    +         memcpy(newBuf, parser->m_bufferPtr,
    +                EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr));
    +-        FREE(parser, parser->m_buffer);
    ++        // NOTE: We are avoiding FREE(..) here because parser->m_buffer
    ++        //       is not being allocated with MALLOC(..) but with plain
    ++        //       .malloc_fcn(..).
    ++        parser->m_mem.free_fcn(parser->m_buffer);
    +         parser->m_bufferEnd
    +             = newBuf
    +               + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr);
    +@@ -2451,28 +2723,43 @@ XML_GetCurrentColumnNumber(XML_Parser parser) {
    + 
    + void XMLCALL
    + XML_FreeContentModel(XML_Parser parser, XML_Content *model) {
    +-  if (parser != NULL)
    +-    FREE(parser, model);
    ++  if (parser == NULL)
    ++    return;
    ++
    ++  // NOTE: We are avoiding FREE(..) here because the content model
    ++  //       has been created using plain .malloc_fcn(..) rather than MALLOC(..).
    ++  parser->m_mem.free_fcn(model);
    + }
    + 
    + void *XMLCALL
    + XML_MemMalloc(XML_Parser parser, size_t size) {
    +   if (parser == NULL)
    +     return NULL;
    +-  return MALLOC(parser, size);
    ++
    ++  // NOTE: We are avoiding MALLOC(..) here to not include
    ++  //       user allocations with allocation tracking and limiting.
    ++  return parser->m_mem.malloc_fcn(size);
    + }
    + 
    + void *XMLCALL
    + XML_MemRealloc(XML_Parser parser, void *ptr, size_t size) {
    +   if (parser == NULL)
    +     return NULL;
    +-  return REALLOC(parser, ptr, size);
    ++
    ++  // NOTE: We are avoiding REALLOC(..) here to not include
    ++  //       user allocations with allocation tracking and limiting.
    ++  return parser->m_mem.realloc_fcn(ptr, size);
    + }
    + 
    + void XMLCALL
    + XML_MemFree(XML_Parser parser, void *ptr) {
    +-  if (parser != NULL)
    +-    FREE(parser, ptr);
    ++  if (parser == NULL)
    ++    return;
    ++
    ++  // NOTE: We are avoiding FREE(..) here because XML_MemMalloc and
    ++  //       XML_MemRealloc are not using MALLOC(..) and REALLOC(..)
    ++  //       but plain .malloc_fcn(..) and .realloc_fcn(..), internally.
    ++  parser->m_mem.free_fcn(ptr);
    + }
    + 
    + void XMLCALL
    +@@ -2669,6 +2956,13 @@ XML_GetFeatureList(void) {
    +       {XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT,
    +        XML_L("XML_BLAP_ACT_THRES"),
    +        EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT},
    ++      /* Added in Expat 2.7.2. */
    ++      {XML_FEATURE_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT,
    ++       XML_L("XML_AT_MAX_AMP"),
    ++       (long int)EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT},
    ++      {XML_FEATURE_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT,
    ++       XML_L("XML_AT_ACT_THRES"),
    ++       (long int)EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT},
    + #endif
    +       {XML_FEATURE_END, NULL, 0}};
    + 
    +@@ -2697,6 +2991,29 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold(
    +   parser->m_accounting.activationThresholdBytes = activationThresholdBytes;
    +   return XML_TRUE;
    + }
    ++
    ++XML_Bool XMLCALL
    ++XML_SetAllocTrackerMaximumAmplification(XML_Parser parser,
    ++                                        float maximumAmplificationFactor) {
    ++  if ((parser == NULL) || (parser->m_parentParser != NULL)
    ++      || isnan(maximumAmplificationFactor)
    ++      || (maximumAmplificationFactor < 1.0f)) {
    ++    return XML_FALSE;
    ++  }
    ++  parser->m_alloc_tracker.maximumAmplificationFactor
    ++      = maximumAmplificationFactor;
    ++  return XML_TRUE;
    ++}
    ++
    ++XML_Bool XMLCALL
    ++XML_SetAllocTrackerActivationThreshold(
    ++    XML_Parser parser, unsigned long long activationThresholdBytes) {
    ++  if ((parser == NULL) || (parser->m_parentParser != NULL)) {
    ++    return XML_FALSE;
    ++  }
    ++  parser->m_alloc_tracker.activationThresholdBytes = activationThresholdBytes;
    ++  return XML_TRUE;
    ++}
    + #endif /* XML_DTD */
    + 
    + XML_Bool XMLCALL
    +@@ -5652,8 +5969,12 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
    +     case XML_ROLE_CONTENT_EMPTY:
    +       if (dtd->in_eldecl) {
    +         if (parser->m_elementDeclHandler) {
    ++          // NOTE: We are avoiding MALLOC(..) here to so that
    ++          //       applications that are not using XML_FreeContentModel but
    ++          //       plain free(..) or .free_fcn() to free the content model's
    ++          //       memory are safe.
    +           XML_Content *content
    +-              = (XML_Content *)MALLOC(parser, sizeof(XML_Content));
    ++              = (XML_Content *)parser->m_mem.malloc_fcn(sizeof(XML_Content));
    +           if (! content)
    +             return XML_ERROR_NO_MEMORY;
    +           content->quant = XML_CQUANT_NONE;
    +@@ -7009,19 +7330,19 @@ normalizePublicId(XML_Char *publicId) {
    + }
    + 
    + static DTD *
    +-dtdCreate(const XML_Memory_Handling_Suite *ms) {
    +-  DTD *p = ms->malloc_fcn(sizeof(DTD));
    ++dtdCreate(XML_Parser parser) {
    ++  DTD *p = MALLOC(parser, sizeof(DTD));
    +   if (p == NULL)
    +     return p;
    +-  poolInit(&(p->pool), ms);
    +-  poolInit(&(p->entityValuePool), ms);
    +-  hashTableInit(&(p->generalEntities), ms);
    +-  hashTableInit(&(p->elementTypes), ms);
    +-  hashTableInit(&(p->attributeIds), ms);
    +-  hashTableInit(&(p->prefixes), ms);
    ++  poolInit(&(p->pool), parser);
    ++  poolInit(&(p->entityValuePool), parser);
    ++  hashTableInit(&(p->generalEntities), parser);
    ++  hashTableInit(&(p->elementTypes), parser);
    ++  hashTableInit(&(p->attributeIds), parser);
    ++  hashTableInit(&(p->prefixes), parser);
    + #ifdef XML_DTD
    +   p->paramEntityRead = XML_FALSE;
    +-  hashTableInit(&(p->paramEntities), ms);
    ++  hashTableInit(&(p->paramEntities), parser);
    + #endif /* XML_DTD */
    +   p->defaultPrefix.name = NULL;
    +   p->defaultPrefix.binding = NULL;
    +@@ -7041,7 +7362,7 @@ dtdCreate(const XML_Memory_Handling_Suite *ms) {
    + }
    + 
    + static void
    +-dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) {
    ++dtdReset(DTD *p, XML_Parser parser) {
    +   HASH_TABLE_ITER iter;
    +   hashTableIterInit(&iter, &(p->elementTypes));
    +   for (;;) {
    +@@ -7049,7 +7370,7 @@ dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) {
    +     if (! e)
    +       break;
    +     if (e->allocDefaultAtts != 0)
    +-      ms->free_fcn(e->defaultAtts);
    ++      FREE(parser, e->defaultAtts);
    +   }
    +   hashTableClear(&(p->generalEntities));
    + #ifdef XML_DTD
    +@@ -7066,9 +7387,9 @@ dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) {
    + 
    +   p->in_eldecl = XML_FALSE;
    + 
    +-  ms->free_fcn(p->scaffIndex);
    ++  FREE(parser, p->scaffIndex);
    +   p->scaffIndex = NULL;
    +-  ms->free_fcn(p->scaffold);
    ++  FREE(parser, p->scaffold);
    +   p->scaffold = NULL;
    + 
    +   p->scaffLevel = 0;
    +@@ -7082,7 +7403,7 @@ dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) {
    + }
    + 
    + static void
    +-dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) {
    ++dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser) {
    +   HASH_TABLE_ITER iter;
    +   hashTableIterInit(&iter, &(p->elementTypes));
    +   for (;;) {
    +@@ -7090,7 +7411,7 @@ dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) {
    +     if (! e)
    +       break;
    +     if (e->allocDefaultAtts != 0)
    +-      ms->free_fcn(e->defaultAtts);
    ++      FREE(parser, e->defaultAtts);
    +   }
    +   hashTableDestroy(&(p->generalEntities));
    + #ifdef XML_DTD
    +@@ -7102,10 +7423,10 @@ dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) {
    +   poolDestroy(&(p->pool));
    +   poolDestroy(&(p->entityValuePool));
    +   if (isDocEntity) {
    +-    ms->free_fcn(p->scaffIndex);
    +-    ms->free_fcn(p->scaffold);
    ++    FREE(parser, p->scaffIndex);
    ++    FREE(parser, p->scaffold);
    +   }
    +-  ms->free_fcn(p);
    ++  FREE(parser, p);
    + }
    + 
    + /* Do a deep copy of the DTD. Return 0 for out of memory, non-zero otherwise.
    +@@ -7113,7 +7434,7 @@ dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) {
    + */
    + static int
    + dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd,
    +-        const XML_Memory_Handling_Suite *ms) {
    ++        XML_Parser parser) {
    +   HASH_TABLE_ITER iter;
    + 
    +   /* Copy the prefix table. */
    +@@ -7194,7 +7515,7 @@ dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd,
    +       }
    + #endif
    +       newE->defaultAtts
    +-          = ms->malloc_fcn(oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE));
    ++          = MALLOC(parser, oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE));
    +       if (! newE->defaultAtts) {
    +         return 0;
    +       }
    +@@ -7356,7 +7677,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) {
    +     /* table->size is a power of 2 */
    +     table->size = (size_t)1 << INIT_POWER;
    +     tsize = table->size * sizeof(NAMED *);
    +-    table->v = table->mem->malloc_fcn(tsize);
    ++    table->v = MALLOC(table->parser, tsize);
    +     if (! table->v) {
    +       table->size = 0;
    +       return NULL;
    +@@ -7396,7 +7717,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) {
    +       }
    + 
    +       size_t tsize = newSize * sizeof(NAMED *);
    +-      NAMED **newV = table->mem->malloc_fcn(tsize);
    ++      NAMED **newV = MALLOC(table->parser, tsize);
    +       if (! newV)
    +         return NULL;
    +       memset(newV, 0, tsize);
    +@@ -7412,7 +7733,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) {
    +           }
    +           newV[j] = table->v[i];
    +         }
    +-      table->mem->free_fcn(table->v);
    ++      FREE(table->parser, table->v);
    +       table->v = newV;
    +       table->power = newPower;
    +       table->size = newSize;
    +@@ -7425,7 +7746,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) {
    +       }
    +     }
    +   }
    +-  table->v[i] = table->mem->malloc_fcn(createSize);
    ++  table->v[i] = MALLOC(table->parser, createSize);
    +   if (! table->v[i])
    +     return NULL;
    +   memset(table->v[i], 0, createSize);
    +@@ -7438,7 +7759,7 @@ static void FASTCALL
    + hashTableClear(HASH_TABLE *table) {
    +   size_t i;
    +   for (i = 0; i < table->size; i++) {
    +-    table->mem->free_fcn(table->v[i]);
    ++    FREE(table->parser, table->v[i]);
    +     table->v[i] = NULL;
    +   }
    +   table->used = 0;
    +@@ -7448,17 +7769,17 @@ static void FASTCALL
    + hashTableDestroy(HASH_TABLE *table) {
    +   size_t i;
    +   for (i = 0; i < table->size; i++)
    +-    table->mem->free_fcn(table->v[i]);
    +-  table->mem->free_fcn(table->v);
    ++    FREE(table->parser, table->v[i]);
    ++  FREE(table->parser, table->v);
    + }
    + 
    + static void FASTCALL
    +-hashTableInit(HASH_TABLE *p, const XML_Memory_Handling_Suite *ms) {
    ++hashTableInit(HASH_TABLE *p, XML_Parser parser) {
    +   p->power = 0;
    +   p->size = 0;
    +   p->used = 0;
    +   p->v = NULL;
    +-  p->mem = ms;
    ++  p->parser = parser;
    + }
    + 
    + static void FASTCALL
    +@@ -7478,13 +7799,13 @@ hashTableIterNext(HASH_TABLE_ITER *iter) {
    + }
    + 
    + static void FASTCALL
    +-poolInit(STRING_POOL *pool, const XML_Memory_Handling_Suite *ms) {
    ++poolInit(STRING_POOL *pool, XML_Parser parser) {
    +   pool->blocks = NULL;
    +   pool->freeBlocks = NULL;
    +   pool->start = NULL;
    +   pool->ptr = NULL;
    +   pool->end = NULL;
    +-  pool->mem = ms;
    ++  pool->parser = parser;
    + }
    + 
    + static void FASTCALL
    +@@ -7511,13 +7832,13 @@ poolDestroy(STRING_POOL *pool) {
    +   BLOCK *p = pool->blocks;
    +   while (p) {
    +     BLOCK *tem = p->next;
    +-    pool->mem->free_fcn(p);
    ++    FREE(pool->parser, p);
    +     p = tem;
    +   }
    +   p = pool->freeBlocks;
    +   while (p) {
    +     BLOCK *tem = p->next;
    +-    pool->mem->free_fcn(p);
    ++    FREE(pool->parser, p);
    +     p = tem;
    +   }
    + }
    +@@ -7672,8 +7993,8 @@ poolGrow(STRING_POOL *pool) {
    +     if (bytesToAllocate == 0)
    +       return XML_FALSE;
    + 
    +-    temp = (BLOCK *)pool->mem->realloc_fcn(pool->blocks,
    +-                                           (unsigned)bytesToAllocate);
    ++    temp = (BLOCK *)REALLOC(pool->parser, pool->blocks,
    ++                            (unsigned)bytesToAllocate);
    +     if (temp == NULL)
    +       return XML_FALSE;
    +     pool->blocks = temp;
    +@@ -7713,7 +8034,7 @@ poolGrow(STRING_POOL *pool) {
    +     if (bytesToAllocate == 0)
    +       return XML_FALSE;
    + 
    +-    tem = pool->mem->malloc_fcn(bytesToAllocate);
    ++    tem = MALLOC(pool->parser, bytesToAllocate);
    +     if (! tem)
    +       return XML_FALSE;
    +     tem->size = blockSize;
    +@@ -7828,7 +8149,10 @@ build_model(XML_Parser parser) {
    +   const size_t allocsize = (dtd->scaffCount * sizeof(XML_Content)
    +                             + (dtd->contentStringLen * sizeof(XML_Char)));
    + 
    +-  ret = (XML_Content *)MALLOC(parser, allocsize);
    ++  // NOTE: We are avoiding MALLOC(..) here to so that
    ++  //       applications that are not using XML_FreeContentModel but plain
    ++  //       free(..) or .free_fcn() to free the content model's memory are safe.
    ++  ret = (XML_Content *)parser->m_mem.malloc_fcn(allocsize);
    +   if (! ret)
    +     return NULL;
    + 
    +@@ -7949,7 +8273,7 @@ getElementType(XML_Parser parser, const ENCODING *enc, const char *ptr,
    + }
    + 
    + static XML_Char *
    +-copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) {
    ++copyString(const XML_Char *s, XML_Parser parser) {
    +   size_t charsRequired = 0;
    +   XML_Char *result;
    + 
    +@@ -7961,7 +8285,7 @@ copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) {
    +   charsRequired++;
    + 
    +   /* Now allocate space for the copy */
    +-  result = memsuite->malloc_fcn(charsRequired * sizeof(XML_Char));
    ++  result = MALLOC(parser, charsRequired * sizeof(XML_Char));
    +   if (result == NULL)
    +     return NULL;
    +   /* Copy the original into place */
    +diff --git a/expat/tests/runtests.c b/expat/tests/runtests.c
    +index 5db384d..4a0a2e3 100644
    +--- a/expat/tests/runtests.c
    ++++ b/expat/tests/runtests.c
    +@@ -51,6 +51,9 @@
    + #include  // for SIZE_MAX
    + #include 
    + #include 
    ++#include  /* NAN, INFINITY */
    ++#include 
    ++#include  /* for SIZE_MAX */
    + #include 
    + #include  /* ptrdiff_t */
    + #include 
    +@@ -62,7 +65,10 @@
    + #  include 
    + #endif
    + 
    ++#include "expat_config.h"
    ++
    + #include "expat.h"
    ++#include "internal.h"
    + #include "chardata.h"
    + #include "structdata.h"
    + #include "internal.h"
    +@@ -11420,6 +11426,209 @@ START_TEST(test_alloc_reset_after_external_entity_parser_create_fail) {
    + }
    + END_TEST
    + 
    ++static size_t
    ++sizeRecordedFor(void *ptr) {
    ++  return *(size_t *)((char *)ptr - EXPAT_MALLOC_PADDING - sizeof(size_t));
    ++}
    ++
    ++START_TEST(test_alloc_tracker_size_recorded) {
    ++  XML_Memory_Handling_Suite memsuite = {malloc, realloc, free};
    ++
    ++  bool values[] = {true, false};
    ++  for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++) {
    ++    const bool useMemSuite = values[i];
    ++    XML_Parser parser = useMemSuite
    ++                            ? XML_ParserCreate_MM(NULL, &memsuite, XCS("|"))
    ++                            : XML_ParserCreate(NULL);
    ++
    ++    void *ptr = expat_malloc(parser, 10, -1);
    ++
    ++    assert_true(ptr != NULL);
    ++    assert_true(sizeRecordedFor(ptr) == 10);
    ++
    ++    assert_true(expat_realloc(parser, ptr, SIZE_MAX / 2, -1) == NULL);
    ++
    ++    assert_true(sizeRecordedFor(ptr) == 10); // i.e. unchanged
    ++
    ++    ptr = expat_realloc(parser, ptr, 20, -1);
    ++
    ++    assert_true(ptr != NULL);
    ++    assert_true(sizeRecordedFor(ptr) == 20);
    ++
    ++    expat_free(parser, ptr, -1);
    ++
    ++    XML_ParserFree(parser);
    ++  }
    ++}
    ++END_TEST
    ++
    ++START_TEST(test_alloc_tracker_pointer_alignment) {
    ++  XML_Parser parser = XML_ParserCreate(NULL);
    ++  assert_true(sizeof(long long) >= sizeof(size_t)); // self-test
    ++  long long *const ptr
    ++      = (long long *)expat_malloc(parser, 4 * sizeof(long long), -1);
    ++  ptr[0] = 0LL;
    ++  ptr[1] = 1LL;
    ++  ptr[2] = 2LL;
    ++  ptr[3] = 3LL;
    ++  expat_free(parser, ptr, -1);
    ++  XML_ParserFree(parser);
    ++}
    ++END_TEST
    ++
    ++START_TEST(test_alloc_tracker_maximum_amplification) {
    ++  if (g_reparseDeferralEnabledDefault == XML_TRUE) {
    ++    return;
    ++  }
    ++
    ++  XML_Parser parser = XML_ParserCreate(NULL);
    ++
    ++  // Get .m_accounting.countBytesDirect from 0 to 3
    ++  const char *const chunk = "";
    ++  assert_true(_XML_Parse_SINGLE_BYTES(parser, chunk, (int)strlen(chunk),
    ++                                      /*isFinal=*/XML_FALSE)
    ++              == XML_STATUS_OK);
    ++
    ++  // Stop activation threshold from interfering
    ++  assert_true(XML_SetAllocTrackerActivationThreshold(parser, 0) == XML_TRUE);
    ++
    ++  // Exceed maximum amplification: should be rejected.
    ++  assert_true(expat_malloc(parser, 1000, -1) == NULL);
    ++
    ++  // Increase maximum amplification, and try the same amount once more: should
    ++  // work.
    ++  assert_true(XML_SetAllocTrackerMaximumAmplification(parser, 3000.0f)
    ++              == XML_TRUE);
    ++
    ++  void *const ptr = expat_malloc(parser, 1000, -1);
    ++  assert_true(ptr != NULL);
    ++  expat_free(parser, ptr, -1);
    ++
    ++  XML_ParserFree(parser);
    ++}
    ++END_TEST
    ++
    ++START_TEST(test_alloc_tracker_threshold) {
    ++  XML_Parser parser = XML_ParserCreate(NULL);
    ++
    ++  // Exceed maximum amplification *before* (default) threshold: should work.
    ++  void *const ptr = expat_malloc(parser, 1000, -1);
    ++  assert_true(ptr != NULL);
    ++  expat_free(parser, ptr, -1);
    ++
    ++  // Exceed maximum amplification *after* threshold: should be rejected.
    ++  assert_true(XML_SetAllocTrackerActivationThreshold(parser, 999) == XML_TRUE);
    ++  assert_true(expat_malloc(parser, 1000, -1) == NULL);
    ++
    ++  XML_ParserFree(parser);
    ++}
    ++END_TEST
    ++
    ++START_TEST(test_alloc_tracker_getbuffer_unlimited) {
    ++  XML_Parser parser = XML_ParserCreate(NULL);
    ++
    ++  // Artificially lower threshold
    ++  assert_true(XML_SetAllocTrackerActivationThreshold(parser, 0) == XML_TRUE);
    ++
    ++  // Self-test: Prove that threshold is as rejecting as expected
    ++  assert_true(expat_malloc(parser, 1000, -1) == NULL);
    ++  // XML_GetBuffer should be allowed to pass, though
    ++  assert_true(XML_GetBuffer(parser, 1000) != NULL);
    ++
    ++  XML_ParserFree(parser);
    ++}
    ++END_TEST
    ++
    ++START_TEST(test_alloc_tracker_api) {
    ++  XML_Parser parserWithoutParent = XML_ParserCreate(NULL);
    ++  XML_Parser parserWithParent = XML_ExternalEntityParserCreate(
    ++      parserWithoutParent, XCS("entity123"), NULL);
    ++  if (parserWithoutParent == NULL)
    ++    fail("parserWithoutParent is NULL");
    ++  if (parserWithParent == NULL)
    ++    fail("parserWithParent is NULL");
    ++
    ++  // XML_SetAllocTrackerMaximumAmplification, error cases
    ++  if (XML_SetAllocTrackerMaximumAmplification(NULL, 123.0f) == XML_TRUE)
    ++    fail("Call with NULL parser is NOT supposed to succeed");
    ++  if (XML_SetAllocTrackerMaximumAmplification(parserWithParent, 123.0f)
    ++      == XML_TRUE)
    ++    fail("Call with non-root parser is NOT supposed to succeed");
    ++  if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, NAN)
    ++      == XML_TRUE)
    ++    fail("Call with NaN limit is NOT supposed to succeed");
    ++  if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, -1.0f)
    ++      == XML_TRUE)
    ++    fail("Call with negative limit is NOT supposed to succeed");
    ++  if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, 0.9f)
    ++      == XML_TRUE)
    ++    fail("Call with positive limit <1.0 is NOT supposed to succeed");
    ++
    ++  // XML_SetAllocTrackerMaximumAmplification, success cases
    ++  if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, 1.0f)
    ++      == XML_FALSE)
    ++    fail("Call with positive limit >=1.0 is supposed to succeed");
    ++  if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, 123456.789f)
    ++      == XML_FALSE)
    ++    fail("Call with positive limit >=1.0 is supposed to succeed");
    ++  if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, INFINITY)
    ++      == XML_FALSE)
    ++    fail("Call with positive limit >=1.0 is supposed to succeed");
    ++
    ++  // XML_SetAllocTrackerActivationThreshold, error cases
    ++  if (XML_SetAllocTrackerActivationThreshold(NULL, 123) == XML_TRUE)
    ++    fail("Call with NULL parser is NOT supposed to succeed");
    ++  if (XML_SetAllocTrackerActivationThreshold(parserWithParent, 123) == XML_TRUE)
    ++    fail("Call with non-root parser is NOT supposed to succeed");
    ++
    ++  // XML_SetAllocTrackerActivationThreshold, success cases
    ++  if (XML_SetAllocTrackerActivationThreshold(parserWithoutParent, 123)
    ++      == XML_FALSE)
    ++    fail("Call with non-NULL parentless parser is supposed to succeed");
    ++
    ++  XML_ParserFree(parserWithParent);
    ++  XML_ParserFree(parserWithoutParent);
    ++}
    ++END_TEST
    ++
    ++START_TEST(test_mem_api_cycle) {
    ++  XML_Parser parser = XML_ParserCreate(NULL);
    ++
    ++  void *ptr = XML_MemMalloc(parser, 10);
    ++
    ++  assert_true(ptr != NULL);
    ++  memset(ptr, 'x', 10); // assert writability, with ASan in mind
    ++
    ++  ptr = XML_MemRealloc(parser, ptr, 20);
    ++
    ++  assert_true(ptr != NULL);
    ++  memset(ptr, 'y', 20); // assert writability, with ASan in mind
    ++
    ++  XML_MemFree(parser, ptr);
    ++
    ++  XML_ParserFree(parser);
    ++}
    ++END_TEST
    ++
    ++START_TEST(test_mem_api_unlimited) {
    ++  XML_Parser parser = XML_ParserCreate(NULL);
    ++
    ++  assert_true(XML_SetAllocTrackerActivationThreshold(parser, 0) == XML_TRUE);
    ++
    ++  void *ptr = XML_MemMalloc(parser, 1000);
    ++
    ++  assert_true(ptr != NULL);
    ++
    ++  ptr = XML_MemRealloc(parser, ptr, 2000);
    ++
    ++  assert_true(ptr != NULL);
    ++
    ++  XML_MemFree(parser, ptr);
    ++
    ++  XML_ParserFree(parser);
    ++}
    ++END_TEST
    ++
    + static void
    + nsalloc_setup(void) {
    +   XML_Memory_Handling_Suite memsuite = {duff_allocator, duff_reallocator, free};
    +@@ -11820,10 +12029,8 @@ START_TEST(test_nsalloc_realloc_attributes) {
    +     nsalloc_teardown();
    +     nsalloc_setup();
    +   }
    +-  if (i == 0)
    +-    fail("Parsing worked despite failing reallocations");
    +-  else if (i == max_realloc_count)
    +-    fail("Parsing failed at max reallocation count");
    ++  assert_true(
    ++      i == 0); // because expat_realloc relies on expat_malloc to some extent
    + }
    + END_TEST
    + 
    +@@ -13694,6 +13901,18 @@ make_suite(void) {
    +   tcase_add_test__ifdef_xml_dtd(
    +       tc_alloc, test_alloc_reset_after_external_entity_parser_create_fail);
    + 
    ++  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_tracker_size_recorded);
    ++  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_tracker_pointer_alignment);
    ++  tcase_add_test__ifdef_xml_dtd(tc_alloc,
    ++                                test_alloc_tracker_maximum_amplification);
    ++  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_tracker_threshold);
    ++  tcase_add_test__ifdef_xml_dtd(tc_alloc,
    ++                                test_alloc_tracker_getbuffer_unlimited);
    ++  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_tracker_api);
    ++
    ++  tcase_add_test(tc_alloc, test_mem_api_cycle);
    ++  tcase_add_test__ifdef_xml_dtd(tc_alloc, test_mem_api_unlimited);
    ++
    +   suite_add_tcase(s, tc_nsalloc);
    +   tcase_add_checked_fixture(tc_nsalloc, nsalloc_setup, nsalloc_teardown);
    +   tcase_add_test(tc_nsalloc, test_nsalloc_xmlns);
    +diff --git a/expat/xmlwf/xmlwf.c b/expat/xmlwf/xmlwf.c
    +index 7c62919..99622df 100644
    +--- a/expat/xmlwf/xmlwf.c
    ++++ b/expat/xmlwf/xmlwf.c
    +@@ -908,11 +908,11 @@ usage(const XML_Char *prog, int rc) {
    +       T("  -t            write no XML output for [t]iming of plain parsing\n")
    +       T("  -N            enable adding doctype and [n]otation declarations\n")
    +       T("\n")
    +-      T("billion laughs attack protection:\n")
    ++      T("amplification attack protection (e.g. billion laughs):\n")
    +       T("  NOTE: If you ever need to increase these values for non-attack payload, please file a bug report.\n")
    +       T("\n")
    +       T("  -a FACTOR     set maximum tolerated [a]mplification factor (default: 100.0)\n")
    +-      T("  -b BYTES      set number of output [b]ytes needed to activate (default: 8 MiB)\n")
    ++      T("  -b BYTES      set number of output [b]ytes needed to activate (default: 8 MiB/64 MiB)\n")
    +       T("\n")
    +       T("reparse deferral:\n")
    +       T("  -q             disable reparse deferral, and allow [q]uadratic parse runtime with large tokens\n")
    +@@ -921,6 +921,9 @@ usage(const XML_Char *prog, int rc) {
    +       T("  -h            show this [h]elp message and exit\n")
    +       T("  -v            show program's [v]ersion number and exit\n")
    +       T("\n")
    ++      T("environment variables:\n")
    ++      T("  EXPAT_MALLOC_DEBUG=(0|1|2)\n")
    ++      T("                 Control verbosity of allocation tracker (default: 0)\n")
    +       T("exit status:\n")
    +       T("  0             the input files are well-formed and the output (if requested) was written successfully\n")
    +       T("  1             could not allocate data structures, signals a serious problem with execution environment\n")
    +@@ -1133,12 +1136,15 @@ tmain(int argc, XML_Char **argv) {
    + #ifdef XML_DTD
    +       XML_SetBillionLaughsAttackProtectionMaximumAmplification(
    +           parser, attackMaximumAmplification);
    ++      XML_SetAllocTrackerMaximumAmplification(parser,
    ++                                              attackMaximumAmplification);
    + #endif
    +     }
    +     if (attackThresholdGiven) {
    + #ifdef XML_DTD
    +       XML_SetBillionLaughsAttackProtectionActivationThreshold(
    +           parser, attackThresholdBytes);
    ++      XML_SetAllocTrackerActivationThreshold(parser, attackThresholdBytes);
    + #else
    +       (void)attackThresholdBytes; // silence -Wunused-but-set-variable
    + #endif
    +diff --git a/expat/xmlwf/xmlwf_helpgen.py b/expat/xmlwf/xmlwf_helpgen.py
    +index 1bd0a0a..1bacd60 100755
    +--- a/expat/xmlwf/xmlwf_helpgen.py
    ++++ b/expat/xmlwf/xmlwf_helpgen.py
    +@@ -32,6 +32,10 @@
    + import argparse
    + 
    + epilog = """
    ++environment variables:
    ++  EXPAT_MALLOC_DEBUG=(0|1|2)
    ++                 Control verbosity of allocation tracker (default: 0)
    ++
    + exit status:
    +   0             the input files are well-formed and the output (if requested) was written successfully
    +   1             could not allocate data structures, signals a serious problem with execution environment
    +@@ -73,13 +77,13 @@ output_mode.add_argument('-m', action='store_true', help='write [m]eta XML, not
    + output_mode.add_argument('-t', action='store_true', help='write no XML output for [t]iming of plain parsing')
    + output_related.add_argument('-N', action='store_true', help='enable adding doctype and [n]otation declarations')
    + 
    +-billion_laughs = parser.add_argument_group('billion laughs attack protection',
    ++billion_laughs = parser.add_argument_group('amplification attack protection (e.g. billion laughs)',
    +                                            description='NOTE: '
    +                                                        'If you ever need to increase these values '
    +                                                        'for non-attack payload, please file a bug report.')
    + billion_laughs.add_argument('-a', metavar='FACTOR',
    +                             help='set maximum tolerated [a]mplification factor (default: 100.0)')
    +-billion_laughs.add_argument('-b', metavar='BYTES', help='set number of output [b]ytes needed to activate (default: 8 MiB)')
    ++billion_laughs.add_argument('-b', metavar='BYTES', help='set number of output [b]ytes needed to activate (default: 8 MiB/64 MiB)')
    + 
    + reparse_deferral = parser.add_argument_group('reparse deferral')
    + reparse_deferral.add_argument('-q', metavar='FACTOR',
    diff --git a/expat.spec b/expat.spec
    index a4080f1..0620993 100644
    --- a/expat.spec
    +++ b/expat.spec
    @@ -1,33 +1,30 @@
    -%global unversion 2_2_5
    +%global unversion 2_5_0
     
     Summary: An XML parser library
     Name: expat
     Version: %(echo %{unversion} | sed 's/_/./g')
    -Release: 17%{?dist}
    +Release: 1%{?dist}
     Source: https://github.com/libexpat/libexpat/archive/R_%{unversion}.tar.gz#/expat-%{version}.tar.gz
     URL: https://libexpat.github.io/
     License: MIT
     BuildRequires: autoconf, libtool, xmlto, gcc-c++
    -Patch0: expat-2.2.5-doc2man.patch
    -Patch1: expat-2.2.5-CVE-2018-20843.patch
    -Patch2: expat-2.2.5-CVE-2019-15903.patch
    -Patch3:	expat-2.2.5-Detect-and-prevent-integer-overflow-in-XML_GetBuffer.patch
    -Patch4:	expat-2.2.5-Detect-and-prevent-troublesome-left-shifts.patch
    -Patch5:	expat-2.2.5-Prevent-integer-overflow-on-m_groupSize-in-function.patch
    -Patch6:	expat-2.2.5-Prevent-more-integer-overflows.patch
    -Patch7: expat-2.2.5-Protect-against-malicious-namespace-declarations.patch
    -Patch8: expat-2.2.5-Add-missing-validation-of-encoding.patch
    -Patch9: expat-2.2.5-Prevent-integer-overflow-in-storeRawNames.patch
    -Patch10: expat-2.2.5-Prevent-integer-overflow-in-copyString.patch
    -Patch11: expat-2.2.5-Prevent-stack-exhaustion-in-build_model.patch
    -Patch12: expat-2.2.5-Ensure-raw-tagnames-are-safe-exiting-internalEntityParser.patch
    -Patch13: expat-2.2.5-CVE-2022-43680.patch
    -Patch14: expat-2.2.5-CVE-2023-52425.patch
    -Patch15: expat-2.2.5-CVE-2024-45490.patch
    -Patch16: expat-2.2.5-CVE-2024-45491.patch
    -Patch17: expat-2.2.5-CVE-2024-45492.patch
    -Patch18: expat-2.2.5-CVE-2024-50602.patch
    -Patch19: expat-2.2.5-CVE-2024-8176.patch
    +BuildRequires: make
    +# https://issues.redhat.com/browse/RHEL-24227
    +Patch0: expat-2.5.0-CVE-2023-52425.patch
    +# https://issues.redhat.com/browse/RHEL-28700
    +Patch1: expat-2.5.0-CVE-2024-28757.patch
    +# https://issues.redhat.com/browse/RHEL-56761
    +Patch2: expat-2.5.0-CVE-2024-45490.patch
    +# https://issues.redhat.com/browse/RHEL-57520
    +Patch3: expat-2.5.0-CVE-2024-45491.patch
    +# https://issues.redhat.com/browse/RHEL-57511
    +Patch4: expat-2.5.0-CVE-2024-45492.patch
    +# https://issues.redhat.com/browse/RHEL-65066
    +Patch5: expat-2.5.0-CVE-2024-50602.patch
    +# https://issues.redhat.com/browse/RHEL-57489
    +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
     
     %description
     This is expat, the C library for parsing XML, written by James Clark. Expat
    @@ -55,27 +52,15 @@ Install it if you need to link statically with expat.
     
     %prep
     %setup -q -n libexpat-R_%{unversion}/expat
    -%patch0 -p2 -b .doc2man
    -%patch1 -p2 -b .cve20843
    -%patch2 -p2 -b .cve15903
    -%patch3 -p1 -b .CVE-2022-23852
    -%patch4 -p1 -b .CVE-2021-45960
    -%patch5 -p1 -b .CVE-2021-46143
    -%patch6 -p1 -b .CVE-2022-22822-CVE-2022-22827
    -%patch7 -p1 -b .CVE-2022-25236
    -%patch8 -p1 -b .CVE-2022-25235
    -%patch9 -p1 -b .CVE-2022-25315
    -%patch10 -p1 -b .CVE-2022-25314
    -%patch11 -p1 -b .CVE-2022-25313
    -%patch12 -p1 -b .CVE-2022-40674
    -%patch13 -p1 -b .CVE-2022-43680
     pushd ..
    -%patch14 -p1 -b .CVE-2023-52425
    -%patch15 -p1 -b .CVE-2024-45490
    -%patch16 -p1 -b .CVE-2024-45491
    -%patch17 -p1 -b .CVE-2024-45492
    -%patch18 -p1 -b .CVE-2024-50602
    -%patch19 -p1 -b .CVE-2024-8176
    +%patch0 -p1 -b .CVE-2023-52425
    +%patch1 -p1 -b .CVE-2024-28757
    +%patch2 -p1 -b .CVE-2024-45490
    +%patch3 -p1 -b .CVE-2024-45491
    +%patch4 -p1 -b .CVE-2024-45492
    +%patch5 -p1 -b .CVE-2024-50602
    +%patch6 -p1 -b .CVE-2024-8176
    +%patch7 -p1 -b .CVE-2025-59375
     popd
     
     sed -i 's/install-data-hook/do-nothing-please/' lib/Makefile.am
    @@ -115,15 +100,21 @@ make check
     %{_mandir}/*/*
     
     %files devel
    -%doc doc/reference.html doc/*.png doc/*.css examples/*.c
    +%doc doc/reference.html doc/*.css examples/*.c
     %{_libdir}/lib*.so
     %{_libdir}/pkgconfig/*.pc
     %{_includedir}/*.h
    +%{_libdir}/cmake/expat-%{version}
     
     %files static
     %{_libdir}/lib*.a
     
     %changelog
    +* Wed Nov 19 2025 Tomas Korbar  - 2.5.0-1
    +- Rebase to version 2.5.0
    +- Fix CVE-2025-59375
    +- Resolves: RHEL-114618
    +
     * Mon Apr 07 2025 Tomas Korbar  - 2.2.5-17
     - Fix CVE-2024-8176
     - Resolves: RHEL-57477
    diff --git a/sources b/sources
    index 043438b..30d98c8 100644
    --- a/sources
    +++ b/sources
    @@ -1 +1 @@
    -SHA512 (expat-2.2.5.tar.gz) = 61ce2a479521412e0c56c352106c4adfb61a6bedb883921aba3ebccc29311ddd192646ac2c51b41572728d4de6ab4cb60a1dbc71515d742a80a8b59d89ca74d6
    +SHA512 (expat-2.5.0.tar.gz) = 779f0d0f3f2d8b33db0fd044864ab5ab1a40f20501f792fe90ad0d18de536c4765c3749f120e21fec11a0e6c89af1dc576d1fe261c871ca44a594f7b61fd1d9e