diff --git a/SOURCES/expat-2.5.0-CVE-2026-45186.patch b/SOURCES/expat-2.5.0-CVE-2026-45186.patch new file mode 100644 index 0000000..6b2cc1f --- /dev/null +++ b/SOURCES/expat-2.5.0-CVE-2026-45186.patch @@ -0,0 +1,635 @@ +From 88e9daef8fdaea58da12044cd16f5c9c1f201bfa Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Berkay=20Eren=20=C3=9Cr=C3=BCn?= +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?= +Date: Fri, 13 Mar 2026 13:27:31 +0100 +Subject: [PATCH 2/6] test(attlist): Cover duplicate attribute names + +Co-authored-by: Sebastian Pipping +--- + 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 ++ = "\n" ++ "]>\n" ++ "\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 ++ = "\n" ++ "]>\n" ++ "\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 ++ = "\n" ++ "]>\n" ++ "\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 = "\n" ++ " \n" ++ "]>\n" ++ "\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 = "\n" ++ " \n" ++ " \n" ++ "]>\n" ++ "\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 ++ = "\n" ++ " \n" ++ " \n" ++ "]>\n" ++ "\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 = "\n" ++ " \n" ++ " \n" ++ "]>\n" ++ "\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 +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 +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 +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 +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 + diff --git a/SPECS/expat.spec b/SPECS/expat.spec index 0620993..ce209c2 100644 --- a/SPECS/expat.spec +++ b/SPECS/expat.spec @@ -3,7 +3,7 @@ Summary: An XML parser library Name: expat Version: %(echo %{unversion} | sed 's/_/./g') -Release: 1%{?dist} +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 @@ -25,6 +25,9 @@ Patch5: expat-2.5.0-CVE-2024-50602.patch Patch6: expat-2.5.0-CVE-2024-8176.patch # https://issues.redhat.com/browse/RHEL-114618 Patch7: expat-2.5.0-CVE-2025-59375.patch +# https://issues.redhat.com/browse/RHEL-177979 +# https://github.com/libexpat/libexpat/pull/1216 +Patch8: expat-2.5.0-CVE-2026-45186.patch %description This is expat, the C library for parsing XML, written by James Clark. Expat @@ -61,6 +64,7 @@ pushd .. %patch5 -p1 -b .CVE-2024-50602 %patch6 -p1 -b .CVE-2024-8176 %patch7 -p1 -b .CVE-2025-59375 +%patch8 -p1 -b .CVE-2026-45186 popd sed -i 's/install-data-hook/do-nothing-please/' lib/Makefile.am @@ -110,6 +114,10 @@ make check %{_libdir}/lib*.a %changelog +* Thu May 28 2026 RHEL Packaging Agent - 2.5.0-2 +- Fix CVE-2026-45186 +- Resolves: RHEL-177979 + * Wed Nov 19 2025 Tomas Korbar - 2.5.0-1 - Rebase to version 2.5.0 - Fix CVE-2025-59375