From 6a50b1efe182b38c4013f7fc122bc8c588c838d3 Mon Sep 17 00:00:00 2001 From: Azure Linux Security Servicing Account Date: Wed, 19 Aug 2026 18:37:18 +0000 Subject: [PATCH] Patch expat for CVE-2026-66046 --- SPECS/expat/CVE-2026-66046.patch | 199 ++++++++++++++++++ SPECS/expat/expat.spec | 6 +- .../manifests/package/pkggen_core_aarch64.txt | 6 +- .../manifests/package/pkggen_core_x86_64.txt | 6 +- .../manifests/package/toolchain_aarch64.txt | 8 +- .../manifests/package/toolchain_x86_64.txt | 8 +- 6 files changed, 218 insertions(+), 15 deletions(-) create mode 100644 SPECS/expat/CVE-2026-66046.patch diff --git a/SPECS/expat/CVE-2026-66046.patch b/SPECS/expat/CVE-2026-66046.patch new file mode 100644 index 00000000000..d03fcb7fb67 --- /dev/null +++ b/SPECS/expat/CVE-2026-66046.patch @@ -0,0 +1,199 @@ +From 6541096ee48868ebbbf64b05260cbb3bd698a772 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping +Date: Thu, 13 Aug 2026 15:47:24 +0200 +Subject: [PATCH 1/2] lib: Rename hash table `defaultAttsNames` to + `defaultAttForName` + +It was previously used as a "set". This prepares for the upcoming +change to a true "dictionary". +--- + lib/xmlparse.c | 24 ++++++++++++------------ + 1 file changed, 12 insertions(+), 12 deletions(-) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index ac79b9c..36105f8 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -394,7 +394,7 @@ typedef struct { + size_t nDefaultAtts; + size_t allocDefaultAtts; + DEFAULT_ATTRIBUTE *defaultAtts; +- HASH_TABLE defaultAttsNames; ++ HASH_TABLE defaultAttForName; + } ELEMENT_TYPE; + + typedef struct { +@@ -3837,8 +3837,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 (! elementType->defaultAttForName.parser) ++ hashTableInit(&(elementType->defaultAttForName), parser); + if (parser->m_ns && ! setElementTypePrefix(parser, elementType)) + return XML_ERROR_NO_MEMORY; + } +@@ -7239,7 +7239,7 @@ defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, XML_Bool isCdata, + /* The handling of default attributes gets messed up if we have + a default which duplicates a non-default. */ + NAMED *const nameFound +- = lookup(parser, &(type->defaultAttsNames), attId->name, 0); ++ = lookup(parser, &(type->defaultAttForName), attId->name, 0); + if (nameFound) + return 1; + if (isId && ! type->idAtt && ! attId->xmlns) +@@ -7276,7 +7276,7 @@ defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, XML_Bool isCdata, + attId->maybeTokenized = XML_TRUE; + + NAMED *const nameAddedOrFound +- = lookup(parser, &(type->defaultAttsNames), attId->name, sizeof(NAMED)); ++ = lookup(parser, &(type->defaultAttForName), attId->name, sizeof(NAMED)); + if (! nameAddedOrFound) + return 0; + +@@ -7597,7 +7597,7 @@ dtdReset(DTD *p, XML_Parser parser) { + ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter); + if (! e) + break; +- hashTableDestroy(&(e->defaultAttsNames)); ++ hashTableDestroy(&(e->defaultAttForName)); + FREE(parser, e->defaultAtts); + } + hashTableClear(&(p->generalEntities)); +@@ -7639,7 +7639,7 @@ dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser) { + ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter); + if (! e) + break; +- hashTableDestroy(&(e->defaultAttsNames)); ++ hashTableDestroy(&(e->defaultAttForName)); + FREE(parser, e->defaultAtts); + } + hashTableDestroy(&(p->generalEntities)); +@@ -7732,8 +7732,8 @@ dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, + if (! newE) + return 0; + +- if (! newE->defaultAttsNames.parser) +- hashTableInit(&(newE->defaultAttsNames), parser); ++ if (! newE->defaultAttForName.parser) ++ hashTableInit(&(newE->defaultAttForName), parser); + + if (oldE->nDefaultAtts) { + /* Detect and prevent integer overflow. */ +@@ -7766,7 +7766,7 @@ dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, + } else + newE->defaultAtts[i].value = NULL; + +- NAMED *const nameAddedOrFound = lookup(parser, &(newE->defaultAttsNames), ++ NAMED *const nameAddedOrFound = lookup(parser, &(newE->defaultAttForName), + attributeName, sizeof(NAMED)); + if (! nameAddedOrFound) { + return 0; +@@ -8535,8 +8535,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->defaultAttForName.parser) ++ hashTableInit(&(ret->defaultAttForName), getRootParserOf(parser, NULL)); + if (ret->name != name) + poolDiscard(&dtd->pool); + else { +-- +2.45.4 + + +From b73b995a5a27f19a310fb9239c6199827fabca32 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping +Date: Thu, 13 Aug 2026 16:39:35 +0200 +Subject: [PATCH 2/2] lib: Migrate .isCdata lookup from a linear loop to a hash + table lookup + +.. to resolve quadratic runtime + +Signed-off-by: Azure Linux Security Servicing Account +Upstream-reference: https://github.com/libexpat/libexpat/pull/1321.patch +--- + lib/xmlparse.c | 48 ++++++++++++++++++++++++++++++++++++++++-------- + 1 file changed, 40 insertions(+), 8 deletions(-) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index 36105f8..0865d4b 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -381,6 +381,22 @@ typedef struct { + const XML_Char *value; + } DEFAULT_ATTRIBUTE; + ++// This structure allows mapping attribute names to instances of ++// `DEFAULT_ATTRIBUTE`. ++typedef struct { ++ // Member `name` goes first to make this structure compatible with structure ++ // `NAMED` (further up), which is needed to support use of structure ++ // `NAME_AND_DEFAULT_ATTRIBUTE` in a hash table as implemented by function ++ // `lookup` (further down). ++ const XML_Char *name; ++ // We would store a `DEFAULT_ATTRIBUTE *` here but the backing array ++ // can be reallocated which would invalidate the pointer. Using an index ++ // into the array instead, avoids that problem. ++ size_t attIndex; ++ // This is set to `false` by function `lookup`. ++ bool initialized; ++} NAME_AND_DEFAULT_ATTRIBUTE; ++ + typedef struct { + unsigned long version; + unsigned long hash; +@@ -3951,11 +3967,14 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr, + + /* figure out whether declared as other than CDATA */ + if (attId->maybeTokenized) { +- for (size_t j = 0; j < nDefaultAtts; j++) { +- if (attId == elementType->defaultAtts[j].id) { +- isCdata = elementType->defaultAtts[j].isCdata; +- break; +- } ++ NAME_AND_DEFAULT_ATTRIBUTE *const nameAndDefaultAttribute ++ = (NAME_AND_DEFAULT_ATTRIBUTE *)lookup( ++ parser, &(elementType->defaultAttForName), attId->name, 0); ++ if (nameAndDefaultAttribute != NULL) { ++ assert(nameAndDefaultAttribute->attIndex < elementType->nDefaultAtts); ++ const DEFAULT_ATTRIBUTE *const att ++ = elementType->defaultAtts + nameAndDefaultAttribute->attIndex; ++ isCdata = att->isCdata; + } + } + +@@ -7275,11 +7294,24 @@ defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, XML_Bool isCdata, + if (! isCdata) + attId->maybeTokenized = XML_TRUE; + +- NAMED *const nameAddedOrFound +- = lookup(parser, &(type->defaultAttForName), attId->name, sizeof(NAMED)); +- if (! nameAddedOrFound) ++ NAME_AND_DEFAULT_ATTRIBUTE *const nameAndDefaultAttribute ++ = (NAME_AND_DEFAULT_ATTRIBUTE *)lookup( ++ parser, &(type->defaultAttForName), attId->name, ++ sizeof(NAME_AND_DEFAULT_ATTRIBUTE)); ++ if (! nameAndDefaultAttribute) + return 0; + ++ assert(nameAndDefaultAttribute->name == attId->name); ++ ++ // NOTE: The XML 1.0r4 spec says: ++ // "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." ++ if (! nameAndDefaultAttribute->initialized) { ++ nameAndDefaultAttribute->attIndex = type->nDefaultAtts; ++ nameAndDefaultAttribute->initialized = true; ++ } ++ + type->nDefaultAtts += 1; + return 1; + } +-- +2.45.4 + diff --git a/SPECS/expat/expat.spec b/SPECS/expat/expat.spec index 52a3a8a7947..282e1e0687a 100644 --- a/SPECS/expat/expat.spec +++ b/SPECS/expat/expat.spec @@ -2,13 +2,14 @@ Summary: An XML parser library Name: expat Version: 2.8.3 -Release: 1%{?dist} +Release: 2%{?dist} License: MIT Vendor: Microsoft Corporation Distribution: Azure Linux Group: System Environment/GeneralLibraries URL: https://libexpat.github.io/ Source0: https://github.com/libexpat/libexpat/releases/download/R_%{underscore_version}/%{name}-%{version}.tar.bz2 +Patch0: CVE-2026-66046.patch Requires: %{name}-libs = %{version}-%{release} %description @@ -66,6 +67,9 @@ rm -rf %{buildroot}/%{_docdir}/%{name} %{_libdir}/libexpat.so.1* %changelog +* Wed Aug 19 2026 Azure Linux Security Servicing Account - 2.8.3-2 +- Patch for CVE-2026-66046 + * Tue Aug 11 2026 CBL-Mariner Servicing Account - 2.8.3-1 - Auto-upgrade to 2.8.3 - for CVE-2026-72522 diff --git a/toolkit/resources/manifests/package/pkggen_core_aarch64.txt b/toolkit/resources/manifests/package/pkggen_core_aarch64.txt index bfd77ffca3b..1349a92959d 100644 --- a/toolkit/resources/manifests/package/pkggen_core_aarch64.txt +++ b/toolkit/resources/manifests/package/pkggen_core_aarch64.txt @@ -99,9 +99,9 @@ elfutils-libelf-0.189-6.azl3.aarch64.rpm elfutils-libelf-devel-0.189-6.azl3.aarch64.rpm elfutils-libelf-devel-static-0.189-6.azl3.aarch64.rpm elfutils-libelf-lang-0.189-6.azl3.aarch64.rpm -expat-2.8.3-1.azl3.aarch64.rpm -expat-devel-2.8.3-1.azl3.aarch64.rpm -expat-libs-2.8.3-1.azl3.aarch64.rpm +expat-2.8.3-2.azl3.aarch64.rpm +expat-devel-2.8.3-2.azl3.aarch64.rpm +expat-libs-2.8.3-2.azl3.aarch64.rpm libpipeline-1.5.7-1.azl3.aarch64.rpm libpipeline-devel-1.5.7-1.azl3.aarch64.rpm gdbm-1.23-1.azl3.aarch64.rpm diff --git a/toolkit/resources/manifests/package/pkggen_core_x86_64.txt b/toolkit/resources/manifests/package/pkggen_core_x86_64.txt index e7a3d8ddb73..92a56a6eace 100644 --- a/toolkit/resources/manifests/package/pkggen_core_x86_64.txt +++ b/toolkit/resources/manifests/package/pkggen_core_x86_64.txt @@ -99,9 +99,9 @@ elfutils-libelf-0.189-6.azl3.x86_64.rpm elfutils-libelf-devel-0.189-6.azl3.x86_64.rpm elfutils-libelf-devel-static-0.189-6.azl3.x86_64.rpm elfutils-libelf-lang-0.189-6.azl3.x86_64.rpm -expat-2.8.3-1.azl3.x86_64.rpm -expat-devel-2.8.3-1.azl3.x86_64.rpm -expat-libs-2.8.3-1.azl3.x86_64.rpm +expat-2.8.3-2.azl3.x86_64.rpm +expat-devel-2.8.3-2.azl3.x86_64.rpm +expat-libs-2.8.3-2.azl3.x86_64.rpm libpipeline-1.5.7-1.azl3.x86_64.rpm libpipeline-devel-1.5.7-1.azl3.x86_64.rpm gdbm-1.23-1.azl3.x86_64.rpm diff --git a/toolkit/resources/manifests/package/toolchain_aarch64.txt b/toolkit/resources/manifests/package/toolchain_aarch64.txt index 916884293a6..383d57f1047 100644 --- a/toolkit/resources/manifests/package/toolchain_aarch64.txt +++ b/toolkit/resources/manifests/package/toolchain_aarch64.txt @@ -94,10 +94,10 @@ elfutils-libelf-0.189-6.azl3.aarch64.rpm elfutils-libelf-devel-0.189-6.azl3.aarch64.rpm elfutils-libelf-devel-static-0.189-6.azl3.aarch64.rpm elfutils-libelf-lang-0.189-6.azl3.aarch64.rpm -expat-2.8.3-1.azl3.aarch64.rpm -expat-debuginfo-2.8.3-1.azl3.aarch64.rpm -expat-devel-2.8.3-1.azl3.aarch64.rpm -expat-libs-2.8.3-1.azl3.aarch64.rpm +expat-2.8.3-2.azl3.aarch64.rpm +expat-debuginfo-2.8.3-2.azl3.aarch64.rpm +expat-devel-2.8.3-2.azl3.aarch64.rpm +expat-libs-2.8.3-2.azl3.aarch64.rpm file-5.45-1.azl3.aarch64.rpm file-debuginfo-5.45-1.azl3.aarch64.rpm file-devel-5.45-1.azl3.aarch64.rpm diff --git a/toolkit/resources/manifests/package/toolchain_x86_64.txt b/toolkit/resources/manifests/package/toolchain_x86_64.txt index eeb8e5570a7..dd5c9bc946b 100644 --- a/toolkit/resources/manifests/package/toolchain_x86_64.txt +++ b/toolkit/resources/manifests/package/toolchain_x86_64.txt @@ -99,10 +99,10 @@ elfutils-libelf-0.189-6.azl3.x86_64.rpm elfutils-libelf-devel-0.189-6.azl3.x86_64.rpm elfutils-libelf-devel-static-0.189-6.azl3.x86_64.rpm elfutils-libelf-lang-0.189-6.azl3.x86_64.rpm -expat-2.8.3-1.azl3.x86_64.rpm -expat-debuginfo-2.8.3-1.azl3.x86_64.rpm -expat-devel-2.8.3-1.azl3.x86_64.rpm -expat-libs-2.8.3-1.azl3.x86_64.rpm +expat-2.8.3-2.azl3.x86_64.rpm +expat-debuginfo-2.8.3-2.azl3.x86_64.rpm +expat-devel-2.8.3-2.azl3.x86_64.rpm +expat-libs-2.8.3-2.azl3.x86_64.rpm file-5.45-1.azl3.x86_64.rpm file-debuginfo-5.45-1.azl3.x86_64.rpm file-devel-5.45-1.azl3.x86_64.rpm