Compare commits

...

No commits in common. "c8" and "c9s-plans-gating-update" have entirely different histories.

28 changed files with 638 additions and 2296 deletions

View File

@ -1 +0,0 @@
fa46ccce6770ccae767c28f6ac55e2428089d4a0 SOURCES/expat-2.2.5.tar.gz

1
.fmf/version Normal file
View File

@ -0,0 +1 @@
1

24
.gitignore vendored
View File

@ -1 +1,23 @@
SOURCES/expat-2.2.5.tar.gz expat-1.95.8.tar.gz
i386
*.rpm
clog
expat-2.0.1.tar.gz
/results_expat
/expat-2.?.?
/expat-2.1.0.tar.gz
/expat-2.1.1.tar.bz2
/expat-2.2.0.tar.bz2
/expat-2.2.1.tar.gz
/expat-2.2.2.tar.gz
/libexpat-R_*
/expat-2.2.3.tar.gz
/expat-2.2.4.tar.gz
/expat-2.2.5.tar.gz
/expat-2.2.6.tar.gz
/expat-2.2.7.tar.gz
/expat-2.2.8.tar.gz
/expat-2.2.10.tar.gz
/expat-2.4.7.tar.gz
/expat-2.4.9.tar.gz
/expat-2.5.0.tar.gz

View File

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

View File

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

View File

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

View File

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

View File

@ -1,129 +0,0 @@
commit 3c1a64705b5662c5b78f4aa5a5acc7a59c477094
Author: Tomas Korbar <tkorbar@redhat.com>
Date: Wed Sep 11 15:03:05 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
--- 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
that are part of the document is indicated by <code>len</code>. This means
that <code>s</code> doesn't have to be null terminated. It also means that
if <code>len</code> is larger than the number of bytes in the block of
-memory that <code>s</code> points at, then a memory fault is likely. The
+memory that <code>s</code> points at, then a memory fault is likely.
+Negative values for <code>len</code> are rejected since Expat 2.2.1.
+The
<code>isFinal</code> parameter informs the parser that this is the last
piece of the document. Frequently, the last piece is empty (i.e.
<code>len</code> is zero.)
@@ -1054,11 +1056,17 @@ XML_ParseBuffer(XML_Parser p,
int isFinal);
</pre>
<div class="fcndef">
+<p>
This is just like <code><a href= "#XML_Parse" >XML_Parse</a></code>,
except in this case Expat provides the buffer. By obtaining the
buffer from Expat with the <code><a href= "#XML_GetBuffer"
>XML_GetBuffer</a></code> function, the application can avoid double
copying of the input.
+</p>
+
+<p>
+Negative values for <code>len</code> are rejected since Expat 2.6.3.
+</p>
</div>
<pre class="fcndec" id="XML_GetBuffer">
diff --git a/expat/lib/xmlparse.c b/expat/lib/xmlparse.c
index 488f63f..c3c1af9 100644
--- a/expat/lib/xmlparse.c
+++ b/expat/lib/xmlparse.c
@@ -1981,6 +1981,12 @@ XML_ParseBuffer(XML_Parser parser, int len, int isFinal)
if (parser == NULL)
return XML_STATUS_ERROR;
+
+ if (len < 0) {
+ parser->m_errorCode = XML_ERROR_INVALID_ARGUMENT;
+ return XML_STATUS_ERROR;
+ }
+
switch (parser->m_parsingStatus.parsing) {
case XML_SUSPENDED:
parser->m_errorCode = XML_ERROR_SUSPENDED;
diff --git a/expat/tests/runtests.c b/expat/tests/runtests.c
index 486073f..6a3e09a 100644
--- a/expat/tests/runtests.c
+++ b/expat/tests/runtests.c
@@ -4083,6 +4083,57 @@ START_TEST(test_empty_parse)
}
END_TEST
+/* Test XML_Parse for len < 0 */
+START_TEST(test_negative_len_parse) {
+ const char *const doc = "<root/>";
+ for (int isFinal = 0; isFinal < 2; isFinal++) {
+ XML_Parser parser = XML_ParserCreate(NULL);
+
+ if (XML_GetErrorCode(parser) != XML_ERROR_NONE)
+ fail("There was not supposed to be any initial parse error.");
+
+ const enum XML_Status status = XML_Parse(parser, doc, -1, isFinal);
+
+ if (status != XML_STATUS_ERROR)
+ fail("Negative len was expected to fail the parse but did not.");
+
+ if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_ARGUMENT)
+ fail("Parse error does not match XML_ERROR_INVALID_ARGUMENT.");
+
+ XML_ParserFree(parser);
+ }
+}
+END_TEST
+
+/* Test XML_ParseBuffer for len < 0 */
+START_TEST(test_negative_len_parse_buffer) {
+ const char *const doc = "<root/>";
+ for (int isFinal = 0; isFinal < 2; isFinal++) {
+ XML_Parser parser = XML_ParserCreate(NULL);
+
+ if (XML_GetErrorCode(parser) != XML_ERROR_NONE)
+ fail("There was not supposed to be any initial parse error.");
+
+ void *const buffer = XML_GetBuffer(parser, (int)strlen(doc));
+
+ if (buffer == NULL)
+ fail("XML_GetBuffer failed.");
+
+ memcpy(buffer, doc, strlen(doc));
+
+ const enum XML_Status status = XML_ParseBuffer(parser, -1, isFinal);
+
+ if (status != XML_STATUS_ERROR)
+ fail("Negative len was expected to fail the parse but did not.");
+
+ if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_ARGUMENT)
+ fail("Parse error does not match XML_ERROR_INVALID_ARGUMENT.");
+
+ XML_ParserFree(parser);
+ }
+}
+END_TEST
+
/* Test odd corners of the XML_GetBuffer interface */
static enum XML_Status
get_feature(enum XML_FeatureEnum feature_id, long *presult)
@@ -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);
#if defined(XML_CONTEXT_BYTES)

View File

