Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
199 changes: 199 additions & 0 deletions SPECS/expat/CVE-2026-66046.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
From 6541096ee48868ebbbf64b05260cbb3bd698a772 Mon Sep 17 00:00:00 2001
From: Sebastian Pipping <sebastian@pipping.org>
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 <sebastian@pipping.org>
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 <azurelinux-security@microsoft.com>
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

6 changes: 5 additions & 1 deletion SPECS/expat/expat.spec
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -66,6 +67,9 @@ rm -rf %{buildroot}/%{_docdir}/%{name}
%{_libdir}/libexpat.so.1*

%changelog
* Wed Aug 19 2026 Azure Linux Security Servicing Account <azurelinux-security@microsoft.com> - 2.8.3-2
- Patch for CVE-2026-66046

* Tue Aug 11 2026 CBL-Mariner Servicing Account <cblmargh@microsoft.com> - 2.8.3-1
- Auto-upgrade to 2.8.3 - for CVE-2026-72522

Expand Down
6 changes: 3 additions & 3 deletions toolkit/resources/manifests/package/pkggen_core_aarch64.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions toolkit/resources/manifests/package/pkggen_core_x86_64.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions toolkit/resources/manifests/package/toolchain_aarch64.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions toolkit/resources/manifests/package/toolchain_x86_64.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading