Compare commits

...

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

19 changed files with 161 additions and 5860 deletions

View File

@ -1 +0,0 @@
03d9882ede56aa48919fbf50fe17614630257a82 SOURCES/expat-2.5.0.tar.gz

1
.fmf/version Normal file
View File

@ -0,0 +1 @@
1

24
.gitignore vendored
View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

View File

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

1
ci.fmf Normal file
View File

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

View File

@ -3,7 +3,7 @@
Summary: An XML parser library
Name: expat
Version: %(echo %{unversion} | sed 's/_/./g')
Release: 2%{?dist}.2
Release: 2%{?dist}
Source: https://github.com/libexpat/libexpat/archive/R_%{unversion}.tar.gz#/expat-%{version}.tar.gz
URL: https://libexpat.github.io/
License: MIT
@ -13,27 +13,6 @@ BuildRequires: make
Patch0: expat-2.5.0-CVE-2023-52425.patch
# https://issues.redhat.com/browse/RHEL-28700
Patch1: expat-2.5.0-CVE-2024-28757.patch
# https://issues.redhat.com/browse/RHEL-56761
Patch2: expat-2.5.0-CVE-2024-45490.patch
# https://issues.redhat.com/browse/RHEL-57520
Patch3: expat-2.5.0-CVE-2024-45491.patch
# https://issues.redhat.com/browse/RHEL-57511
Patch4: expat-2.5.0-CVE-2024-45492.patch
# https://issues.redhat.com/browse/RHEL-65066
Patch5: expat-2.5.0-CVE-2024-50602.patch
# https://issues.redhat.com/browse/RHEL-57489
Patch6: expat-2.5.0-CVE-2024-8176.patch
# https://issues.redhat.com/browse/RHEL-114618
Patch7: expat-2.5.0-CVE-2025-59375.patch
# https://issues.redhat.com/browse/RHEL-177979
# https://github.com/libexpat/libexpat/pull/1216
Patch8: expat-2.5.0-CVE-2026-45186.patch
# https://issues.redhat.com/browse/RHEL-220979
# https://github.com/libexpat/libexpat/pull/1246
Patch9: expat-2.5.0-CVE-2026-50219.patch
# https://issues.redhat.com/browse/RHEL-221012
# https://github.com/libexpat/libexpat/pull/1272
Patch10: expat-2.5.0-CVE-2026-56132.patch
%description
This is expat, the C library for parsing XML, written by James Clark. Expat
@ -64,15 +43,6 @@ Install it if you need to link statically with expat.
pushd ..
%patch0 -p1 -b .CVE-2023-52425
%patch1 -p1 -b .CVE-2024-28757
%patch2 -p1 -b .CVE-2024-45490
%patch3 -p1 -b .CVE-2024-45491
%patch4 -p1 -b .CVE-2024-45492
%patch5 -p1 -b .CVE-2024-50602
%patch6 -p1 -b .CVE-2024-8176
%patch7 -p1 -b .CVE-2025-59375
%patch8 -p1 -b .CVE-2026-45186
%patch9 -p1 -b .CVE-2026-50219
%patch10 -p1 -b .CVE-2026-56132
popd
sed -i 's/install-data-hook/do-nothing-please/' lib/Makefile.am
@ -82,21 +52,21 @@ sed -i 's/install-data-hook/do-nothing-please/' lib/Makefile.am
export CFLAGS="$RPM_OPT_FLAGS -fPIC"
export DOCBOOK_TO_MAN="xmlto man --skip-validation"
%configure
make %{?_smp_mflags}
%make_build
%install
make install DESTDIR=$RPM_BUILD_ROOT
%make_install
rm -f $RPM_BUILD_ROOT%{_libdir}/*.la
%check
bash -c "for i in {1..500000}; do printf AAAAAAAAAAAAAAAAAAAA >> achars.txt; done"
for testfile in ../testdata/largefiles/aaaaaa_*; do
first_part="$(sed 's/\(.*\)ACHARS.*/\1/g' $testfile)"
second_part="$(sed 's/.*ACHARS\(.*\)/\1/g' $testfile)"
printf "$first_part" > "$testfile"
cat achars.txt >> "$testfile"
printf "$second_part" >> "$testfile"
first_part="$(sed 's/\(.*\)ACHARS.*/\1/g' $testfile)"
second_part="$(sed 's/.*ACHARS\(.*\)/\1/g' $testfile)"
printf "$first_part" > "$testfile"
cat achars.txt >> "$testfile"
printf "$second_part" >> "$testfile"
done
make check
@ -104,7 +74,6 @@ make check
%ldconfig_scriptlets
%files
%{!?_licensedir:%global license %%doc}
%doc AUTHORS Changes
%license COPYING
%{_bindir}/*
@ -122,73 +91,32 @@ make check
%{_libdir}/lib*.a
%changelog
* Fri Jul 31 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 2.5.0-2.2
- Fix CVE-2026-56132: out-of-bound scaffolding index store in doProlog
- Resolves: RHEL-221012
* Tue Feb 13 2024 Tomas Korbar <tkorbar@redhat.com> - 2.5.0-2
- Fix parsing of large tokens
- Reject direct parameter entity recursion
- Resolves: RHEL-29699
- Resolves: RHEL-29696
* Fri Jul 31 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 2.5.0-2.1
- Fix CVE-2026-50219
- Resolves: RHEL-220979
* Thu May 28 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 2.5.0-2
- Fix CVE-2026-45186
- Resolves: RHEL-177979
* Wed Nov 19 2025 Tomas Korbar <tkorbar@redhat.com> - 2.5.0-1
* Thu Nov 10 2022 Tomas Korbar <tkorbar@redhat.com> - 2.5.0-1
- Rebase to version 2.5.0
- Fix CVE-2025-59375
- Resolves: RHEL-114618
* Mon Apr 07 2025 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-17
- Fix CVE-2024-8176
- Resolves: RHEL-57477
* Fri Nov 08 2024 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-16
- Fix CVE-2024-50602
- Resolves: RHEL-65062
* Wed Sep 11 2024 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-15
- Rebuild for test reconfiguration
* Wed Sep 11 2024 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-14
- Fix multiple CVEs
- Fix CVE-2024-45492 integer overflow
- Fix CVE-2024-45491 Integer Overflow or Wraparound
- Fix CVE-2024-45490 Negative Length Parsing Vulnerability
- Resolves: RHEL-57505
- Resolves: RHEL-57493
- Resolves: RHEL-56751
* Tue Mar 26 2024 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-13
- Fix wrongly exposed variables
- Resolves: RHEL-29321
* Thu Mar 21 2024 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-12
- CVE-2023-52425 expat: parsing large tokens can trigger a denial of service
- Resolves: RHEL-29321
* Mon Nov 14 2022 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-11
- CVE-2022-43680 expat: use-after free caused by overeager destruction of a shared DTD in XML_ExternalEntityParserCreate
- Resolves: CVE-2022-43680
* Fri Sep 30 2022 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-10
- Ensure raw tagnames are safe exiting internalEntityParser
* Thu Sep 29 2022 Tomas Korbar <tkorbar@redhat.com> - 2.4.9-1
- Rebase to version 2.4.9
- Resolves: CVE-2022-40674
* Fri May 06 2022 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-9
- Fix multiple CVEs
- Resolves: CVE-2022-25314
* Tue Apr 26 2022 Tomas Korbar <tkorbar@redhat.com> - 2.4.7-1
- Rebase to version 2.4.7
- Resolves: rhbz#2067201
- 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
- Improve patch for CVE-2022-25236
* Mon Mar 14 2022 Tomas Korbar <tkorbar@redhat.com> - 2.2.10-11
- Improve fix for CVE-2022-25236
- Related: CVE-2022-25236
* Fri Mar 04 2022 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-7
- Fix patch for CVE-2022-25235
- Resolves: CVE-2022-25235
* Thu Mar 03 2022 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-6
* Mon Feb 28 2022 Tomas Korbar <tkorbar@redhat.com> - 2.2.10-10
- Fix multiple CVEs
- 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
@ -197,20 +125,25 @@ make check
- Resolves: CVE-2022-25235
- Resolves: CVE-2022-25315
* Fri Feb 14 2022 Tomas Korbar <tkorbar@redhat.com> - 2.2.5-5
- Fix multiple CVEs
* Thu Feb 10 2022 Tomas Korbar <tkorbar@redhat.com> - 2.2.10-9
- 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
- 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
- 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-22826 Integer overflow in nextScaffoldPart 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-22823 Integer overflow in build_model 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-22826
- Resolves: CVE-2022-22825
@ -218,8 +151,46 @@ make check
- Resolves: CVE-2022-22823
- Resolves: CVE-2022-22822
* Fri Apr 24 2020 Joe Orton <jorton@redhat.com> - 2.2.5-4
- add security fixes for CVE-2018-20843, CVE-2019-15903
* Mon Feb 07 2022 Tomas Korbar <tkorbar@redhat.com> - 2.2.10-5
- 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
- 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