@ -1,29 +0,0 @@
commit 75bb51c072a0a505037bea18d18103473000b339
Author: Tomas Korbar <tkorbar@redhat.com>
Date: Wed Sep 11 15:07:26 2024 +0200
Fix CVE-2024-45491
https://github.com/libexpat/libexpat/pull/891
diff --git a/expat/lib/xmlparse.c b/expat/lib/xmlparse.c
index c3c1af9..6818c4e 100644
--- a/expat/lib/xmlparse.c
+++ b/expat/lib/xmlparse.c
@@ -6843,6 +6843,16 @@ dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, const XML_Memory_H
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 = (DEFAULT_ATTRIBUTE *)
ms->malloc_fcn(oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE));
if (!newE->defaultAtts) {

View File

@ -1,28 +0,0 @@
commit 6fd04be3c2f7a2730c85b0eaf061549953161da3
Author: Tomas Korbar <tkorbar@redhat.com>
Date: Wed Sep 11 15:12:38 2024 +0200
Fix CVE-2024-45492
https://github.com/libexpat/libexpat/pull/892
diff --git a/expat/lib/xmlparse.c b/expat/lib/xmlparse.c
index 6818c4e..698e907 100644
--- a/expat/lib/xmlparse.c
+++ b/expat/lib/xmlparse.c
@@ -7426,6 +7426,15 @@ nextScaffoldPart(XML_Parser parser)
int next;
if (!dtd->scaffIndex) {
+ /* Detect and prevent integer overflow.
+ * The preprocessor guard addresses the "always false" warning
+ * from -Wtype-limits on platforms where
+ * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
+#if UINT_MAX >= SIZE_MAX
+ if (parser->m_groupSize > ((size_t)(-1) / sizeof(int))) {
+ return -1;
+ }
+#endif
dtd->scaffIndex = (int *)MALLOC(parser, parser->m_groupSize * sizeof(int));
if (!dtd->scaffIndex)
return -1;

View File

@ -1,108 +0,0 @@
commit c84ad1507fa42c25937af06e349c8f2f9bc34c11
Author: Tomas Korbar <tkorbar@redhat.com>
Date: Fri Nov 8 11:18:42 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
--- 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,
+ /* Added in 2.6.4. */
+ XML_ERROR_NOT_STARTED
};
enum XML_Content_Type {
diff --git a/expat/lib/xmlparse.c b/expat/lib/xmlparse.c
index 698e907..ed079a5 100644
--- a/expat/lib/xmlparse.c
+++ b/expat/lib/xmlparse.c
@@ -2170,6 +2170,9 @@ XML_StopParser(XML_Parser parser, XML_Bool resumable)
if (parser == NULL)
return XML_STATUS_ERROR;
switch (parser->m_parsingStatus.parsing) {
+ case XML_INITIALIZED:
+ parser->m_errorCode = XML_ERROR_NOT_STARTED;
+ return XML_STATUS_ERROR;
case XML_SUSPENDED:
if (resumable) {
parser->m_errorCode = XML_ERROR_SUSPENDED;
@@ -2180,7 +2183,7 @@ XML_StopParser(XML_Parser parser, XML_Bool resumable)
case XML_FINISHED:
parser->m_errorCode = XML_ERROR_FINISHED;
return XML_STATUS_ERROR;
- default:
+ case XML_PARSING:
if (resumable) {
#ifdef XML_DTD
if (parser->m_isParamEntity) {
@@ -2192,6 +2195,9 @@ XML_StopParser(XML_Parser parser, XML_Bool resumable)
}
else
parser->m_parsingStatus.parsing = XML_FINISHED;
+ break;
+ default:
+ assert(0);
}
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");
+ /* Added in 2.6.4. */
+ case XML_ERROR_NOT_STARTED:
+ return XML_L("parser not started");
}
return NULL;
}
diff --git a/expat/tests/runtests.c b/expat/tests/runtests.c
index 6a3e09a..7b6d9fb 100644
--- a/expat/tests/runtests.c
+++ b/expat/tests/runtests.c
@@ -9162,6 +9162,28 @@ START_TEST(test_misc_utf16le)
END_TEST
+START_TEST(test_misc_resumeparser_not_crashing) {
+ XML_Parser parser = XML_ParserCreate(NULL);
+ XML_GetBuffer(parser, 1);
+ XML_StopParser(parser, /*resumable=*/XML_TRUE);
+ XML_ResumeParser(parser); // could crash here, previously
+ XML_ParserFree(parser);
+}
+END_TEST
+
+START_TEST(test_misc_stopparser_rejects_unstarted_parser) {
+ const XML_Bool cases[] = {XML_TRUE, XML_FALSE};
+ for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
+ const XML_Bool resumable = cases[i];
+ XML_Parser parser = XML_ParserCreate(NULL);
+ assert_true(XML_GetErrorCode(parser) == XML_ERROR_NONE);
+ assert_true(XML_StopParser(parser, resumable) == XML_STATUS_ERROR);
+ assert_true(XML_GetErrorCode(parser) == XML_ERROR_NOT_STARTED);
+ XML_ParserFree(parser);
+ }
+}
+END_TEST
+
static void
alloc_setup(void)
{
@@ -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);
suite_add_tcase(s, tc_alloc);
tcase_add_checked_fixture(tc_alloc, alloc_setup, alloc_teardown);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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

1
ci.fmf Normal file
View File

@ -0,0 +1 @@
resultsdb-testcase: separate

View File

@ -1,16 +1,14 @@
commit 5948204b65267ef0c16c2181e4bd92c0ef50075b commit 678a2f7efcaaa977886e055613f2332615aef82c
Author: Tomas Korbar <tkorbar@redhat.com> Author: Tomas Korbar <tkorbar@redhat.com>
Date: Tue Mar 26 10:34:14 2024 +0100 Date: Tue Feb 13 13:52:28 2024 +0100
Fix CVE-2023-52425 Fix CVE-2023-52425
upstream PR #789
diff --git a/expat/Makefile.am b/expat/Makefile.am diff --git a/expat/Makefile.am b/expat/Makefile.am
index 5ed9ac4..0e3181d 100644 index 37ae373..cd0117f 100644
--- a/expat/Makefile.am --- a/expat/Makefile.am
+++ b/expat/Makefile.am +++ b/expat/Makefile.am
@@ -120,6 +120,11 @@ buildlib: @@ -131,6 +131,11 @@ buildlib:
run-benchmark: run-benchmark:
$(MAKE) -C tests/benchmark $(MAKE) -C tests/benchmark
./run.sh tests/benchmark/benchmark@EXEEXT@ -n $(top_srcdir)/../testdata/largefiles/recset.xml 65535 3 ./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_tag.xml 4096 3
+ ./run.sh tests/benchmark/benchmark@EXEEXT@ -n $(top_srcdir)/../testdata/largefiles/aaaaaa_text.xml 4096 3 + ./run.sh tests/benchmark/benchmark@EXEEXT@ -n $(top_srcdir)/../testdata/largefiles/aaaaaa_text.xml 4096 3
tests/xmlts.zip: .PHONY: download-xmlts-zip
if test "$(XMLTS_ZIP)" = ""; then \ download-xmlts-zip:
diff --git a/expat/doc/reference.html b/expat/doc/reference.html 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 --- a/expat/doc/reference.html
+++ b/expat/doc/reference.html +++ b/expat/doc/reference.html
@@ -1996,6 +1996,27 @@ parse position may be before the beginning of the buffer.</p> @@ -151,10 +151,11 @@ interface.</p>
</ul>
</li>
<li>
- <a href="#billion-laughs">Billion Laughs Attack Protection</a>
+ <a href="#attack-protection">Attack Protection</a>
<ul>
<li><a href="#XML_SetBillionLaughsAttackProtectionMaximumAmplification">XML_SetBillionLaughsAttackProtectionMaximumAmplification</a></li>
<li><a href="#XML_SetBillionLaughsAttackProtectionActivationThreshold">XML_SetBillionLaughsAttackProtectionActivationThreshold</a></li>
+ <li><a href="#XML_SetReparseDeferralEnabled">XML_SetReparseDeferralEnabled</a></li>
</ul>
</li>
<li><a href="#miscellaneous">Miscellaneous Functions</a>
@@ -2096,11 +2097,7 @@ parse position may be before the beginning of the buffer.</p>
return NULL.</p> return NULL.</p>
</div> </div>
-<h3><a name="billion-laughs">Billion Laughs Attack Protection</a></h3>
-
-<p>The functions in this section configure the built-in
- protection against various forms of
- <a href="https://en.wikipedia.org/wiki/Billion_laughs_attack">billion laughs attacks</a>.</p>
+<h3><a name="attack-protection">Attack Protection</a><a name="billion-laughs"></a></h3>
<h4 id="XML_SetBillionLaughsAttackProtectionMaximumAmplification">XML_SetBillionLaughsAttackProtectionMaximumAmplification</h4>
<pre class="fcndec">
@@ -2188,6 +2185,27 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold(XML_Parser p,
</p>
</div>
+<h4 id="XML_SetReparseDeferralEnabled">XML_SetReparseDeferralEnabled</h4> +<h4 id="XML_SetReparseDeferralEnabled">XML_SetReparseDeferralEnabled</h4>
+<pre class="fcndec"> +<pre class="fcndec">
+/* Added in Expat 2.6.0. */ +/* Added in Expat 2.6.0. */
@ -55,10 +79,10 @@ index efc19f4..95c33c7 100644
<p>The functions in this section either obtain state information from <p>The functions in this section either obtain state information from
diff --git a/expat/doc/xmlwf.xml b/expat/doc/xmlwf.xml 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 --- a/expat/doc/xmlwf.xml
+++ b/expat/doc/xmlwf.xml +++ b/expat/doc/xmlwf.xml
@@ -246,6 +246,16 @@ supports both. @@ -313,6 +313,16 @@ supports both.
</listitem> </listitem>
</varlistentry> </varlistentry>
@ -75,57 +99,21 @@ index 5e2a4ae..6b719eb 100644
<varlistentry> <varlistentry>
<term><option>-r</option></term> <term><option>-r</option></term>
<listitem> <listitem>
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 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 --- a/expat/lib/expat.h
+++ b/expat/lib/expat.h +++ b/expat/lib/expat.h
@@ -1071,6 +1071,10 @@ XMLPARSEAPI(const XML_Feature *) @@ -16,6 +16,7 @@
XML_GetFeatureList(void); Copyright (c) 2016 Thomas Beutlich <tc@tbeu.de>
Copyright (c) 2017 Rhodri James <rhodri@wildebeest.org.uk>
Copyright (c) 2022 Thijs Schreijer <thijs@thijsschreijer.nl>
+ Copyright (c) 2023 Sony Corporation / Snild Dolkow <snild@sony.com>
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. */ +/* Added in Expat 2.6.0. */
+XMLPARSEAPI(XML_Bool) +XMLPARSEAPI(XML_Bool)
@ -135,61 +123,58 @@ index 1f608c0..afe12c5 100644
See http://semver.org. See http://semver.org.
*/ */
diff --git a/expat/lib/internal.h b/expat/lib/internal.h 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 --- a/expat/lib/internal.h
+++ b/expat/lib/internal.h +++ b/expat/lib/internal.h
@@ -109,6 +109,7 @@ @@ -31,6 +31,7 @@
# endif Copyright (c) 2016-2022 Sebastian Pipping <sebastian@pipping.org>
Copyright (c) 2018 Yury Gribov <tetra2005@gmail.com>
Copyright (c) 2019 David Loffredo <loffredo@steptools.com>
+ Copyright (c) 2023 Sony Corporation / Snild Dolkow <snild@sony.com>
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 #endif
+#include "expat.h" +extern XML_Bool g_reparseDeferralEnabledDefault; // written ONLY in runtests.c
+extern unsigned int g_parseAttempts; // used for testing only
#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
+ +
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
diff --git a/expat/lib/libexpat.def b/expat/lib/libexpat.def diff --git a/expat/lib/libexpat.def.cmake b/expat/lib/libexpat.def.cmake
index d08f5b7..163870b 100644 index cf434a2..3ff4d55 100644
--- a/expat/lib/libexpat.def --- a/expat/lib/libexpat.def.cmake
+++ b/expat/lib/libexpat.def +++ b/expat/lib/libexpat.def.cmake
@@ -75,4 +75,5 @@ EXPORTS @@ -77,3 +77,4 @@ EXPORTS
; XML_GetAttributeInfo @66 ; added with version 2.4.0
XML_SetHashSalt @67@ @_EXPAT_COMMENT_DTD@ XML_SetBillionLaughsAttackProtectionActivationThreshold @69
; added with version 2.2.5 @_EXPAT_COMMENT_DTD@ XML_SetBillionLaughsAttackProtectionMaximumAmplification @70
- _INTERNAL_trim_to_complete_utf8_characters @68@ +XML_SetReparseDeferralEnabled @71
\ No newline at end of file
+ _INTERNAL_trim_to_complete_utf8_characters @68@
+ XML_SetReparseDeferralEnabled @69
diff --git a/expat/lib/xmlparse.c b/expat/lib/xmlparse.c 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 --- a/expat/lib/xmlparse.c
+++ b/expat/lib/xmlparse.c +++ b/expat/lib/xmlparse.c
@@ -34,6 +34,7 @@ @@ -73,6 +73,7 @@
# define _GNU_SOURCE 1 /* syscall prototype */ # endif
#endif #endif
+#include <stdbool.h> +#include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <string.h> /* memset(), memcpy() */ #include <string.h> /* memset(), memcpy() */
#include <assert.h> #include <assert.h>
@@ -173,6 +174,8 @@ typedef char ICHAR; @@ -196,6 +197,8 @@ typedef char ICHAR;
#endif /* HAVE_BCOPY */ /* Do safe (NULL-aware) pointer arithmetic */
#endif /* HAVE_MEMMOVE */ #define EXPAT_SAFE_PTR_DIFF(p, q) (((p) && (q)) ? ((p) - (q)) : 0)
+#define EXPAT_MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define EXPAT_MIN(a, b) (((a) < (b)) ? (a) : (b))
+ +
#include "internal.h" #include "internal.h"
#include "xmltok.h" #include "xmltok.h"
#include "xmlrole.h" #include "xmlrole.h"
@@ -544,6 +547,9 @@ parserInit(XML_Parser parser, const XML_Char *encodingName); @@ -602,6 +605,9 @@ static unsigned long getDebugLevel(const char *variableName,
? 0 \ ? 0 \
: ((*((pool)->ptr)++ = c), 1)) : ((*((pool)->ptr)++ = c), 1))
@ -199,7 +184,7 @@ index 3f765f7..488f63f 100644
struct XML_ParserStruct { struct XML_ParserStruct {
/* The first member must be m_userData so that the XML_GetUserData /* The first member must be m_userData so that the XML_GetUserData
macro works. */ macro works. */
@@ -559,6 +565,9 @@ struct XML_ParserStruct { @@ -617,6 +623,9 @@ struct XML_ParserStruct {
const char *m_bufferLim; const char *m_bufferLim;
XML_Index m_parseEndByteIndex; XML_Index m_parseEndByteIndex;
const char *m_parseEndPtr; const char *m_parseEndPtr;
@ -209,7 +194,7 @@ index 3f765f7..488f63f 100644
XML_Char *m_dataBuf; XML_Char *m_dataBuf;
XML_Char *m_dataBufEnd; XML_Char *m_dataBufEnd;
XML_StartElementHandler m_startElementHandler; 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; 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. + // ...but *do* try anyway if we're close to causing a reallocation.
+ size_t available_buffer + size_t available_buffer
+ = EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_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); + available_buffer -= EXPAT_MIN(available_buffer, XML_CONTEXT_BYTES);
+#endif +#endif
+ available_buffer + available_buffer
@ -253,12 +238,11 @@ index 3f765f7..488f63f 100644
+ } + }
+ return ret; + return ret;
+} +}
+
+ +
static XML_Bool /* only valid for root parser */ static XML_Bool /* only valid for root parser */
startParsing(XML_Parser parser) startParsing(XML_Parser parser) {
{ /* hash functions must be initialized before setContext() is called */
@@ -1078,6 +1129,9 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) @@ -1129,6 +1179,9 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) {
parser->m_bufferEnd = parser->m_buffer; parser->m_bufferEnd = parser->m_buffer;
parser->m_parseEndByteIndex = 0; parser->m_parseEndByteIndex = 0;
parser->m_parseEndPtr = NULL; parser->m_parseEndPtr = NULL;
@ -268,7 +252,7 @@ index 3f765f7..488f63f 100644
parser->m_declElementType = NULL; parser->m_declElementType = NULL;
parser->m_declAttributeId = NULL; parser->m_declAttributeId = NULL;
parser->m_declEntity = 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. to worry which hash secrets each table has.
*/ */
unsigned long oldhash_secret_salt; unsigned long oldhash_secret_salt;
@ -276,7 +260,7 @@ index 3f765f7..488f63f 100644
/* Validate the oldParser parameter before we pull everything out of it */ /* Validate the oldParser parameter before we pull everything out of it */
if (oldParser == NULL) 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. to worry which hash secrets each table has.
*/ */
oldhash_secret_salt = parser->m_hash_secret_salt; oldhash_secret_salt = parser->m_hash_secret_salt;
@ -284,7 +268,7 @@ index 3f765f7..488f63f 100644
#ifdef XML_DTD #ifdef XML_DTD
if (! context) if (! context)
@@ -1336,6 +1392,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, @@ -1394,6 +1449,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context,
parser->m_defaultExpandInternalEntities = oldDefaultExpandInternalEntities; parser->m_defaultExpandInternalEntities = oldDefaultExpandInternalEntities;
parser->m_ns_triplets = oldns_triplets; parser->m_ns_triplets = oldns_triplets;
parser->m_hash_secret_salt = oldhash_secret_salt; parser->m_hash_secret_salt = oldhash_secret_salt;
@ -292,7 +276,7 @@ index 3f765f7..488f63f 100644
parser->m_parentParser = oldParser; parser->m_parentParser = oldParser;
#ifdef XML_DTD #ifdef XML_DTD
parser->m_paramEntityParsing = oldParamEntityParsing; 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; parser->m_parsingStatus.parsing = XML_PARSING;
} }
@ -307,7 +291,9 @@ index 3f765f7..488f63f 100644
- data are the final chunk of input, then we have to check them again - data are the final chunk of input, then we have to check them again
- to detect errors based on that fact. - 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) { - if (parser->m_errorCode == XML_ERROR_NONE) {
- switch (parser->m_parsingStatus.parsing) { - switch (parser->m_parsingStatus.parsing) {
@ -324,7 +310,8 @@ index 3f765f7..488f63f 100644
- * - *
- * LCOV_EXCL_START - * 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; - parser->m_positionPtr = parser->m_bufferPtr;
- return XML_STATUS_SUSPENDED; - return XML_STATUS_SUSPENDED;
- /* LCOV_EXCL_STOP */ - /* LCOV_EXCL_STOP */
@ -346,20 +333,29 @@ index 3f765f7..488f63f 100644
const char *end; const char *end;
int nLeftOver; int nLeftOver;
enum XML_Status result; 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_positionPtr = s;
parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal; parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal;
- parser->m_errorCode = parser->m_processor(parser, s, parser->m_parseEndPtr = s + len, &end); parser->m_errorCode
+ parser->m_errorCode = callProcessor(parser, s, parser->m_parseEndPtr = s + len, &end); - = 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) { if (parser->m_errorCode != XML_ERROR_NONE) {
parser->m_eventEndPtr = parser->m_eventPtr; parser->m_eventEndPtr = parser->m_eventPtr;
@@ -1920,22 +1933,25 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) @@ -1939,23 +1951,25 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) {
XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr, end, &parser->m_position); &parser->m_position);
nLeftOver = s + len - end; nLeftOver = s + len - end;
if (nLeftOver) { 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 */ - /* avoid _signed_ integer overflow */
- char *temp = NULL; - char *temp = NULL;
- const int bytesToAllocate = (int)((unsigned)len * 2U); - const int bytesToAllocate = (int)((unsigned)len * 2U);
@ -396,7 +392,7 @@ index 3f765f7..488f63f 100644
memcpy(parser->m_buffer, end, nLeftOver); memcpy(parser->m_buffer, end, nLeftOver);
} }
parser->m_bufferPtr = parser->m_buffer; 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; return result;
} }
#endif /* not defined XML_CONTEXT_BYTES */ #endif /* not defined XML_CONTEXT_BYTES */
@ -419,45 +415,61 @@ index 3f765f7..488f63f 100644
} }
enum XML_Status XMLCALL 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_parseEndByteIndex += len;
parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal; 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_errorCode = callProcessor(parser, start, parser->m_parseEndPtr,
+ &parser->m_bufferPtr); + &parser->m_bufferPtr);
if (parser->m_errorCode != XML_ERROR_NONE) { if (parser->m_errorCode != XML_ERROR_NONE) {
parser->m_eventEndPtr = parser->m_eventPtr; parser->m_eventEndPtr = parser->m_eventPtr;
@@ -2035,8 +2051,14 @@ XML_GetBuffer(XML_Parser parser, int len) @@ -2061,10 +2074,14 @@ XML_GetBuffer(XML_Parser parser, int len) {
default:; default:;
} }
- if (len > EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferEnd)) { - 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 + // whether or not the request succeeds, `len` seems to be the app's preferred
+ // buffer fill size; remember it. + // buffer fill size; remember it.
+ parser->m_lastBufferRequestSize = len; + parser->m_lastBufferRequestSize = len;
+ if (len > EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferEnd) + if (len > EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferEnd)
+ || parser->m_buffer == NULL) { + || parser->m_buffer == NULL) {
+#ifdef XML_CONTEXT_BYTES +#if XML_CONTEXT_BYTES > 0
int keep; int keep;
-#endif /* defined XML_CONTEXT_BYTES */
+#endif /* XML_CONTEXT_BYTES > 0 */ +#endif /* XML_CONTEXT_BYTES > 0 */
/* Do not invoke signed arithmetic overflow: */ /* Do not invoke signed arithmetic overflow: */
int neededSize = (int)((unsigned)len int neededSize = (int)((unsigned)len
+ (unsigned)EXPAT_SAFE_PTR_DIFF( + (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; return NULL;
} }
neededSize += keep; neededSize += keep;
-#endif /* defined XML_CONTEXT_BYTES */
- if (neededSize - if (neededSize
- <= EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_buffer)) { - <= 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 + if (parser->m_buffer && parser->m_bufferPtr
+ && neededSize + && neededSize
+ <= EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_buffer)) { + <= 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)) { if (keep < EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer)) {
int offset 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; parser->m_bufferPtr -= offset;
} }
#else #else
@ -485,7 +497,7 @@ index 3f765f7..488f63f 100644
if (bufferSize == 0) if (bufferSize == 0)
bufferSize = INIT_BUFFER_SIZE; bufferSize = INIT_BUFFER_SIZE;
do { 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; return NULL;
} }
parser->m_bufferLim = newBuf + bufferSize; parser->m_bufferLim = newBuf + bufferSize;
@ -494,18 +506,27 @@ index 3f765f7..488f63f 100644
if (parser->m_bufferPtr) { if (parser->m_bufferPtr) {
memcpy(newBuf, &parser->m_bufferPtr[-keep], memcpy(newBuf, &parser->m_bufferPtr[-keep],
EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr) 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_parsingStatus.parsing = XML_PARSING;
- parser->m_errorCode = parser->m_processor(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); + parser->m_errorCode = callProcessor(
parser, parser->m_bufferPtr, parser->m_parseEndPtr, &parser->m_bufferPtr);
if (parser->m_errorCode != XML_ERROR_NONE) { if (parser->m_errorCode != XML_ERROR_NONE) {
parser->m_eventEndPtr = parser->m_eventPtr; @@ -2561,6 +2577,15 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold(
@@ -2504,6 +2525,15 @@ XML_GetFeatureList(void)
return features;
} }
#endif /* XML_DTD */
+XML_Bool XMLCALL +XML_Bool XMLCALL
+XML_SetReparseDeferralEnabled(XML_Parser parser, XML_Bool enabled) { +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; /* Initially tag->rawName always points into the parse buffer;
for those TAG instances opened while the current parse buffer was 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 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; parser->m_processor = entityValueProcessor;
return entityValueProcessor(parser, next, end, nextPtr); 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; - tokens, but not for the BOM - we would rather like to skip it;
- then, when this routine is entered the next time, XmlPrologTok will - then, when this routine is entered the next time, XmlPrologTok will
- return XML_TOK_INVALID, since the BOM is still in the buffer - 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 + /* XmlPrologTok has now set the encoding based on the BOM it found, and we
+ must move s and nextPtr forward to consume the BOM. + must move s and nextPtr forward to consume the BOM.
+ +
@ -538,34 +557,27 @@ index 3f765f7..488f63f 100644
+ would leave the BOM in the buffer and return. On the next call to this + 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 + function, our XmlPrologTok call would return XML_TOK_INVALID, since it
+ is not valid to have multiple BOMs. + 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) { + 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; *nextPtr = next;
- return XML_ERROR_NONE; - return XML_ERROR_NONE;
+ s = next; + s = next;
} }
/* If we get this token, we have the start of what might be a /* 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 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 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 --- a/expat/tests/minicheck.c
+++ b/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) {
} }
} }
@ -585,17 +597,18 @@ index be1e37e..6c694a0 100644
+} +}
+ +
void 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 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 --- a/expat/tests/minicheck.h
+++ b/expat/tests/minicheck.h +++ b/expat/tests/minicheck.h
@@ -60,7 +60,13 @@ extern "C" { @@ -64,7 +64,14 @@ extern "C" {
{ } \
#define END_TEST } } }
-#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 fail(msg) _fail(__FILE__, __LINE__, msg)
+# define assert_true(cond) \ +# define assert_true(cond) \
+ do { \ + do { \
@ -606,7 +619,7 @@ index a2f57dd..894895a 100644
typedef void (*tcase_setup_function)(void); typedef void (*tcase_setup_function)(void);
typedef void (*tcase_teardown_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. * Prototypes for the actual implementation.
*/ */
@ -619,48 +632,48 @@ index a2f57dd..894895a 100644
Suite *suite_create(const char *name); Suite *suite_create(const char *name);
TCase *tcase_create(const char *name); TCase *tcase_create(const char *name);
diff --git a/expat/tests/runtests.c b/expat/tests/runtests.c 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 --- a/expat/tests/runtests.c
+++ b/expat/tests/runtests.c +++ b/expat/tests/runtests.c
@@ -46,6 +46,7 @@ @@ -54,6 +54,7 @@
#include <stddef.h> /* ptrdiff_t */
#include <ctype.h> #include <ctype.h>
#include <limits.h> #include <limits.h>
#include <stdint.h> /* intptr_t uint64_t */
+#include <time.h> +#include <time.h>
#if ! defined(__cplusplus) #if ! defined(__cplusplus)
# if defined(_MSC_VER) && (_MSC_VER <= 1700) # include <stdbool.h>
@@ -1112,7 +1113,7 @@ START_TEST(test_column_number_after_parse) @@ -1071,7 +1072,7 @@ START_TEST(test_column_number_after_parse) {
const char *text = "<tag></tag>"; const char *text = "<tag></tag>";
XML_Size colno; XML_Size colno;
- if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_FALSE) == XML_STATUS_ERROR) - if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_FALSE)
+ if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) + if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
xml_failure(parser); == XML_STATUS_ERROR)
colno = XML_GetCurrentColumnNumber(parser); xml_failure(g_parser);
if (colno != 11) { colno = XML_GetCurrentColumnNumber(g_parser);
@@ -2769,7 +2770,7 @@ START_TEST(test_default_current) @@ -2582,7 +2583,7 @@ START_TEST(test_default_current) {
if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
XML_TRUE) == XML_STATUS_ERROR) == XML_STATUS_ERROR)
xml_failure(parser); xml_failure(g_parser);
- CharData_CheckXMLChars(&storage, XCS("DCDCDCDCDCDD")); - CharData_CheckXMLChars(&storage, XCS("DCDCDCDCDCDD"));
+ CharData_CheckXMLChars(&storage, XCS("DCDCDCDD")); + CharData_CheckXMLChars(&storage, XCS("DCDCDCDD"));
/* Again, without the defaulting */ /* Again, without the defaulting */
XML_ParserReset(parser, NULL); XML_ParserReset(g_parser, NULL);
@@ -2780,7 +2781,7 @@ START_TEST(test_default_current) @@ -2593,7 +2594,7 @@ START_TEST(test_default_current) {
if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
XML_TRUE) == XML_STATUS_ERROR) == XML_STATUS_ERROR)
xml_failure(parser); xml_failure(g_parser);
- CharData_CheckXMLChars(&storage, XCS("DcccccD")); - CharData_CheckXMLChars(&storage, XCS("DcccccD"));
+ CharData_CheckXMLChars(&storage, XCS("DcccD")); + CharData_CheckXMLChars(&storage, XCS("DcccD"));
/* Now with an internal entity to complicate matters */ /* Now with an internal entity to complicate matters */
XML_ParserReset(parser, NULL); XML_ParserReset(g_parser, NULL);
@@ -4216,6 +4217,19 @@ END_TEST @@ -3946,6 +3947,19 @@ START_TEST(test_get_buffer_3_overflow) {
END_TEST
#endif // defined(XML_CONTEXT_BYTES) #endif // defined(XML_CONTEXT_BYTES)
+START_TEST(test_getbuffer_allocates_on_zero_len) { +START_TEST(test_getbuffer_allocates_on_zero_len) {
+ for (int first_len = 1; first_len >= 0; first_len--) { + for (int first_len = 1; first_len >= 0; first_len--) {
+ XML_Parser parser = XML_ParserCreate(NULL); + XML_Parser parser = XML_ParserCreate(NULL);
@ -675,9 +688,9 @@ index f58f794..486073f 100644
+END_TEST +END_TEST
+ +
/* Test position information macros */ /* Test position information macros */
START_TEST(test_byte_info_at_end) START_TEST(test_byte_info_at_end) {
{ const char *text = "<doc></doc>";
@@ -6747,6 +6761,12 @@ START_TEST(test_utf8_in_start_tags) { @@ -6205,6 +6219,12 @@ START_TEST(test_utf8_in_start_tags) {
char doc[1024]; char doc[1024];
size_t failCount = 0; size_t failCount = 0;
@ -690,7 +703,7 @@ index f58f794..486073f 100644
for (; i < sizeof(cases) / sizeof(cases[0]); i++) { for (; i < sizeof(cases) / sizeof(cases[0]); i++) {
size_t j = 0; size_t j = 0;
for (; j < sizeof(atNameStart) / sizeof(atNameStart[0]); j++) { 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 END_TEST
@ -871,7 +884,8 @@ index f58f794..486073f 100644
+}; +};
+ +
+static void +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; + struct element_decl_data *testdata = (struct element_decl_data *)userData;
+ testdata->count += 1; + testdata->count += 1;
+ XML_FreeContentModel(testdata->parser, model); + XML_FreeContentModel(testdata->parser, model);
@ -879,8 +893,11 @@ index f58f794..486073f 100644
+ +
+static int +static int
+external_inherited_parser(XML_Parser p, const XML_Char *context, +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 *base, const XML_Char *systemId,
+ const XML_Char *UNUSED_P(publicId)) { + const XML_Char *publicId) {
+ UNUSED_P(base);
+ UNUSED_P(systemId);
+ UNUSED_P(publicId);
+ const char *const pre = "<!ELEMENT document ANY>\n"; + const char *const pre = "<!ELEMENT document ANY>\n";
+ const char *const start = "<!ELEMENT "; + const char *const start = "<!ELEMENT ";
+ const char *const end = " ANY>\n"; + const char *const end = " ANY>\n";
@ -1300,23 +1317,23 @@ index f58f794..486073f 100644
/* /*
* Namespaces tests. * Namespaces tests.
*/ */
@@ -7435,13 +8058,13 @@ START_TEST(test_return_ns_triplet) @@ -6902,13 +7529,13 @@ START_TEST(test_return_ns_triplet) {
if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_FALSE)
XML_FALSE) == XML_STATUS_ERROR) == XML_STATUS_ERROR)
xml_failure(parser); xml_failure(g_parser);
- if (! triplet_start_flag) - if (! triplet_start_flag)
- fail("triplet_start_checker not invoked"); - fail("triplet_start_checker not invoked");
/* Check that unsetting "return triplets" fails while still parsing */ /* Check that unsetting "return triplets" fails while still parsing */
XML_SetReturnNSTriplet(parser, XML_FALSE); XML_SetReturnNSTriplet(g_parser, XML_FALSE);
if (_XML_Parse_SINGLE_BYTES(parser, epilog, strlen(epilog), if (_XML_Parse_SINGLE_BYTES(g_parser, epilog, (int)strlen(epilog), XML_TRUE)
XML_TRUE) == XML_STATUS_ERROR) == XML_STATUS_ERROR)
xml_failure(parser); xml_failure(g_parser);
+ if (! triplet_start_flag) + if (! triplet_start_flag)
+ fail("triplet_start_checker not invoked"); + fail("triplet_start_checker not invoked");
if (! triplet_end_flag) if (! triplet_end_flag)
fail("triplet_end_checker not invoked"); fail("triplet_end_checker not invoked");
if (dummy_handler_flags != (DUMMY_START_NS_DECL_HANDLER_FLAG | if (dummy_handler_flags
@@ -12476,6 +13099,7 @@ make_suite(void) @@ -12219,6 +12846,7 @@ make_suite(void) {
#if defined(XML_CONTEXT_BYTES) #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 #endif
@ -1324,10 +1341,11 @@ index f58f794..486073f 100644
tcase_add_test(tc_basic, test_byte_info_at_end); 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_error);
tcase_add_test(tc_basic, test_byte_info_at_cdata); tcase_add_test(tc_basic, test_byte_info_at_cdata);
@@ -12588,6 +13212,14 @@ make_suite(void) @@ -12337,7 +12965,14 @@ make_suite(void) {
tcase_add_test(tc_basic, test_bad_notation); tcase_add_test__ifdef_xml_dtd(tc_basic,
tcase_add_test(tc_basic, test_default_doctype_handler); test_pool_integrity_with_unfinished_attr);
tcase_add_test(tc_basic, test_empty_element_abort); 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_big_tokens_take_linear_time);
+ tcase_add_test(tc_basic, test_set_reparse_deferral); + 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_reparse_deferral_is_inherited);
@ -1336,43 +1354,36 @@ index f58f794..486073f 100644
+ tcase_add_test(tc_basic, test_set_bad_reparse_option); + 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_bypass_heuristic_when_close_to_bufsize);
+ tcase_add_test(tc_basic, test_varying_buffer_fills); + tcase_add_test(tc_basic, test_varying_buffer_fills);
suite_add_tcase(s, tc_namespace); suite_add_tcase(s, tc_namespace);
tcase_add_checked_fixture(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 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 --- a/expat/xmlwf/xmlwf.c
+++ b/expat/xmlwf/xmlwf.c +++ b/expat/xmlwf/xmlwf.c
@@ -35,6 +35,7 @@ @@ -914,6 +914,9 @@ usage(const XML_Char *prog, int rc) {
#include <stdlib.h> T(" -a FACTOR set maximum tolerated [a]mplification factor (default: 100.0)\n")
#include <stddef.h> T(" -b BYTES set number of output [b]ytes needed to activate (default: 8 MiB)\n")
#include <string.h> T("\n")
+#include <errno.h> + 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; + XML_Bool disableDeferral = XML_FALSE;
+ +
int exitCode = XMLWF_EXIT_SUCCESS;
#ifdef _MSC_VER enum XML_ParamEntityParsing paramEntityParsing
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_LEAK_CHECK_DF); = XML_PARAM_ENTITY_PARSING_NEVER;
@@ -1003,6 +1006,11 @@ tmain(int argc, XML_Char **argv) @@ -1089,6 +1094,11 @@ tmain(int argc, XML_Char **argv) {
case T('v'): #endif
showVersion(argv[0]); break;
return 0; }
+ case T('q'): { + case T('q'): {
+ disableDeferral = XML_TRUE; + disableDeferral = XML_TRUE;
+ j++; + j++;
@ -1381,8 +1392,8 @@ index 82d028e..cd26919 100644
case T('\0'): case T('\0'):
if (j > 1) { if (j > 1) {
i++; i++;
@@ -1033,6 +1041,16 @@ tmain(int argc, XML_Char **argv) @@ -1134,6 +1144,16 @@ tmain(int argc, XML_Char **argv) {
exit(1); #endif
} }
+ if (disableDeferral) { + if (disableDeferral) {
@ -1391,13 +1402,28 @@ index 82d028e..cd26919 100644
+ // This prevents tperror(..) from reporting misleading "[..]: Success" + // This prevents tperror(..) from reporting misleading "[..]: Success"
+ errno = EINVAL; + errno = EINVAL;
+ tperror(T("Failed to disable reparse deferral")); + tperror(T("Failed to disable reparse deferral"));
+ exit(1); + exit(XMLWF_EXIT_INTERNAL_ERROR);
+ } + }
+ } + }
+ +
if (requireStandalone) if (requireStandalone)
XML_SetNotStandaloneHandler(parser, notStandalone); XML_SetNotStandaloneHandler(parser, notStandalone);
XML_SetParamEntityParsing(parser, paramEntityParsing); 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 diff --git a/testdata/largefiles/aaaaaa_attr.xml b/testdata/largefiles/aaaaaa_attr.xml
new file mode 100644 new file mode 100644
index 0000000..66e3d25 index 0000000..66e3d25

View File

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

View File

@ -1,32 +1,18 @@
%global unversion 2_2_5 %global unversion 2_5_0
Summary: An XML parser library Summary: An XML parser library
Name: expat Name: expat
Version: %(echo %{unversion} | sed 's/_/./g') Version: %(echo %{unversion} | sed 's/_/./g')
Release: 16%{?dist} Release: 2%{?dist}
Source: https://github.com/libexpat/libexpat/archive/R_%{unversion}.tar.gz#/expat-%{version}.tar.gz Source: https://github.com/libexpat/libexpat/archive/R_%{unversion}.tar.gz#/expat-%{version}.tar.gz
URL: https://libexpat.github.io/ URL: https://libexpat.github.io/
License: MIT License: MIT
BuildRequires: autoconf, libtool, xmlto, gcc-c++ BuildRequires: autoconf, libtool, xmlto, gcc-c++
Patch0: expat-2.2.5-doc2man.patch BuildRequires: make
Patch1: expat-2.2.5-CVE-2018-20843.patch # https://issues.redhat.com/browse/RHEL-24227
Patch2: expat-2.2.5-CVE-2019-15903.patch Patch0: expat-2.5.0-CVE-2023-52425.patch
Patch3: expat-2.2.5-Detect-and-prevent-integer-overflow-in-XML_GetBuffer.patch # https://issues.redhat.com/browse/RHEL-28700
Patch4: expat-2.2.5-Detect-and-prevent-troublesome-left-shifts.patch Patch1: expat-2.5.0-CVE-2024-28757.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
%description %description
This is expat, the C library for parsing XML, written by James Clark. Expat This is expat, the C library for parsing XML, written by James Clark. Expat
@ -54,26 +40,9 @@ Install it if you need to link statically with expat.
%prep %prep
%setup -q -n libexpat-R_%{unversion}/expat %setup -q -n libexpat-R_%{unversion}/expat
%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 .. pushd ..
%patch14 -p1 -b .CVE-2023-52425 %patch0 -p1 -b .CVE-2023-52425
%patch15 -p1 -b .CVE-2024-45490 %patch1 -p1 -b .CVE-2024-28757
%patch16 -p1 -b .CVE-2024-45491
%patch17 -p1 -b .CVE-2024-45492
%patch18 -p1 -b .CVE-2024-50602
popd popd
sed -i 's/install-data-hook/do-nothing-please/' lib/Makefile.am sed -i 's/install-data-hook/do-nothing-please/' lib/Makefile.am
@ -83,10 +52,10 @@ sed -i 's/install-data-hook/do-nothing-please/' lib/Makefile.am
export CFLAGS="$RPM_OPT_FLAGS -fPIC" export CFLAGS="$RPM_OPT_FLAGS -fPIC"
export DOCBOOK_TO_MAN="xmlto man --skip-validation" export DOCBOOK_TO_MAN="xmlto man --skip-validation"
%configure %configure
make %{?_smp_mflags} %make_build
%install %install
make install DESTDIR=$RPM_BUILD_ROOT %make_install
rm -f $RPM_BUILD_ROOT%{_libdir}/*.la rm -f $RPM_BUILD_ROOT%{_libdir}/*.la
@ -105,7 +74,6 @@ make check
%ldconfig_scriptlets %ldconfig_scriptlets
%files %files
%{!?_licensedir:%global license %%doc}
%doc AUTHORS Changes %doc AUTHORS Changes
%license COPYING %license COPYING
%{_bindir}/* %{_bindir}/*
@ -113,61 +81,42 @@ make check
%{_mandir}/*/* %{_mandir}/*/*
%files devel %files devel
%doc doc/reference.html doc/*.png doc/*.css examples/*.c %doc doc/reference.html doc/*.css examples/*.c
%{_libdir}/lib*.so %{_libdir}/lib*.so
%{_libdir}/pkgconfig/*.pc %{_libdir}/pkgconfig/*.pc
%{_includedir}/*.h %{_includedir}/*.h
%{_libdir}/cmake/expat-%{version}
%files static %files static
%{_libdir}/lib*.a %{_libdir}/lib*.a
%changelog %changelog
* Fri Nov 08 2024 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-16 * Tue Feb 13 2024 Tomas Korbar <tkorbar@redhat.com> - 2.5.0-2
- Fix CVE-2024-50602 - Fix parsing of large tokens
- Resolves: RHEL-65062 - Reject direct parameter entity recursion
- Resolves: RHEL-29699
- Resolves: RHEL-29696
* Wed Sep 11 2024 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-15 * Thu Nov 10 2022 Tomas Korbar <tkorbar@redhat.com> - 2.5.0-1
- Rebuild for test reconfiguration - Rebase to version 2.5.0
* Wed Sep 11 2024 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-14
- Fix multiple CVEs
- Fix CVE-2024-45492 integer overflow
- Fix CVE-2024-45491 Integer Overflow or Wraparound
- Fix CVE-2024-45490 Negative Length Parsing Vulnerability
- Resolves: RHEL-57505
- Resolves: RHEL-57493
- Resolves: RHEL-56751
* Tue Mar 26 2024 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-13
- Fix wrongly exposed variables
- Resolves: RHEL-29321
* Thu Mar 21 2024 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-12
- CVE-2023-52425 expat: parsing large tokens can trigger a denial of service
- Resolves: RHEL-29321
* Mon Nov 14 2022 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-11
- CVE-2022-43680 expat: use-after free caused by overeager destruction of a shared DTD in XML_ExternalEntityParserCreate
- Resolves: CVE-2022-43680 - Resolves: CVE-2022-43680
* Fri Sep 30 2022 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-10 * Thu Sep 29 2022 Tomas Korbar <tkorbar@redhat.com> - 2.4.9-1
- Ensure raw tagnames are safe exiting internalEntityParser - Rebase to version 2.4.9
- Resolves: CVE-2022-40674 - Resolves: CVE-2022-40674
* Fri May 06 2022 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-9 * Tue Apr 26 2022 Tomas Korbar <tkorbar@redhat.com> - 2.4.7-1
- Fix multiple CVEs - Rebase to version 2.4.7
- Resolves: CVE-2022-25314 - Resolves: rhbz#2067201
- Resolves: CVE-2022-25313 - Resolves: CVE-2022-25313
- Resolves: CVE-2022-25314
- Resolves: CVE-2022-25236
* Mon Mar 14 2022 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-8 * Mon Mar 14 2022 Tomas Korbar <tkorbar@redhat.com> - 2.2.10-11
- Improve patch for CVE-2022-25236 - Improve fix for CVE-2022-25236
- Related: CVE-2022-25236 - Related: CVE-2022-25236
* Fri Mar 04 2022 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-7 * Mon Feb 28 2022 Tomas Korbar <tkorbar@redhat.com> - 2.2.10-10
- Fix patch for CVE-2022-25235
- Resolves: CVE-2022-25235
* Thu Mar 03 2022 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-6
- Fix multiple CVEs - Fix multiple CVEs
- CVE-2022-25236 expat: namespace-separator characters in "xmlns[:prefix]" attribute values can lead to arbitrary code execution - CVE-2022-25236 expat: namespace-separator characters in "xmlns[:prefix]" attribute values can lead to arbitrary code execution
- CVE-2022-25235 expat: malformed 2- and 3-byte UTF-8 sequences can lead to arbitrary code execution - CVE-2022-25235 expat: malformed 2- and 3-byte UTF-8 sequences can lead to arbitrary code execution
@ -176,20 +125,25 @@ make check
- Resolves: CVE-2022-25235 - Resolves: CVE-2022-25235
- Resolves: CVE-2022-25315 - Resolves: CVE-2022-25315
* Fri Feb 14 2022 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-5 * Thu Feb 10 2022 Tomas Korbar <tkorbar@redhat.com> - 2.2.10-9
- Fix multiple CVEs
- CVE-2022-23852 expat: integer overflow in function XML_GetBuffer - CVE-2022-23852 expat: integer overflow in function XML_GetBuffer
- Resolves: CVE-2022-23852
* Thu Feb 10 2022 Tomas Korbar <tkorbar@redhat.com> - 2.2.10-8
- CVE-2021-45960 expat: Large number of prefixed XML attributes on a single tag can crash libexpat - CVE-2021-45960 expat: Large number of prefixed XML attributes on a single tag can crash libexpat
- Resolves: CVE-2021-45960
* Wed Feb 09 2022 Tomas Korbar <tkorbar@redhat.com> - 2.2.10-7
- CVE-2021-46143 expat: Integer overflow in doProlog in xmlparse.c - CVE-2021-46143 expat: Integer overflow in doProlog in xmlparse.c
- Resolves: CVE-2021-46143
* Wed Feb 09 2022 Tomas Korbar <tkorbar@redhat.com> - 2.2.10-6
- CVE-2022-22827 Integer overflow in storeAtts in xmlparse.c - CVE-2022-22827 Integer overflow in storeAtts in xmlparse.c
- CVE-2022-22826 Integer overflow in nextScaffoldPart in xmlparse.c - CVE-2022-22826 Integer overflow in nextScaffoldPart in xmlparse.c
- CVE-2022-22825 Integer overflow in lookup in xmlparse.c - CVE-2022-22825 Integer overflow in lookup in xmlparse.c
- CVE-2022-22824 Integer overflow in defineAttribute in xmlparse.c - CVE-2022-22824 Integer overflow in defineAttribute in xmlparse.c
- CVE-2022-22823 Integer overflow in build_model in xmlparse.c - CVE-2022-22823 Integer overflow in build_model in xmlparse.c
- CVE-2022-22822 Integer overflow in addBinding in xmlparse.c - CVE-2022-22822 Integer overflow in addBinding in xmlparse.c
- Resolves: CVE-2022-23852
- Resolves: CVE-2021-45960
- Resolves: CVE-2021-46143
- Resolves: CVE-2022-22827 - Resolves: CVE-2022-22827
- Resolves: CVE-2022-22826 - Resolves: CVE-2022-22826
- Resolves: CVE-2022-22825 - Resolves: CVE-2022-22825
@ -197,8 +151,46 @@ make check
- Resolves: CVE-2022-22823 - Resolves: CVE-2022-22823
- Resolves: CVE-2022-22822 - Resolves: CVE-2022-22822
* Fri Apr 24 2020 Joe Orton <jorton@redhat.com> - 2.2.5-4 * Mon Feb 07 2022 Tomas Korbar <tkorbar@redhat.com> - 2.2.10-5
- add security fixes for CVE-2018-20843, CVE-2019-15903 - CVE-2022-23990 expat: integer overflow in the doProlog function
- Resolve: rhbz#2050503
* Mon Aug 09 2021 Mohan Boddu <mboddu@redhat.com> - 2.2.10-4
- Rebuilt for IMA sigs, glibc 2.34, aarch64 flags
Related: rhbz#1991688
* Thu Apr 15 2021 Mohan Boddu <mboddu@redhat.com> - 2.2.10-3
- Rebuilt for RHEL 9 BETA on Apr 15th 2021. Related: rhbz#1947937
* Tue Jan 26 2021 Fedora Release Engineering <releng@fedoraproject.org> - 2.2.10-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
* Fri Nov 13 2020 Joe Orton <jorton@redhat.com> - 2.2.10-1
- update to 2.2.10 (#1884940)
* Mon Jul 27 2020 Fedora Release Engineering <releng@fedoraproject.org> - 2.2.8-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
* Tue Jan 28 2020 Fedora Release Engineering <releng@fedoraproject.org> - 2.2.8-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild
* Mon Sep 16 2019 Joe Orton <jorton@redhat.com> - 2.2.8-1
- update to 2.2.8 (#1752167)
* Thu Jul 25 2019 Fedora Release Engineering <releng@fedoraproject.org> - 2.2.7-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild
* Thu Jun 27 2019 Joe Orton <jorton@redhat.com> - 2.2.7-1
- update to 2.2.7 (#1723724, #1722224)
* Thu Jan 31 2019 Fedora Release Engineering <releng@fedoraproject.org> - 2.2.6-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
* Wed Aug 15 2018 Joe Orton <jorton@redhat.com> - 2.2.6-1
- update to 2.2.6
* Fri Jul 13 2018 Fedora Release Engineering <releng@fedoraproject.org> - 2.2.5-4
- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild
* Wed Feb 07 2018 Fedora Release Engineering <releng@fedoraproject.org> - 2.2.5-3 * Wed Feb 07 2018 Fedora Release Engineering <releng@fedoraproject.org> - 2.2.5-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild - Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild

25
gating.yaml Normal file
View File

@ -0,0 +1,25 @@
--- !Policy
product_versions:
- fedora-*
decision_context: bodhi_update_push_testing
subject_type: koji_build
rules:
- !PassingTestCaseRule {test_case_name: fedora-ci.koji-build./plans/tier1-public.functional}
#Rawhide
--- !Policy
product_versions:
- fedora-*
decision_context: bodhi_update_push_stable
subject_type: koji_build
rules:
- !PassingTestCaseRule {test_case_name: fedora-ci.koji-build./plans/tier1-public.functional}
#gating rhel
--- !Policy
product_versions:
- rhel-*
decision_context: osci_compose_gate
rules:
- !PassingTestCaseRule {test_case_name: osci.brew-build./plans/tier1-public.functional}
- !PassingTestCaseRule {test_case_name: osci.brew-build./plans/tier1-internal.functional}

36
plans.fmf Normal file
View File

@ -0,0 +1,36 @@
/tier1-internal:
plan:
import:
url: https://src.fedoraproject.org/tests/expat.git
name: /plans/tier1/internal
/tier1-public:
plan:
import:
url: https://src.fedoraproject.org/tests/expat.git
name: /plans/tier1/public
/tier2-tier3-internal:
plan:
import:
url: https://src.fedoraproject.org/tests/expat.git
name: /plans/tier2-tier3/internal
/tier2-tier3-public:
plan:
import:
url: https://src.fedoraproject.org/tests/expat.git
name: /plans/tier2-tier3/public
/others-internal:
plan:
import:
url: https://src.fedoraproject.org/tests/expat.git
name: /plans/others/internal
/others-public:
plan:
import:
url: https://src.fedoraproject.org/tests/expat.git
name: /plans/others/public

1
sources Normal file
View File

@ -0,0 +1 @@
SHA512 (expat-2.5.0.tar.gz) = 779f0d0f3f2d8b33db0fd044864ab5ab1a40f20501f792fe90ad0d18de536c4765c3749f120e21fec11a0e6c89af1dc576d1fe261c871ca44a594f7b61fd1d9e