diff --git a/expat-2.5.0-CVE-2026-56132.patch b/expat-2.5.0-CVE-2026-56132.patch new file mode 100644 index 0000000..0fd13c8 --- /dev/null +++ b/expat-2.5.0-CVE-2026-56132.patch @@ -0,0 +1,288 @@ +From b5b73d23072fd277b3816b45c027238260467947 Mon Sep 17 00:00:00 2001 +From: Matthew Fernandez +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 +--- + 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 +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 parser’s +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 parent’s 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 +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 +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 +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[] ++ = "\n" ++ "\n" ++ "%e;\n" ++ "\n" ++ "]>\n" ++ ""; ++ ExtOption options[] ++ = {{XCS("ext"), ++ ""}, ++ {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 +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 +--- + 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 +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); diff --git a/expat.spec b/expat.spec index a66e8e1..d84d3d4 100644 --- a/expat.spec +++ b/expat.spec @@ -3,7 +3,7 @@ Summary: An XML parser library Name: expat Version: %(echo %{unversion} | sed 's/_/./g') -Release: 2%{?dist}.1 +Release: 2%{?dist}.2 Source: https://github.com/libexpat/libexpat/archive/R_%{unversion}.tar.gz#/expat-%{version}.tar.gz URL: https://libexpat.github.io/ License: MIT @@ -31,6 +31,9 @@ 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 @@ -69,6 +72,7 @@ pushd .. %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 @@ -118,6 +122,10 @@ make check %{_libdir}/lib*.a %changelog +* Fri Jul 31 2026 RHEL Packaging Agent - 2.5.0-2.2 +- Fix CVE-2026-56132: out-of-bound scaffolding index store in doProlog +- Resolves: RHEL-221012 + * Fri Jul 31 2026 RHEL Packaging Agent - 2.5.0-2.1 - Fix CVE-2026-50219 - Resolves: RHEL-220979