expat: backport CVE-2026-45186 fix (attribute collision check DoS)
Backport CVE-2026-45186 fix from upstream PR #1216 to expat-2.7.3. The patch includes 7 cherry-picked commits: 4 test infrastructure updates and 3 library changes that introduce a hash table for attribute name deduplication in ELEMENT_TYPE. Added expat-2.7.3-CVE-2026-45186.patch and changed %autosetup to use -p2 to correctly strip the a/expat/ path prefix. CVE: CVE-2026-45186 Upstream patches: - https://github.com/libexpat/libexpat/pull/1216.patch Resolves: RHEL-177983 This commit was backported by Ymir, a Red Hat Enterprise Linux software maintenance AI agent. Assisted-by: Ymir
This commit is contained in:
parent
eef65ac89e
commit
66bbbc6015
586
expat-2.7.3-CVE-2026-45186.patch
Normal file
586
expat-2.7.3-CVE-2026-45186.patch
Normal file
@ -0,0 +1,586 @@
|
||||
From 5ec3c665d9b29f7c0373873080a88f4f826d2d6c 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/7] Make "counting_start_element_handler" count default attrs
|
||||
|
||||
---
|
||||
expat/tests/basic_tests.c | 8 ++++----
|
||||
expat/tests/handlers.c | 2 +-
|
||||
expat/tests/handlers.h | 1 +
|
||||
3 files changed, 6 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/expat/tests/basic_tests.c b/expat/tests/basic_tests.c
|
||||
index 0231e094..cc356e95 100644
|
||||
--- a/expat/tests/basic_tests.c
|
||||
+++ b/expat/tests/basic_tests.c
|
||||
@@ -2466,9 +2466,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;
|
||||
|
||||
@@ -5499,7 +5499,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_Parser parser = XML_ParserCreate(NULL);
|
||||
diff --git a/expat/tests/handlers.c b/expat/tests/handlers.c
|
||||
index 5bca2b1f..72b4240f 100644
|
||||
--- a/expat/tests/handlers.c
|
||||
+++ b/expat/tests/handlers.c
|
||||
@@ -136,7 +136,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))
|
||||
diff --git a/expat/tests/handlers.h b/expat/tests/handlers.h
|
||||
index fa6267fb..1970fe66 100644
|
||||
--- a/expat/tests/handlers.h
|
||||
+++ b/expat/tests/handlers.h
|
||||
@@ -88,6 +88,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;
|
||||
|
||||
From 5cbc6b297c87021b5659d4520654cf746b2a4850 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/7] test(attlist): Cover duplicate attribute names
|
||||
|
||||
Co-authored-by: Sebastian Pipping <sebastian@pipping.org>
|
||||
---
|
||||
expat/tests/basic_tests.c | 282 ++++++++++++++++++++++++++++++++++++++
|
||||
1 file changed, 282 insertions(+)
|
||||
|
||||
diff --git a/expat/tests/basic_tests.c b/expat/tests/basic_tests.c
|
||||
index cc356e95..9729757e 100644
|
||||
--- a/expat/tests/basic_tests.c
|
||||
+++ b/expat/tests/basic_tests.c
|
||||
@@ -2489,6 +2489,279 @@ START_TEST(test_attributes) {
|
||||
}
|
||||
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().
|
||||
*/
|
||||
@@ -6311,6 +6584,15 @@ make_basic_test_case(Suite *s) {
|
||||
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__if_xml_ge(tc_basic, test_reset_in_entity);
|
||||
tcase_add_test(tc_basic, test_resume_invalid_parse);
|
||||
tcase_add_test(tc_basic, test_resume_resuspended);
|
||||
|
||||
From 58877476211c8cc6337e7d0617d85d470437126a Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Mon, 20 Apr 2026 13:44:43 +0200
|
||||
Subject: [PATCH 3/7] tests: Define .attributes the first time around
|
||||
|
||||
---
|
||||
expat/tests/basic_tests.c | 10 ++++------
|
||||
1 file changed, 4 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/expat/tests/basic_tests.c b/expat/tests/basic_tests.c
|
||||
index 9729757e..cdcae029 100644
|
||||
--- a/expat/tests/basic_tests.c
|
||||
+++ b/expat/tests/basic_tests.c
|
||||
@@ -2466,11 +2466,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, 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;
|
||||
|
||||
XML_Parser parser = XML_ParserCreate(NULL);
|
||||
assert_true(parser != NULL);
|
||||
@@ -5772,8 +5770,8 @@ 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}};
|
||||
|
||||
XML_Parser parser = XML_ParserCreate(NULL);
|
||||
ParserAndElementInfo parserPlusElemenInfo = {parser, info};
|
||||
|
||||
From b3afb59fdcdb11cb994f65a3566bd97de793a2ff Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Mon, 13 Apr 2026 01:34:03 +0200
|
||||
Subject: [PATCH 4/7] tests: Make counting_start_element_handler enforce
|
||||
complete attribute lists
|
||||
|
||||
---
|
||||
expat/tests/handlers.c | 3 +++
|
||||
1 file changed, 3 insertions(+)
|
||||
|
||||
diff --git a/expat/tests/handlers.c b/expat/tests/handlers.c
|
||||
index 72b4240f..8008bc21 100644
|
||||
--- a/expat/tests/handlers.c
|
||||
+++ b/expat/tests/handlers.c
|
||||
@@ -154,6 +154,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);
|
||||
}
|
||||
|
||||
void XMLCALL
|
||||
|
||||
From c54aa8e912db8d0bcccbc3ceebaea011865a5024 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Sun, 8 Mar 2026 22:14:41 +0100
|
||||
Subject: [PATCH 5/7] 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 a187a3a1..5cf2c279 100644
|
||||
--- a/expat/lib/xmlparse.c
|
||||
+++ b/expat/lib/xmlparse.c
|
||||
@@ -7685,8 +7685,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
|
||||
|
||||
From be28a1e7abf734463b0388a9dc58320892e4d256 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Sun, 8 Mar 2026 23:05:49 +0100
|
||||
Subject: [PATCH 6/7] 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 5cf2c279..8c0d2a43 100644
|
||||
--- a/expat/lib/xmlparse.c
|
||||
+++ b/expat/lib/xmlparse.c
|
||||
@@ -389,6 +389,7 @@ typedef struct {
|
||||
int nDefaultAtts;
|
||||
int allocDefaultAtts;
|
||||
DEFAULT_ATTRIBUTE *defaultAtts;
|
||||
+ HASH_TABLE defaultAttsNames;
|
||||
} ELEMENT_TYPE;
|
||||
|
||||
typedef struct {
|
||||
@@ -3845,6 +3846,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;
|
||||
}
|
||||
@@ -7526,6 +7529,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);
|
||||
}
|
||||
@@ -7567,6 +7571,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);
|
||||
}
|
||||
@@ -7660,6 +7665,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
|
||||
@@ -7696,6 +7705,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;
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8423,6 +8438,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 {
|
||||
|
||||
From b8960a10a17a1706ad9c07b31ed10fbf1993a544 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Sun, 8 Mar 2026 23:06:29 +0100
|
||||
Subject: [PATCH 7/7] 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 8c0d2a43..c5835738 100644
|
||||
--- a/expat/lib/xmlparse.c
|
||||
+++ b/expat/lib/xmlparse.c
|
||||
@@ -7162,10 +7162,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;
|
||||
}
|
||||
@@ -7212,6 +7212,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;
|
||||
}
|
||||
@ -10,6 +10,10 @@ Source2: https://keys.openpgp.org/vks/v1/by-fingerprint/3176EF7DB2367F1FCA4F306B
|
||||
URL: https://libexpat.github.io/
|
||||
VCS: git:https://github.com/libexpat/libexpat.git
|
||||
License: MIT
|
||||
|
||||
# https://issues.redhat.com/browse/RHEL-177983
|
||||
# https://github.com/libexpat/libexpat/pull/1216
|
||||
Patch0: expat-2.7.3-CVE-2026-45186.patch
|
||||
BuildRequires: autoconf, libtool, xmlto, gcc-c++
|
||||
BuildRequires: make
|
||||
BuildRequires: gnupg2
|
||||
@ -40,7 +44,7 @@ Install it if you need to link statically with expat.
|
||||
|
||||
%prep
|
||||
%{gpgverify} --keyring='%{SOURCE2}' --signature='%{SOURCE1}' --data='%{SOURCE0}'
|
||||
%autosetup
|
||||
%autosetup -p2
|
||||
sed -i 's/install-data-hook/do-nothing-please/' lib/Makefile.am
|
||||
./buildconf.sh
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user