From patchwork Tue Oct 6 19:36:33 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Marko X-Patchwork-Id: 100074 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 723CFCA5FED for ; Tue, 6 Oct 2026 19:37:01 +0000 (UTC) Received: from mta-64-226.siemens.flowmailer.net (mta-64-226.siemens.flowmailer.net [185.136.64.226]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.3536.1791315413362235358 for ; Tue, 06 Oct 2026 12:36:54 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=peter.marko@siemens.com header.s=fm1 header.b=qQJ5fDLh; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.64.226, mailfrom: fm-256628-202610061936498d2393f07200020767-cdtmhg@rts-flowmailer.siemens.com) Received: by mta-64-226.siemens.flowmailer.net with ESMTPSA id 202610061936498d2393f07200020767 for ; Tue, 06 Oct 2026 21:36:50 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm1; d=siemens.com; i=peter.marko@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc; bh=p9AZ5kpKlTbyF6tGzBm3i3avreWSFe/kNIwYCjLpZ6s=; b=qQJ5fDLhWtwxA+O287wU+ah/FcRhaRUu+EaYJGC6PaSQyxA1CrEbMb9vr5LYzrfN30TLVT VEHj3BaGJTJcxJFSqksuMhwX9BrT3w+W2vo+4qvs8P/NomZQcDdLZNYDA3lU0nJPNAaU3syu u9pFtMN5YatMM0tQgSkb8nvlE8dcjbQEh6498XwrSwpuqmevx5SkW5+MqDRe7P0HvxNyJUYo zwda0f7qtRHtOxRzIz+00LyqFysGtFihLbLw/xrfgNnaqRfXD6cwpqpKOx3lYmloi6jkyhjr 0f5hAE+ILVnSTSSP6EpjlLYcfGLgzARFrwve7uaPeWE1AbYT1Vr5Or5A==; From: Peter Marko To: openembedded-core@lists.openembedded.org Cc: Peter Marko Subject: [wrynose][PATCH 1/6] expat: patch CVE-2026-66046 and CVE-2026-76641 Date: Tue, 6 Oct 2026 21:36:33 +0200 Message-ID: <20261006193638.2018393-1-peter.marko@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-256628:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 06 Oct 2026 19:37:01 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/247320 From: Peter Marko Pick patches per [1] and [2]. (fix for CVE-2026-66046 introduces CVE-2026-76641) [1] https://security-tracker.debian.org/tracker/CVE-2026-66046 [2] https://security-tracker.debian.org/tracker/CVE-2026-76641 Signed-off-by: Peter Marko --- .../expat/expat/CVE-2026-66046-01.patch | 107 ++++++++++++ .../expat/expat/CVE-2026-66046-02.patch | 90 ++++++++++ .../expat/expat/CVE-2026-76641.patch | 160 ++++++++++++++++++ meta/recipes-core/expat/expat_2.8.3.bb | 3 + 4 files changed, 360 insertions(+) create mode 100644 meta/recipes-core/expat/expat/CVE-2026-66046-01.patch create mode 100644 meta/recipes-core/expat/expat/CVE-2026-66046-02.patch create mode 100644 meta/recipes-core/expat/expat/CVE-2026-76641.patch diff --git a/meta/recipes-core/expat/expat/CVE-2026-66046-01.patch b/meta/recipes-core/expat/expat/CVE-2026-66046-01.patch new file mode 100644 index 0000000000..9bb0e2ee13 --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2026-66046-01.patch @@ -0,0 +1,107 @@ +From 98f5acc146af76859cd7c345c0906e9e9e8ea656 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping +Date: Thu, 13 Aug 2026 15:47:24 +0200 +Subject: [PATCH] lib: Rename hash table `defaultAttsNames` to + `defaultAttForName` + +It was previously used as a "set". This prepares for the upcoming +change to a true "dictionary". + +CVE: CVE-2026-76641 +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/98f5acc146af76859cd7c345c0906e9e9e8ea656] +Signed-off-by: Peter Marko +--- + lib/xmlparse.c | 24 ++++++++++++------------ + 1 file changed, 12 insertions(+), 12 deletions(-) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index e5242480..239dc6de 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 { diff --git a/meta/recipes-core/expat/expat/CVE-2026-66046-02.patch b/meta/recipes-core/expat/expat/CVE-2026-66046-02.patch new file mode 100644 index 0000000000..31e4cc2e7b --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2026-66046-02.patch @@ -0,0 +1,90 @@ +From f8f7c4ffd883e3c2c58f0ebb49416a6c1d248738 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping +Date: Thu, 13 Aug 2026 16:39:35 +0200 +Subject: [PATCH] lib: Migrate .isCdata lookup from a linear loop to a hash + table lookup + +.. to resolve quadratic runtime + +CVE: CVE-2026-76641 +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/f8f7c4ffd883e3c2c58f0ebb49416a6c1d248738] +Signed-off-by: Peter Marko +--- + lib/xmlparse.c | 48 ++++++++++++++++++++++++++++++++++++++++-------- + 1 file changed, 40 insertions(+), 8 deletions(-) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index 239dc6de..1cd20125 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; + } diff --git a/meta/recipes-core/expat/expat/CVE-2026-76641.patch b/meta/recipes-core/expat/expat/CVE-2026-76641.patch new file mode 100644 index 0000000000..26fa5704a6 --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2026-76641.patch @@ -0,0 +1,160 @@ +From 98599f6dcc2b460410881fe420f5f55d6bec63bf Mon Sep 17 00:00:00 2001 +From: Zeyou Liu +Date: Thu, 20 Aug 2026 20:29:56 +0800 +Subject: [PATCH] lib: Fix out-of-bounds read from hash table entries created + by dtdCopy + +Commit f8f7c4ff grew the entries of ELEMENT_TYPE member +.defaultAttForName from structure NAMED to the larger structure +NAME_AND_DEFAULT_ATTRIBUTE and adjusted function defineAttribute +accordingly, but function dtdCopy kept creating entries of size +sizeof(NAMED). Because function lookup allocates exactly createSize +bytes, function storeAtts reads member .attIndex past the end of those +entries whenever attributes are parsed by a parser that was created by +XML_ExternalEntityParserCreate. + +That out-of-bounds value is then used as an index into member +.defaultAtts, so the effects range from silently not normalizing +whitespace in attributes that are not of type CDATA, to dereferencing a +wild pointer: a release build of master segfaults in function storeAtts +on the document used by the new test. A zero-filled heap happens to +yield index 0, which is why the existing tests did not catch this. + +Member .attIndex is now stored the way function defineAttribute stores +it, i.e. keeping the index of the first declaration, so that a copied +DTD resolves attributes exactly like the DTD that it was copied from. + +This was found while backporting the fix for CVE-2026-66046 onto Expat +2.6.4 for the OpenCloudOS Stream distribution. Only master is affected, +no released version of Expat contains commit f8f7c4ff. + +CVE: CVE-2026-76641 +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/98599f6dcc2b460410881fe420f5f55d6bec63bf] +Signed-off-by: Peter Marko +--- + lib/xmlparse.c | 17 +++++++-- + tests/basic_tests.c | 74 +++++++++++++++++++++++++++++++++++++++ + 2 files changed, 88 insertions(+), 3 deletions(-) + +diff --git a/lib/xmlparse.c b/expat/lib/xmlparse.c +index 10592ac3..e72c4570 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -7800,11 +7800,22 @@ dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, + } else + newE->defaultAtts[i].value = NULL; + +- NAMED *const nameAddedOrFound = lookup(parser, &(newE->defaultAttForName), +- attributeName, sizeof(NAMED)); +- if (! nameAddedOrFound) { ++ NAME_AND_DEFAULT_ATTRIBUTE *const nameAndDefaultAttribute ++ = (NAME_AND_DEFAULT_ATTRIBUTE *)lookup( ++ parser, &(newE->defaultAttForName), attributeName, ++ sizeof(NAME_AND_DEFAULT_ATTRIBUTE)); ++ if (! nameAndDefaultAttribute) { + return 0; + } ++ ++ // 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 = i; ++ nameAndDefaultAttribute->initialized = true; ++ } + } + } + +diff --git a/tests/basic_tests.c b/expat/tests/basic_tests.c +index 308adf6c..6c2d3280 100644 +--- a/tests/basic_tests.c ++++ b/tests/basic_tests.c +@@ -2809,6 +2809,79 @@ START_TEST(test_duplicate_id_attribute_multiple_attlistdecl) { + } + END_TEST + ++static void XMLCALL ++check_second_attr_normalization(void *userData, const XML_Char *name, ++ const XML_Char **atts) { ++ int *const seen_second = userData; ++ UNUSED_P(name); ++ ++ for (size_t i = 0; atts[i] != NULL; i += 2) { ++ const XML_Char *const key = atts[i]; ++ const XML_Char *const value = atts[i + 1]; ++ if (xcstrcmp(key, XCS("second")) != 0) ++ continue; ++ *seen_second = 1; ++ /* Attribute "second" is not of type CDATA, so leading, trailing and ++ * repeated whitespace is to be normalized away. */ ++ if (xcstrcmp(value, XCS("a b")) != 0) ++ fail("Attribute of non-CDATA type was not whitespace-normalized"); ++ } ++} ++ ++static int XMLCALL ++external_entity_attr_checker(XML_Parser parser, const XML_Char *context, ++ const XML_Char *base, const XML_Char *systemId, ++ const XML_Char *publicId) { ++ const char *const text = ""; ++ UNUSED_P(base); ++ UNUSED_P(systemId); ++ UNUSED_P(publicId); ++ ++ XML_Parser ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL); ++ if (ext_parser == NULL) ++ fail("Could not create external entity parser"); ++ ++ if (_XML_Parse_SINGLE_BYTES(ext_parser, text, (int)strlen(text), XML_TRUE) ++ != XML_STATUS_OK) ++ xml_failure(ext_parser); ++ ++ XML_ParserFree(ext_parser); ++ return XML_STATUS_OK; ++} ++ ++START_TEST(test_default_attr_index_after_dtd_copy) { ++ /* Function storeAtts resolves member .attIndex of structure ++ * NAME_AND_DEFAULT_ATTRIBUTE to tell whether an attribute value needs ++ * whitespace normalization, so function dtdCopy needs to carry that index ++ * over to the copy. Attribute "first" is declared before attribute ++ * "second" so that a mixed-up index resolves to the wrong declaration. ++ */ ++ const char *text = "\n" ++ " \n" ++ " \n" ++ " \n" ++ " \n" ++ "]>\n" ++ "&e;\n"; ++ int seen_second = 0; ++ ++ XML_Parser parser = XML_ParserCreate(NULL); ++ assert_true(parser != NULL); ++ XML_SetUserData(parser, &seen_second); ++ XML_SetExternalEntityRefHandler(parser, external_entity_attr_checker); ++ XML_SetStartElementHandler(parser, check_second_attr_normalization); ++ ++ if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE) ++ != XML_STATUS_OK) ++ xml_failure(parser); ++ if (! seen_second) ++ fail("Attribute \"second\" has not been reported"); ++ ++ XML_ParserFree(parser); ++} ++END_TEST ++ + /* Test reset works correctly in the middle of processing an internal + * entity. Exercises some obscure code in XML_ParserReset(). + */ +@@ -6737,6 +6810,7 @@ make_basic_test_case(Suite *s) { + 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_default_attr_index_after_dtd_copy); + 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); diff --git a/meta/recipes-core/expat/expat_2.8.3.bb b/meta/recipes-core/expat/expat_2.8.3.bb index 79e8c15227..5d30a844fa 100644 --- a/meta/recipes-core/expat/expat_2.8.3.bb +++ b/meta/recipes-core/expat/expat_2.8.3.bb @@ -10,6 +10,9 @@ VERSION_TAG = "${@d.getVar('PV').replace('.', '_')}" SRC_URI = "${GITHUB_BASE_URI}/download/R_${VERSION_TAG}/expat-${PV}.tar.bz2 \ file://run-ptest \ + file://CVE-2026-66046-01.patch \ + file://CVE-2026-66046-02.patch \ + file://CVE-2026-76641.patch \ " GITHUB_BASE_URI = "https://github.com/libexpat/libexpat/releases/" From patchwork Tue Oct 6 19:36:34 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Marko X-Patchwork-Id: 100075 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 49D06CA5FED for ; Tue, 6 Oct 2026 19:37:21 +0000 (UTC) Received: from mta-64-225.siemens.flowmailer.net (mta-64-225.siemens.flowmailer.net [185.136.64.225]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.3554.1791315439233089299 for ; Tue, 06 Oct 2026 12:37:19 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=peter.marko@siemens.com header.s=fm1 header.b=lg8G987B; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.64.225, mailfrom: fm-256628-202610061937165739737cce00020778-aeu9cd@rts-flowmailer.siemens.com) Received: by mta-64-225.siemens.flowmailer.net with ESMTPSA id 202610061937165739737cce00020778 for ; Tue, 06 Oct 2026 21:37:16 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm1; d=siemens.com; i=peter.marko@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=kecg8rKM/mVNzYqH692WmOxnJoBSAct/XxNE9CQHdVQ=; b=lg8G987B25aaemMfdkbKCeiX9MHihu/2F7OaYrj5c40QeJ0kx6/pYA+tpe0syTCPBbDtcU +Ql4Uf2z9k5M8ZoT+DlWn5914Wjj9L3axWkn1AdjtmOLa5UMsMZUHEfvpk+cNYiuX+d97yi9 J0Mw7Bv0Lsen0asS8Xc4bxuVM3iyJxZaoTr4Qs6Qi5CCAZCehkp0V6PLoRaHmwTbb7uiGeIy 9xW+qXJOloP1U6zR6HwsmBCPjbJ4e5a4qIkmiY1cjI9zCjyl59tPMr4wMbrjydrwRaY64GD3 I1LzU2Q9bCNOi7Dh7W73RKmAJo7TUGUYfR1gaQpMJIQQnop37kxddlBA==; From: Peter Marko To: openembedded-core@lists.openembedded.org Cc: Peter Marko Subject: [wrynose][PATCH 2/6] expat: patch CVE-2026-76956 Date: Tue, 6 Oct 2026 21:36:34 +0200 Message-ID: <20261006193638.2018393-2-peter.marko@siemens.com> In-Reply-To: <20261006193638.2018393-1-peter.marko@siemens.com> References: <20261006193638.2018393-1-peter.marko@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-256628:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 06 Oct 2026 19:37:21 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/247321 From: Peter Marko Pick patch per [1]. [1] https://security-tracker.debian.org/tracker/CVE-2026-76956 Signed-off-by: Peter Marko --- .../expat/expat/CVE-2026-76956.patch | 26 +++++++++++++++++++ meta/recipes-core/expat/expat_2.8.3.bb | 1 + 2 files changed, 27 insertions(+) create mode 100644 meta/recipes-core/expat/expat/CVE-2026-76956.patch diff --git a/meta/recipes-core/expat/expat/CVE-2026-76956.patch b/meta/recipes-core/expat/expat/CVE-2026-76956.patch new file mode 100644 index 0000000000..96d9b68ee7 --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2026-76956.patch @@ -0,0 +1,26 @@ +From 40daa9996d616e66a75dea41ed2b18f2c3901b9f Mon Sep 17 00:00:00 2001 +From: Sorrachat <32319737+Sorrashut-K@users.noreply.github.com> +Date: Fri, 14 Aug 2026 17:29:50 -0400 +Subject: [PATCH] lib: Fix inverted getentropy() return in + writeRandomBytes_getentropy + +CVE: CVE-2026-76956 +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/40daa9996d616e66a75dea41ed2b18f2c3901b9f] +Signed-off-by: Peter Marko +--- + lib/random_getentropy.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/lib/random_getentropy.c b/expat/lib/random_getentropy.c +index d258df6a..ad8b1984 100644 +--- a/lib/random_getentropy.c ++++ b/lib/random_getentropy.c +@@ -54,7 +54,7 @@ + bool + writeRandomBytes_getentropy(void *target, size_t count) { + errno = 0; +- const bool success = getentropy(target, count); ++ const bool success = (getentropy(target, count) == 0); + // MSan does not understand `getentropy`, so explain its effects + if (success) + MSAN_UNPOISON(target, count); diff --git a/meta/recipes-core/expat/expat_2.8.3.bb b/meta/recipes-core/expat/expat_2.8.3.bb index 5d30a844fa..c3c9f73845 100644 --- a/meta/recipes-core/expat/expat_2.8.3.bb +++ b/meta/recipes-core/expat/expat_2.8.3.bb @@ -13,6 +13,7 @@ SRC_URI = "${GITHUB_BASE_URI}/download/R_${VERSION_TAG}/expat-${PV}.tar.bz2 \ file://CVE-2026-66046-01.patch \ file://CVE-2026-66046-02.patch \ file://CVE-2026-76641.patch \ + file://CVE-2026-76956.patch \ " GITHUB_BASE_URI = "https://github.com/libexpat/libexpat/releases/" From patchwork Tue Oct 6 19:36:35 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Marko X-Patchwork-Id: 100076 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4DB58CA5FFF for ; Tue, 6 Oct 2026 19:37:31 +0000 (UTC) Received: from mta-65-227.siemens.flowmailer.net (mta-65-227.siemens.flowmailer.net [185.136.65.227]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.3556.1791315445642124526 for ; Tue, 06 Oct 2026 12:37:25 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=peter.marko@siemens.com header.s=fm1 header.b=rvpG/u0E; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.65.227, mailfrom: fm-256628-20261006193723a7561524df00020747-k0uv8v@rts-flowmailer.siemens.com) Received: by mta-65-227.siemens.flowmailer.net with ESMTPSA id 20261006193723a7561524df00020747 for ; Tue, 06 Oct 2026 21:37:23 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm1; d=siemens.com; i=peter.marko@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=9+g8xDwnrkmG2k1ttevvuH1hAXG+dtjXQWXyofMy354=; b=rvpG/u0EDDFM+LEW4K2lTC1dySbs2euHfie7DVqYKT5vyJ+a1a0hOBCrOnShmjiFWqO6Vz 497/CTQsKMYTvFgzkQ8mfPn0nFm/eaqss7FJIOsMm/L6yZzXoSmOoY0FfycIAm6lh44JxfU+ tbOfnuhllC+/nAdJSIGmpn/eNoxMzTnI1WEoDw4GA0l1OH8fHQVB93Td5Jt59pLWiWUDVk0n WAzlJoF8XzmnBqeMbIJw2KmWo9dtCZAkvSmlwE9uyypDT9Ch3x6Wp164/0TPEtPrlLfw24sR jxkJUECSldF7haZBtNcnWJ9GNled9xSwQxWg/mG4lH0kucBnzt1GEbeQ==; From: Peter Marko To: openembedded-core@lists.openembedded.org Cc: Peter Marko Subject: [wrynose][PATCH 3/6] expat: patch CVE-2026-76957 Date: Tue, 6 Oct 2026 21:36:35 +0200 Message-ID: <20261006193638.2018393-3-peter.marko@siemens.com> In-Reply-To: <20261006193638.2018393-1-peter.marko@siemens.com> References: <20261006193638.2018393-1-peter.marko@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-256628:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 06 Oct 2026 19:37:31 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/247322 From: Peter Marko Pick patches per [1]. [1] https://security-tracker.debian.org/tracker/CVE-2026-76957 Signed-off-by: Peter Marko --- .../expat/expat/CVE-2026-76957-01.patch | 121 ++++++++++++++++++ .../expat/expat/CVE-2026-76957-02.patch | 85 ++++++++++++ meta/recipes-core/expat/expat_2.8.3.bb | 2 + 3 files changed, 208 insertions(+) create mode 100644 meta/recipes-core/expat/expat/CVE-2026-76957-01.patch create mode 100644 meta/recipes-core/expat/expat/CVE-2026-76957-02.patch diff --git a/meta/recipes-core/expat/expat/CVE-2026-76957-01.patch b/meta/recipes-core/expat/expat/CVE-2026-76957-01.patch new file mode 100644 index 0000000000..fb7a8e0b02 --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2026-76957-01.patch @@ -0,0 +1,121 @@ +From 127b7d4beb8fe7e5ce5cb021c2e56379c95863d0 Mon Sep 17 00:00:00 2001 +From: Darren Carreras +Date: Mon, 17 Aug 2026 21:16:00 -0400 +Subject: [PATCH] Protect custom encoding callbacks from parser reentry + +Co-authored-by: Sebastian Pipping + +CVE: CVE-2026-76957 +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/127b7d4beb8fe7e5ce5cb021c2e56379c95863d0] +Signed-off-by: Peter Marko +--- + lib/xmlparse.c | 49 ++++++++++++++++++++++++++++++++++++++++--------- + 1 file changed, 40 insertions(+), 9 deletions(-) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index e5242480..4f9dcbb6 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -771,6 +771,8 @@ struct XML_ParserStruct { + void *m_unknownEncodingMem; + void *m_unknownEncodingData; + void *m_unknownEncodingHandlerData; ++ // Application callback invoked by callUnknownEncodingConvert. ++ int(XMLCALL *m_unknownEncodingConvert)(void *, const char *); + void(XMLCALL *m_unknownEncodingRelease)(void *); + PROLOG_STATE m_prologState; + Processor *m_processor; +@@ -1193,6 +1195,25 @@ isCalledFromInsideHandler(XML_Parser parser) { + return parser->m_handlerCallDepth > 0; + } + ++static void ++callUnknownEncodingRelease(XML_Parser parser) { ++ beforeHandler(parser); ++ parser->m_unknownEncodingRelease(parser->m_unknownEncodingData); ++ afterHandler(parser); ++ parser->m_unknownEncodingRelease = NULL; ++ parser->m_unknownEncodingData = NULL; ++} ++ ++static int XMLCALL ++callUnknownEncodingConvert(void *data, const char *p) { ++ XML_Parser parser = data; ++ beforeHandler(parser); ++ const int result ++ = parser->m_unknownEncodingConvert(parser->m_unknownEncodingData, p); ++ afterHandler(parser); ++ return result; ++} ++ + static enum XML_Error + callProcessor(XML_Parser parser, const char *start, const char *end, + const char **endPtr) { +@@ -1540,6 +1561,7 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) { + parser->m_inheritedBindings = NULL; + parser->m_nSpecifiedAtts = 0; + parser->m_unknownEncodingMem = NULL; ++ parser->m_unknownEncodingConvert = NULL; + parser->m_unknownEncodingRelease = NULL; + parser->m_unknownEncodingData = NULL; + parser->m_parsingStatus.parsing = XML_INITIALIZED; +@@ -1620,7 +1642,7 @@ XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) { + moveToFreeBindingList(parser, parser->m_inheritedBindings); + FREE(parser, parser->m_unknownEncodingMem); + if (parser->m_unknownEncodingRelease) +- parser->m_unknownEncodingRelease(parser->m_unknownEncodingData); ++ callUnknownEncodingRelease(parser); + poolClear(&parser->m_tempPool); + poolClear(&parser->m_temp2Pool); + FREE(parser, (void *)parser->m_protocolEncodingName); +@@ -1931,7 +1953,7 @@ XML_ParserFree(XML_Parser parser) { + FREE(parser, parser->m_nsAtts); + FREE(parser, parser->m_unknownEncodingMem); + if (parser->m_unknownEncodingRelease) +- parser->m_unknownEncodingRelease(parser->m_unknownEncodingData); ++ callUnknownEncodingRelease(parser); + FREE(parser, parser); + } + +@@ -4965,25 +4987,34 @@ handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName) { + const int status = parser->m_unknownEncodingHandler( + parser->m_unknownEncodingHandlerData, encodingName, &info); + afterHandler(parser); ++ ++ parser->m_unknownEncodingRelease = info.release; ++ parser->m_unknownEncodingData = info.data; ++ + if (status) { + ENCODING *enc; + parser->m_unknownEncodingMem = MALLOC(parser, XmlSizeOfUnknownEncoding()); + if (! parser->m_unknownEncodingMem) { +- if (info.release) +- info.release(info.data); ++ if (parser->m_unknownEncodingRelease) ++ callUnknownEncodingRelease(parser); ++ else ++ parser->m_unknownEncodingData = NULL; + return XML_ERROR_NO_MEMORY; + } ++ parser->m_unknownEncodingConvert = info.convert; + enc = (parser->m_ns ? XmlInitUnknownEncodingNS : XmlInitUnknownEncoding)( +- parser->m_unknownEncodingMem, info.map, info.convert, info.data); ++ parser->m_unknownEncodingMem, info.map, ++ info.convert ? callUnknownEncodingConvert : NULL, parser); + if (enc) { +- parser->m_unknownEncodingData = info.data; +- parser->m_unknownEncodingRelease = info.release; + parser->m_encoding = enc; + return XML_ERROR_NONE; + } ++ parser->m_unknownEncodingConvert = NULL; + } +- if (info.release != NULL) +- info.release(info.data); ++ if (parser->m_unknownEncodingRelease != NULL) ++ callUnknownEncodingRelease(parser); ++ else ++ parser->m_unknownEncodingData = NULL; + } + return XML_ERROR_UNKNOWN_ENCODING; + } diff --git a/meta/recipes-core/expat/expat/CVE-2026-76957-02.patch b/meta/recipes-core/expat/expat/CVE-2026-76957-02.patch new file mode 100644 index 0000000000..827499958a --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2026-76957-02.patch @@ -0,0 +1,85 @@ +From acbd2e1179c04fe9a8c3f3837701904d05de71fc Mon Sep 17 00:00:00 2001 +From: Darren Carreras +Date: Mon, 17 Aug 2026 21:19:12 -0400 +Subject: [PATCH] Test custom encoding callback reentry protection + +CVE: CVE-2026-76957 +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/acbd2e1179c04fe9a8c3f3837701904d05de71fc] +Signed-off-by: Peter Marko +--- + tests/misc_tests.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 55 insertions(+) + +diff --git a/tests/misc_tests.c b/tests/misc_tests.c +index 82b4b54b..04538c25 100644 +--- a/tests/misc_tests.c ++++ b/tests/misc_tests.c +@@ -839,6 +839,60 @@ START_TEST(test_misc_resume_parser_forbidden_from_handler) { + } + END_TEST + ++typedef struct { ++ XML_Parser parser; ++ int converterCallCount; ++ int releaseCallCount; ++} EncodingCallbackData; ++ ++static int XMLCALL ++reentrant_encoding_converter(void *userData, const char *s) { ++ EncodingCallbackData *const data = userData; ++ UNUSED_P(s); ++ data->converterCallCount++; ++ forbidden_calls_character_handler(data->parser, NULL, 0); ++ return 'A'; ++} ++ ++static void XMLCALL ++reentrant_encoding_release(void *userData) { ++ EncodingCallbackData *const data = userData; ++ data->releaseCallCount++; ++ forbidden_calls_character_handler(data->parser, NULL, 0); ++} ++ ++static int XMLCALL ++reentrant_encoding_handler(void *userData, const XML_Char *name, ++ XML_Encoding *info) { ++ EncodingCallbackData *const data = userData; ++ UNUSED_P(name); ++ ++ for (int i = 0; i < 256; i++) ++ info->map[i] = i; ++ info->map[0x80] = -2; // Route byte 0x80 through the custom converter. ++ info->data = data; ++ info->convert = reentrant_encoding_converter; ++ info->release = reentrant_encoding_release; ++ return XML_STATUS_OK; ++} ++ ++START_TEST(test_misc_unknown_encoding_callbacks_protected) { ++ const char *const doc ++ = "\x80\x80"; ++ XML_Parser parser = XML_ParserCreate(NULL); ++ EncodingCallbackData data = {parser, 0, 0}; ++ XML_SetUnknownEncodingHandler(parser, reentrant_encoding_handler, &data); ++ ++ assert_true(XML_Parse(parser, doc, (int)strlen(doc), /*isFinal=*/XML_TRUE) ++ == XML_STATUS_OK); ++ assert_true(data.converterCallCount > 0); ++ assert_true(data.releaseCallCount == 0); // Released by XML_ParserFree below. ++ ++ XML_ParserFree(parser); ++ assert_true(data.releaseCallCount == 1); ++} ++END_TEST ++ + // General attack payload idea by Jason Kratzer of Mozilla + START_TEST(test_misc_low_surrogate_mozilla_bug_2053153) { + const char doc_before[] = "<\0!\0D\0O\0C\0T\0Y\0P\0E\0 \0d\0 \0[\0\n\0" +@@ -936,6 +990,7 @@ make_miscellaneous_test_case(Suite *s) { + tcase_add_test(tc_misc, test_misc_no_infinite_loop_issue_1161); + tcase_add_test(tc_misc, test_misc_calls_forbidden_from_handlers); + tcase_add_test(tc_misc, test_misc_resume_parser_forbidden_from_handler); ++ tcase_add_test(tc_misc, test_misc_unknown_encoding_callbacks_protected); + tcase_add_test(tc_misc, test_misc_input_2gb); + tcase_add_test(tc_misc, test_misc_low_surrogate_mozilla_bug_2053153); + } diff --git a/meta/recipes-core/expat/expat_2.8.3.bb b/meta/recipes-core/expat/expat_2.8.3.bb index c3c9f73845..c9a1c9b18e 100644 --- a/meta/recipes-core/expat/expat_2.8.3.bb +++ b/meta/recipes-core/expat/expat_2.8.3.bb @@ -14,6 +14,8 @@ SRC_URI = "${GITHUB_BASE_URI}/download/R_${VERSION_TAG}/expat-${PV}.tar.bz2 \ file://CVE-2026-66046-02.patch \ file://CVE-2026-76641.patch \ file://CVE-2026-76956.patch \ + file://CVE-2026-76957-01.patch \ + file://CVE-2026-76957-02.patch \ " GITHUB_BASE_URI = "https://github.com/libexpat/libexpat/releases/" From patchwork Tue Oct 6 19:36:36 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Marko X-Patchwork-Id: 100077 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 63CEBCA6004 for ; Tue, 6 Oct 2026 19:37:41 +0000 (UTC) Received: from mta-65-227.siemens.flowmailer.net (mta-65-227.siemens.flowmailer.net [185.136.65.227]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.3586.1791315453231380694 for ; Tue, 06 Oct 2026 12:37:34 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=peter.marko@siemens.com header.s=fm1 header.b=ZXHFjLIN; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.65.227, mailfrom: fm-256628-20261006193731eb12a3e1260002079d-_e6frz@rts-flowmailer.siemens.com) Received: by mta-65-227.siemens.flowmailer.net with ESMTPSA id 20261006193731eb12a3e1260002079d for ; Tue, 06 Oct 2026 21:37:31 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm1; d=siemens.com; i=peter.marko@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=gTe9DEarG7Jf9Z0mvS/nxbAv8ylL/fkGVN51CGTBZ+4=; b=ZXHFjLINzKlfCunDJViw2O7ld/DplKWWpvnO6OIA1XmeiOFx/nwU5tFYIZkyLM3VG82UA8 E9kM34bxCG7SJRJqGPlfSOZ+nMiXXnGE5cvS7zdXpGRjh/OenrQ0SRBf/CqnEYyl0CHqkZPs +54I61CGpIhzIGcJ1LGQ+lqrE0Ngn+UxdHtwzSN3ZOU81QD6Io3Jjga+pv4wdH5yiEWa6CFV dRrRoA7cmPMUguRCD2H8/mDuKpjAWjnn8JMtKpgd0VQK4cvZlVjLrI3SVs5S5CUJHjvfh8c6 k+dVsjgBP7QmrPUVciXvfU6jf/DcoriSlZsqNsdU61Syrr66rJaX4SvQ==; From: Peter Marko To: openembedded-core@lists.openembedded.org Cc: Peter Marko Subject: [wrynose][PATCH 4/6] expat: patch CVE-2026-93990 Date: Tue, 6 Oct 2026 21:36:36 +0200 Message-ID: <20261006193638.2018393-4-peter.marko@siemens.com> In-Reply-To: <20261006193638.2018393-1-peter.marko@siemens.com> References: <20261006193638.2018393-1-peter.marko@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-256628:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 06 Oct 2026 19:37:41 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/247323 From: Peter Marko Pick patches per [1]. [1] https://security-tracker.debian.org/tracker/CVE-2026-93990 Signed-off-by: Peter Marko --- .../expat/expat/CVE-2026-93990-01.patch | 191 ++++++++++ .../expat/expat/CVE-2026-93990-02.patch | 328 ++++++++++++++++++ meta/recipes-core/expat/expat_2.8.3.bb | 2 + 3 files changed, 521 insertions(+) create mode 100644 meta/recipes-core/expat/expat/CVE-2026-93990-01.patch create mode 100644 meta/recipes-core/expat/expat/CVE-2026-93990-02.patch diff --git a/meta/recipes-core/expat/expat/CVE-2026-93990-01.patch b/meta/recipes-core/expat/expat/CVE-2026-93990-01.patch new file mode 100644 index 0000000000..fde01b916a --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2026-93990-01.patch @@ -0,0 +1,191 @@ +From 0cfd15bdf4b2c22d6b0df73610709dfb60921091 Mon Sep 17 00:00:00 2001 +From: Kartik Kenchi +Date: Tue, 23 Jun 2026 15:51:06 +0530 +Subject: [PATCH] lib: reject UTF-16 high surrogate not followed by a low + surrogate + +CVE: CVE-2026-93990 +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/0cfd15bdf4b2c22d6b0df73610709dfb60921091] +Signed-off-by: Peter Marko +--- + lib/xmltok.c | 53 ++++++++++++++++++++++++++++++++++++++++------- + lib/xmltok_impl.c | 4 ---- + 2 files changed, 45 insertions(+), 12 deletions(-) + +diff --git a/lib/xmltok.c b/lib/xmltok.c +index 8abb145e..5f4e78ec 100644 +--- a/lib/xmltok.c ++++ b/lib/xmltok.c +@@ -232,6 +232,19 @@ struct normal_encoding { + /* isNmstrt2 */ NULL, /* isNmstrt3 */ NULL, /* isNmstrt4 */ NULL, \ + /* isInvalid2 */ NULL, /* isInvalid3 */ NULL, /* isInvalid4 */ NULL + ++/* Like NULL_VTABLE but with a real isInvalid4 so the UTF-16 encodings reject a ++ high surrogate that is not followed by a low surrogate. Only needed for the ++ XML_MIN_SIZE build, where the shared tokenizer dispatches through the vtable; ++ the regular build inlines the same check via IS_INVALID_CHAR. */ ++#ifdef XML_MIN_SIZE ++# define UTF16_NULL_VTABLE(E) \ ++ /* isName2 */ NULL, /* isName3 */ NULL, /* isName4 */ NULL, \ ++ /* isNmstrt2 */ NULL, /* isNmstrt3 */ NULL, /* isNmstrt4 */ NULL, \ ++ /* isInvalid2 */ NULL, /* isInvalid3 */ NULL, E##isInvalid4 ++#else ++# define UTF16_NULL_VTABLE(E) NULL_VTABLE ++#endif ++ + static int FASTCALL checkCharRefNumber(int result); + + #include "xmltok_impl.h" +@@ -749,6 +762,11 @@ DEFINE_UTF16_TO_UTF16(big2_) + UCS2_GET_NAMING(namePages, (unsigned char)p[1], (unsigned char)p[0]) + #define LITTLE2_IS_NMSTRT_CHAR_MINBPC(p) \ + UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[1], (unsigned char)p[0]) ++/* A 4-byte UTF-16 character is a surrogate pair; byteType only reports BT_LEAD4 ++ for a high surrogate, so the pair is invalid unless the second unit is a low ++ surrogate (U+DC00..U+DFFF, i.e. high byte 0xDC..0xDF). */ ++#define LITTLE2_IS_INVALID_CHAR(p, n) \ ++ ((n) == 4 && ((unsigned char)(p)[3] & 0xFC) != 0xDC) + + #ifdef XML_MIN_SIZE + +@@ -781,6 +799,12 @@ little2_isNmstrtMin(const ENCODING *enc, const char *p) { + return LITTLE2_IS_NMSTRT_CHAR_MINBPC(p); + } + ++static int ++little2_isInvalid4(const ENCODING *enc, const char *p) { ++ UNUSED_P(enc); ++ return LITTLE2_IS_INVALID_CHAR(p, 4); ++} ++ + # undef VTABLE + # define VTABLE VTABLE1, little2_toUtf8, little2_toUtf16 + +@@ -797,6 +821,7 @@ little2_isNmstrtMin(const ENCODING *enc, const char *p) { + # define IS_NAME_CHAR_MINBPC(enc, p) LITTLE2_IS_NAME_CHAR_MINBPC(p) + # define IS_NMSTRT_CHAR(enc, p, n) (0) + # define IS_NMSTRT_CHAR_MINBPC(enc, p) LITTLE2_IS_NMSTRT_CHAR_MINBPC(p) ++# define IS_INVALID_CHAR(enc, p, n) LITTLE2_IS_INVALID_CHAR(p, n) + + # define XML_TOK_IMPL_C + # include "xmltok_impl.c" +@@ -828,7 +853,7 @@ static const struct normal_encoding little2_encoding_ns + # include "asciitab.h" + # include "latin1tab.h" + }, +- STANDARD_VTABLE(little2_) NULL_VTABLE}; ++ STANDARD_VTABLE(little2_) UTF16_NULL_VTABLE(little2_)}; + + #endif + +@@ -846,7 +871,7 @@ static const struct normal_encoding little2_encoding + #undef BT_COLON + #include "latin1tab.h" + }, +- STANDARD_VTABLE(little2_) NULL_VTABLE}; ++ STANDARD_VTABLE(little2_) UTF16_NULL_VTABLE(little2_)}; + + #if BYTEORDER != 4321 + +@@ -858,7 +883,7 @@ static const struct normal_encoding internal_little2_encoding_ns + # include "iasciitab.h" + # include "latin1tab.h" + }, +- STANDARD_VTABLE(little2_) NULL_VTABLE}; ++ STANDARD_VTABLE(little2_) UTF16_NULL_VTABLE(little2_)}; + + # endif + +@@ -870,7 +895,7 @@ static const struct normal_encoding internal_little2_encoding + # undef BT_COLON + # include "latin1tab.h" + }, +- STANDARD_VTABLE(little2_) NULL_VTABLE}; ++ STANDARD_VTABLE(little2_) UTF16_NULL_VTABLE(little2_)}; + + #endif + +@@ -882,6 +907,11 @@ static const struct normal_encoding internal_little2_encoding + UCS2_GET_NAMING(namePages, (unsigned char)p[0], (unsigned char)p[1]) + #define BIG2_IS_NMSTRT_CHAR_MINBPC(p) \ + UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[0], (unsigned char)p[1]) ++/* A 4-byte UTF-16 character is a surrogate pair; byteType only reports BT_LEAD4 ++ for a high surrogate, so the pair is invalid unless the second unit is a low ++ surrogate (U+DC00..U+DFFF, i.e. high byte 0xDC..0xDF). */ ++#define BIG2_IS_INVALID_CHAR(p, n) \ ++ ((n) == 4 && ((unsigned char)(p)[2] & 0xFC) != 0xDC) + + #ifdef XML_MIN_SIZE + +@@ -914,6 +944,12 @@ big2_isNmstrtMin(const ENCODING *enc, const char *p) { + return BIG2_IS_NMSTRT_CHAR_MINBPC(p); + } + ++static int ++big2_isInvalid4(const ENCODING *enc, const char *p) { ++ UNUSED_P(enc); ++ return BIG2_IS_INVALID_CHAR(p, 4); ++} ++ + # undef VTABLE + # define VTABLE VTABLE1, big2_toUtf8, big2_toUtf16 + +@@ -930,6 +966,7 @@ big2_isNmstrtMin(const ENCODING *enc, const char *p) { + # define IS_NAME_CHAR_MINBPC(enc, p) BIG2_IS_NAME_CHAR_MINBPC(p) + # define IS_NMSTRT_CHAR(enc, p, n) (0) + # define IS_NMSTRT_CHAR_MINBPC(enc, p) BIG2_IS_NMSTRT_CHAR_MINBPC(p) ++# define IS_INVALID_CHAR(enc, p, n) BIG2_IS_INVALID_CHAR(p, n) + + # define XML_TOK_IMPL_C + # include "xmltok_impl.c" +@@ -961,7 +998,7 @@ static const struct normal_encoding big2_encoding_ns + # include "asciitab.h" + # include "latin1tab.h" + }, +- STANDARD_VTABLE(big2_) NULL_VTABLE}; ++ STANDARD_VTABLE(big2_) UTF16_NULL_VTABLE(big2_)}; + + #endif + +@@ -979,7 +1016,7 @@ static const struct normal_encoding big2_encoding + #undef BT_COLON + #include "latin1tab.h" + }, +- STANDARD_VTABLE(big2_) NULL_VTABLE}; ++ STANDARD_VTABLE(big2_) UTF16_NULL_VTABLE(big2_)}; + + #if BYTEORDER != 1234 + +@@ -991,7 +1028,7 @@ static const struct normal_encoding internal_big2_encoding_ns + # include "iasciitab.h" + # include "latin1tab.h" + }, +- STANDARD_VTABLE(big2_) NULL_VTABLE}; ++ STANDARD_VTABLE(big2_) UTF16_NULL_VTABLE(big2_)}; + + # endif + +@@ -1003,7 +1040,7 @@ static const struct normal_encoding internal_big2_encoding + # undef BT_COLON + # include "latin1tab.h" + }, +- STANDARD_VTABLE(big2_) NULL_VTABLE}; ++ STANDARD_VTABLE(big2_) UTF16_NULL_VTABLE(big2_)}; + + #endif + +diff --git a/lib/xmltok_impl.c b/lib/xmltok_impl.c +index b7a9b5eb..3cc9e5ab 100644 +--- a/lib/xmltok_impl.c ++++ b/lib/xmltok_impl.c +@@ -44,10 +44,6 @@ + + #ifdef XML_TOK_IMPL_C + +-# ifndef IS_INVALID_CHAR // i.e. for UTF-16 and XML_MIN_SIZE not defined +-# define IS_INVALID_CHAR(enc, ptr, n) (0) +-# endif +- + # define INVALID_LEAD_CASE(n, ptr, nextTokPtr) \ + case BT_LEAD##n: \ + if (end - ptr < n) \ diff --git a/meta/recipes-core/expat/expat/CVE-2026-93990-02.patch b/meta/recipes-core/expat/expat/CVE-2026-93990-02.patch new file mode 100644 index 0000000000..25e38e3956 --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2026-93990-02.patch @@ -0,0 +1,328 @@ +From 28fcfba540f6933aa8904a1514c4811713d2ab72 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping +Date: Thu, 17 Sep 2026 15:12:43 +0200 +Subject: [PATCH] tests: Cover UTF-16 decoding of surrogates + +Co-authored-by: Kartik Kenchi + +CVE: CVE-2026-93990 +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/28fcfba540f6933aa8904a1514c4811713d2ab72] +Signed-off-by: Peter Marko +--- + tests/basic_tests.c | 296 ++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 296 insertions(+) + +diff --git a/tests/basic_tests.c b/tests/basic_tests.c +index dd0494ce..92172a27 100644 +--- a/tests/basic_tests.c ++++ b/tests/basic_tests.c +@@ -1845,6 +1845,301 @@ START_TEST(test_utf16_bad_surrogate_pair) { + } + END_TEST + ++// Helper that creates a UTF-16LE copy of UTF-16BE literal input and vice versa ++static char * ++utf16_dup_flipped(const char *text, size_t lenBytes) { ++ assert_true(lenBytes < SIZE_MAX); ++ assert_true(lenBytes % 2 == 0); ++ char *const buffer = malloc(lenBytes + 1); ++ assert_true(buffer != NULL); ++ ++ for (size_t i = 0; i < lenBytes; i++) { ++ // This maps 0 -> 1, 1 -> 0, 2 -> 3, 3 -> 2, 4 -> 5, .. ++ size_t j = i + ((i % 2 == 0) ? +1 : -1); ++ assert_true(j < lenBytes); ++ buffer[j] = text[i]; ++ } ++ ++ buffer[lenBytes] = '\0'; ++ ++ return buffer; ++} ++ ++/* Tests that invalid combinations of surrogates are detected when decoding ++ UTF-16, both little-endian and big-endian. ++ Previously, a high surrogate not followed by a low surrogate slipped ++ through. Without validation the high would consume the next ++ code unit as a fake low, hiding e.g. a following '<' from the ++ tokenizer. */ ++START_TEST(test_utf16_surrogate_pairs) { ++ struct TestCase { ++ const char *idea; ++ const char *content; ++ bool expectedSuccess; ++ }; ++ ++ struct TestCase testCases[] = { ++ // Group {smallest high - 1}{*} ++ {"{smallest high - 1}{smallest high - 1}", ++ "\0<\0a\0>" ++ "\xD7\xFF" ++ "\xD7\xFF" ++ "\0<\0/\0a\0>", ++ true}, ++ {"{smallest high - 1}{smallest high}", ++ "\0<\0a\0>" ++ "\xD7\xFF" ++ "\xD8\x00" ++ "\0<\0/\0a\0>", ++ false}, ++ {"{smallest high - 1}{largest high}", ++ "\0<\0a\0>" ++ "\xD7\xFF" ++ "\xDB\xFF" ++ "\0<\0/\0a\0>", ++ false}, ++ {"{smallest high - 1}{smallest low}", ++ "\0<\0a\0>" ++ "\xD7\xFF" ++ "\xDC\x00" ++ "\0<\0/\0a\0>", ++ false}, ++ {"{smallest high - 1}{largest low}", ++ "\0<\0a\0>" ++ "\xD7\xFF" ++ "\xDF\xFF" ++ "\0<\0/\0a\0>", ++ false}, ++ {"{smallest high - 1}{largest low + 1}", ++ "\0<\0a\0>" ++ "\xD7\xFF" ++ "\xE0\x00" ++ "\0<\0/\0a\0>", ++ true}, ++ // Group {smallest high}{*} ++ {"{smallest high}{smallest high - 1}", ++ "\0<\0a\0>" ++ "\xD8\x00" ++ "\xD7\xFF" ++ "\0<\0/\0a\0>", ++ false}, ++ {"{smallest high}{smallest high}", ++ "\0<\0a\0>" ++ "\xD8\x00" ++ "\xD8\x00" ++ "\0<\0/\0a\0>", ++ false}, ++ {"{smallest high}{largest high}", ++ "\0<\0a\0>" ++ "\xD8\x00" ++ "\xDB\xFF" ++ "\0<\0/\0a\0>", ++ false}, ++ {"{smallest high}{smallest low}", ++ "\0<\0a\0>" ++ "\xD8\x00" ++ "\xDC\x00" ++ "\0<\0/\0a\0>", ++ true}, ++ {"{smallest high}{largest low}", ++ "\0<\0a\0>" ++ "\xD8\x00" ++ "\xDF\xFF" ++ "\0<\0/\0a\0>", ++ true}, ++ {"{smallest high}{largest low + 1}", ++ "\0<\0a\0>" ++ "\xD8\x00" ++ "\xE0\x00" ++ "\0<\0/\0a\0>", ++ false}, ++ // Group {largest high}{*} ++ {"{largest high}{smallest high - 1}", ++ "\0<\0a\0>" ++ "\xDB\xFF" ++ "\xD7\xFF" ++ "\0<\0/\0a\0>", ++ false}, ++ {"{largest high}{smallest high}", ++ "\0<\0a\0>" ++ "\xDB\xFF" ++ "\xD8\x00" ++ "\0<\0/\0a\0>", ++ false}, ++ {"{largest high}{largest high}", ++ "\0<\0a\0>" ++ "\xDB\xFF" ++ "\xDB\xFF" ++ "\0<\0/\0a\0>", ++ false}, ++ {"{largest high}{smallest low}", ++ "\0<\0a\0>" ++ "\xDB\xFF" ++ "\xDC\x00" ++ "\0<\0/\0a\0>", ++ true}, ++ {"{largest high}{largest low}", ++ "\0<\0a\0>" ++ "\xDB\xFF" ++ "\xDF\xFF" ++ "\0<\0/\0a\0>", ++ true}, ++ {"{largest high}{largest low + 1}", ++ "\0<\0a\0>" ++ "\xDB\xFF" ++ "\xE0\x00" ++ "\0<\0/\0a\0>", ++ false}, ++ // Group {smallest low}{*} ++ {"{smallest low}{smallest high - 1}", ++ "\0<\0a\0>" ++ "\xDC\x00" ++ "\xD7\xFF" ++ "\0<\0/\0a\0>", ++ false}, ++ {"{smallest low}{smallest high}", ++ "\0<\0a\0>" ++ "\xDC\x00" ++ "\xD8\x00" ++ "\0<\0/\0a\0>", ++ false}, ++ {"{smallest low}{largest high}", ++ "\0<\0a\0>" ++ "\xDC\x00" ++ "\xDB\xFF" ++ "\0<\0/\0a\0>", ++ false}, ++ {"{smallest low}{smallest low}", ++ "\0<\0a\0>" ++ "\xDC\x00" ++ "\xDC\x00" ++ "\0<\0/\0a\0>", ++ false}, ++ {"{smallest low}{largest low}", ++ "\0<\0a\0>" ++ "\xDC\x00" ++ "\xDF\xFF" ++ "\0<\0/\0a\0>", ++ false}, ++ {"{smallest low}{largest low + 1}", ++ "\0<\0a\0>" ++ "\xDC\x00" ++ "\xE0\x00" ++ "\0<\0/\0a\0>", ++ false}, ++ // Group {largest low}{*} ++ {"{largest low}{smallest high - 1}", ++ "\0<\0a\0>" ++ "\xDF\xFF" ++ "\xD7\xFF" ++ "\0<\0/\0a\0>", ++ false}, ++ {"{largest low}{smallest high}", ++ "\0<\0a\0>" ++ "\xDF\xFF" ++ "\xD8\x00" ++ "\0<\0/\0a\0>", ++ false}, ++ {"{largest low}{largest high}", ++ "\0<\0a\0>" ++ "\xDF\xFF" ++ "\xDB\xFF" ++ "\0<\0/\0a\0>", ++ false}, ++ {"{largest low}{smallest low}", ++ "\0<\0a\0>" ++ "\xDF\xFF" ++ "\xDC\x00" ++ "\0<\0/\0a\0>", ++ false}, ++ {"{largest low}{largest low}", ++ "\0<\0a\0>" ++ "\xDF\xFF" ++ "\xDF\xFF" ++ "\0<\0/\0a\0>", ++ false}, ++ {"{largest low}{largest low + 1}", ++ "\0<\0a\0>" ++ "\xDF\xFF" ++ "\xE0\x00" ++ "\0<\0/\0a\0>", ++ false}, ++ // Group {largest low + 1}{*} ++ {"{largest low + 1}{smallest high - 1}", ++ "\0<\0a\0>" ++ "\xE0\x00" ++ "\xD7\xFF" ++ "\0<\0/\0a\0>", ++ true}, ++ {"{largest low + 1}{smallest high}", ++ "\0<\0a\0>" ++ "\xE0\x00" ++ "\xD8\x00" ++ "\0<\0/\0a\0>", ++ false}, ++ {"{largest low + 1}{largest high}", ++ "\0<\0a\0>" ++ "\xE0\x00" ++ "\xDB\xFF" ++ "\0<\0/\0a\0>", ++ false}, ++ {"{largest low + 1}{smallest low}", ++ "\0<\0a\0>" ++ "\xE0\x00" ++ "\xDC\x00" ++ "\0<\0/\0a\0>", ++ false}, ++ {"{largest low + 1}{largest low}", ++ "\0<\0a\0>" ++ "\xE0\x00" ++ "\xDF\xFF" ++ "\0<\0/\0a\0>", ++ false}, ++ {"{largest low + 1}{largest low + 1}", ++ "\0<\0a\0>" ++ "\xE0\x00" ++ "\xE0\x00" ++ "\0<\0/\0a\0>", ++ true}, ++ }; ++ ++ for (size_t i = 0; i < sizeof(testCases) / sizeof(testCases[0]); i++) { ++ set_subtest("%s", testCases[i].idea); ++ ++ const int lenBytes = /**/ 6 + /*first*/ 2 + /*second*/ 2 + /**/ 8; ++ const bool expectedSuccess = testCases[i].expectedSuccess; ++ const enum XML_Status expectedStatus ++ = (expectedSuccess ? XML_STATUS_OK : XML_STATUS_ERROR); ++ ++ const char *const bigEndian = testCases[i].content; ++ char *const littleEndian = utf16_dup_flipped(bigEndian, lenBytes); ++ assert_true(littleEndian != NULL); ++ const char *endianCases[] = {bigEndian, littleEndian}; ++ ++ for (size_t j = 0; j < sizeof(endianCases) / sizeof(endianCases[0]); j++) { ++ const char *text = endianCases[j]; ++ ++ assert_true(text[lenBytes] == '\0'); // self-test ++ assert_true((text[0] == '\0') ++ != (text[lenBytes - 1] == '\0')); // self-test ++ ++ XML_Parser parser = XML_ParserCreate(NULL); ++ assert_true(parser != NULL); ++ ++ assert_true(_XML_Parse_SINGLE_BYTES(parser, text, lenBytes, XML_TRUE) ++ == expectedStatus); ++ if (! expectedSuccess) { ++ assert_true(XML_GetErrorCode(parser) == XML_ERROR_INVALID_TOKEN); ++ } ++ ++ XML_ParserFree(parser); ++ } ++ ++ free(littleEndian); ++ } ++} ++END_TEST ++ + START_TEST(test_bad_cdata) { + struct CaseData { + const char *text; +@@ -6711,6 +7006,7 @@ make_basic_test_case(Suite *s) { + tcase_add_test(tc_basic, test_long_cdata_utf16); + tcase_add_test(tc_basic, test_multichar_cdata_utf16); + tcase_add_test(tc_basic, test_utf16_bad_surrogate_pair); ++ tcase_add_test(tc_basic, test_utf16_surrogate_pairs); + tcase_add_test(tc_basic, test_bad_cdata); + tcase_add_test(tc_basic, test_bad_cdata_utf16); + tcase_add_test(tc_basic, test_stop_parser_between_cdata_calls); diff --git a/meta/recipes-core/expat/expat_2.8.3.bb b/meta/recipes-core/expat/expat_2.8.3.bb index c9a1c9b18e..6d08a3fd94 100644 --- a/meta/recipes-core/expat/expat_2.8.3.bb +++ b/meta/recipes-core/expat/expat_2.8.3.bb @@ -16,6 +16,8 @@ SRC_URI = "${GITHUB_BASE_URI}/download/R_${VERSION_TAG}/expat-${PV}.tar.bz2 \ file://CVE-2026-76956.patch \ file://CVE-2026-76957-01.patch \ file://CVE-2026-76957-02.patch \ + file://CVE-2026-93990-01.patch \ + file://CVE-2026-93990-02.patch \ " GITHUB_BASE_URI = "https://github.com/libexpat/libexpat/releases/" From patchwork Tue Oct 6 19:36:37 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Marko X-Patchwork-Id: 100078 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5989CCA5FFF for ; Tue, 6 Oct 2026 19:37:41 +0000 (UTC) Received: from mta-64-225.siemens.flowmailer.net (mta-64-225.siemens.flowmailer.net [185.136.64.225]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.3601.1791315459750414977 for ; Tue, 06 Oct 2026 12:37:40 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=peter.marko@siemens.com header.s=fm1 header.b=nU2BzP0q; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.64.225, mailfrom: fm-256628-20261006193737a1a3eb6b0200020745-cummkg@rts-flowmailer.siemens.com) Received: by mta-64-225.siemens.flowmailer.net with ESMTPSA id 20261006193737a1a3eb6b0200020745 for ; Tue, 06 Oct 2026 21:37:37 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm1; d=siemens.com; i=peter.marko@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=4wu2lfwRscdcPY4Zuxu0oFLBP2W5s94l+29PsAO9GrA=; b=nU2BzP0q91lW2YhBTmE2T2wqwr85rfrixKz5IWa/aTEH9WouCSyNjh1Boh5KEK9gbFTbhe cqj7qV2zLo38KOMQd8xD2JB9AWZwFSZylC5pjK0KULH+ppwzclccifZe7pHjagvy8d49vHaW DoDg4KJS+n76UzVpmgiNBbQbI3E9wWDQO/TkjTThQ30FcpKI74x/oLmpJNVQYH18pxPy/03J 7u9tDBtEsz8kJfCL4sYJf03kS8sGZs7GJ3dshIThGyYhg8p9+4ACV1u/oP382zoCDwanTqIw hZ8iOGjmY3r/q6NnOc7gBUh6dr+pzYbI9PQa4sNWRuefKBnloC9TzLbQ==; From: Peter Marko To: openembedded-core@lists.openembedded.org Cc: Peter Marko Subject: [wrynose][PATCH 5/6] expat: patch CVE-2026-102633 Date: Tue, 6 Oct 2026 21:36:37 +0200 Message-ID: <20261006193638.2018393-5-peter.marko@siemens.com> In-Reply-To: <20261006193638.2018393-1-peter.marko@siemens.com> References: <20261006193638.2018393-1-peter.marko@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-256628:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 06 Oct 2026 19:37:41 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/247324 From: Peter Marko Pick patch per [1]. [1] https://security-tracker.debian.org/tracker/CVE-2026-102633 Signed-off-by: Peter Marko --- .../expat/expat/CVE-2026-102633.patch | 68 +++++++++++++++++++ meta/recipes-core/expat/expat_2.8.3.bb | 1 + 2 files changed, 69 insertions(+) create mode 100644 meta/recipes-core/expat/expat/CVE-2026-102633.patch diff --git a/meta/recipes-core/expat/expat/CVE-2026-102633.patch b/meta/recipes-core/expat/expat/CVE-2026-102633.patch new file mode 100644 index 0000000000..1785120126 --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2026-102633.patch @@ -0,0 +1,68 @@ +From 69edbec09f2cd3e0bd6e0a093b55ea38af902d7d Mon Sep 17 00:00:00 2001 +From: Filippo Tedeschi +Date: Thu, 24 Sep 2026 21:14:23 +0200 +Subject: [PATCH] lib|tests: Replace assert with runtime overflow check in + expat_realloc + +In expat_realloc, the size passed to realloc_fcn is calculated as +sizeof(size_t) + EXPAT_MALLOC_PADDING + size. Previously, expat_realloc +relied on an assertion: +assert(SIZE_MAX - sizeof(size_t) - EXPAT_MALLOC_PADDING >= size); +under the assumption that preceding accounting checks and parser +invariants preclude reaching an overflow state in practice. + +However, in release builds compiled with NDEBUG, assertions are +compiled out. Replace the assert with an explicit runtime check: +if (size > SIZE_MAX - sizeof(size_t) - EXPAT_MALLOC_PADDING) { + return NULL; +} +immediately preceding the addition, ensuring consistent defensive +behavior across all build configurations. + +Also extend test_alloc_tracker_size_recorded in alloc_tests.c to +exercise SIZE_MAX and SIZE_MAX / 2 with expat_realloc, verifying +that the allocation fails and the recorded size remains unchanged. + +Signed-off-by: Filippo Tedeschi + +CVE: CVE-2026-102633 +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/69edbec09f2cd3e0bd6e0a093b55ea38af902d7d] +Signed-off-by: Peter Marko +--- + lib/xmlparse.c | 7 ++++--- + tests/alloc_tests.c | 4 ++++ + 2 files changed, 8 insertions(+), 3 deletions(-) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index dfcfb0c2..67005454 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -1023,9 +1023,10 @@ expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine) { + } + } + +- // NOTE: Integer overflow detection has already been done for us +- // by expat_heap_increase_tolerable(..) above +- assert(SIZE_MAX - sizeof(size_t) - EXPAT_MALLOC_PADDING >= size); ++ // Detect and prevent integer overflow ++ if (size > SIZE_MAX - sizeof(size_t) - EXPAT_MALLOC_PADDING) { ++ return NULL; ++ } + + // Actually allocate + mallocedPtr = parser->m_mem.realloc_fcn( +diff --git a/tests/alloc_tests.c b/tests/alloc_tests.c +index 6be0a073..a5cea463 100644 +--- a/tests/alloc_tests.c ++++ b/tests/alloc_tests.c +@@ -2118,6 +2118,10 @@ START_TEST(test_alloc_tracker_size_recorded) { + assert_true(ptr != NULL); + assert_true(sizeRecordedFor(ptr) == 10); + ++ assert_true(expat_realloc(parser, ptr, SIZE_MAX, -1) == NULL); ++ ++ assert_true(sizeRecordedFor(ptr) == 10); // i.e. unchanged ++ + assert_true(expat_realloc(parser, ptr, SIZE_MAX / 2, -1) == NULL); + + assert_true(sizeRecordedFor(ptr) == 10); // i.e. unchanged diff --git a/meta/recipes-core/expat/expat_2.8.3.bb b/meta/recipes-core/expat/expat_2.8.3.bb index 6d08a3fd94..3a179db62c 100644 --- a/meta/recipes-core/expat/expat_2.8.3.bb +++ b/meta/recipes-core/expat/expat_2.8.3.bb @@ -18,6 +18,7 @@ SRC_URI = "${GITHUB_BASE_URI}/download/R_${VERSION_TAG}/expat-${PV}.tar.bz2 \ file://CVE-2026-76957-02.patch \ file://CVE-2026-93990-01.patch \ file://CVE-2026-93990-02.patch \ + file://CVE-2026-102633.patch \ " GITHUB_BASE_URI = "https://github.com/libexpat/libexpat/releases/" From patchwork Tue Oct 6 19:36:38 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Marko X-Patchwork-Id: 100079 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4E4C7CA5FFF for ; Tue, 6 Oct 2026 19:37:51 +0000 (UTC) Received: from mta-65-227.siemens.flowmailer.net (mta-65-227.siemens.flowmailer.net [185.136.65.227]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.3604.1791315467443675716 for ; Tue, 06 Oct 2026 12:37:47 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=peter.marko@siemens.com header.s=fm1 header.b=oLpS/7rf; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.65.227, mailfrom: fm-256628-20261006193745e41f2f16780002079d-6bi0d6@rts-flowmailer.siemens.com) Received: by mta-65-227.siemens.flowmailer.net with ESMTPSA id 20261006193745e41f2f16780002079d for ; Tue, 06 Oct 2026 21:37:45 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm1; d=siemens.com; i=peter.marko@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=RGSuh2rbw1e/cOsqZSU8PjBTisjVPOX0gV2q1Dbl4Tk=; b=oLpS/7rfYDEdD8szlma/0cjTlnikWrjRWNger9GAPFKbrKeuJwyaIgnEDvK97OLPdHBzxc an0c6gmFJAj1/4n1GA58oi18n/uFhD3nyYkfEmOMmbBfezrGgBSmaLHjjMa2MYYmtl+DHx34 pPpXx/3+85t4A8krXOghzBNsZ4P1+R0IbEQzVN6QOTi9NlYdmm98DYENVNV94HbC0HsIeOzv BbcubSvARthIjHD83Amnw1lf644UaVkbH2+M33OFm/rOY2r3YnDLjHphQb7/3jk7TzgMSU5W 1PPhkt92/6rGbsgIxyqqBi+bT4ScM3ee98BdGPRgkdUgtNeUlodZ/Wow==; From: Peter Marko To: openembedded-core@lists.openembedded.org Cc: Peter Marko Subject: [wrynose][PATCH 6/6] expat: patch CVE-2026-77214 Date: Tue, 6 Oct 2026 21:36:38 +0200 Message-ID: <20261006193638.2018393-6-peter.marko@siemens.com> In-Reply-To: <20261006193638.2018393-1-peter.marko@siemens.com> References: <20261006193638.2018393-1-peter.marko@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-256628:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 06 Oct 2026 19:37:51 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/247325 From: Peter Marko Pick patch per [1]. [1] https://security-tracker.debian.org/tracker/CVE-2026-77214 Signed-off-by: Peter Marko --- .../expat/expat/CVE-2026-77214.patch | 106 ++++++++++++++++++ meta/recipes-core/expat/expat_2.8.3.bb | 1 + 2 files changed, 107 insertions(+) create mode 100644 meta/recipes-core/expat/expat/CVE-2026-77214.patch diff --git a/meta/recipes-core/expat/expat/CVE-2026-77214.patch b/meta/recipes-core/expat/expat/CVE-2026-77214.patch new file mode 100644 index 0000000000..6d74648705 --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2026-77214.patch @@ -0,0 +1,106 @@ +From 4d9b1c499ecb66323260a7274edcf86c5eab0517 Mon Sep 17 00:00:00 2001 +From: Filippo Tedeschi +Date: Fri, 25 Sep 2026 19:51:10 +0200 +Subject: [PATCH] lib|tests: Validate len against buffer capacity in + XML_ParseBuffer + +In XML_ParseBuffer(), if len exceeds the unoccupied capacity of the +internal buffer (EXPAT_SAFE_PTR_DIFF(m_bufferLim, m_bufferEnd)), +parser->m_bufferEnd was previously advanced past parser->m_bufferLim. +This violated the parser's internal pointer invariant (m_bufferEnd <= +m_bufferLim) and caused callProcessor() to read unallocated or +uninitialized heap memory beyond the buffer bounds. + +Validate that len <= EXPAT_SAFE_PTR_DIFF(m_bufferLim, m_bufferEnd) for +both XML_INITIALIZED and XML_PARSING states. If len exceeds the +available buffer capacity, return XML_STATUS_ERROR with +XML_ERROR_INVALID_ARGUMENT. + +Also add test_parse_buffer_exceeds_buffer in tests/basic_tests.c. + +Co-authored-by: Sebastian Pipping +Signed-off-by: Filippo Tedeschi + +CVE: CVE-2026-77214 +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/4d9b1c499ecb66323260a7274edcf86c5eab0517] +Signed-off-by: Peter Marko +--- + lib/xmlparse.c | 5 +++++ + tests/basic_tests.c | 41 +++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 46 insertions(+) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index 08ef9f72..6e3af97b 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -2465,6 +2465,11 @@ XML_ParseBuffer(XML_Parser parser, int len, int isFinal) { + parser->m_parsingStatus.parsing = XML_PARSING; + } + ++ if (len > EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferEnd)) { ++ parser->m_errorCode = XML_ERROR_INVALID_ARGUMENT; ++ return XML_STATUS_ERROR; ++ } ++ + // Detect and avoid integer overflow + if ((uint64_t)len > UINT64_MAX - parser->m_parseEndByteIndex) { + parser->m_errorCode = XML_ERROR_NO_MEMORY; +diff --git a/tests/basic_tests.c b/tests/basic_tests.c +index 06268469..35e17d31 100644 +--- a/tests/basic_tests.c ++++ b/tests/basic_tests.c +@@ -3605,6 +3605,46 @@ START_TEST(test_negative_len_parse_buffer) { + } + END_TEST + ++/* Test XML_ParseBuffer rejects calls where len exceeds available buffer */ ++START_TEST(test_parse_buffer_exceeds_buffer) { ++ for (int isFinal = 0; isFinal < 2; isFinal++) { ++ set_subtest("isFinal=%d", isFinal); ++ ++ XML_Parser parser = XML_ParserCreate(NULL); ++ ++ /* Calling XML_ParseBuffer without any prior XML_GetBuffer call */ ++ if (XML_ParseBuffer(parser, 10, isFinal) != XML_STATUS_ERROR) ++ fail("XML_ParseBuffer without XML_GetBuffer was expected to fail."); ++ if (XML_GetErrorCode(parser) != XML_ERROR_NO_BUFFER) ++ fail("Expected XML_ERROR_NO_BUFFER."); ++ ++ /* Acquire a small buffer */ ++ void *const buffer = XML_GetBuffer(parser, 10); ++ if (buffer == NULL) ++ fail("XML_GetBuffer failed."); ++ ++ /* Calling XML_ParseBuffer with len exceeding available buffer capacity */ ++ if (XML_ParseBuffer(parser, 10000, isFinal) != XML_STATUS_ERROR) ++ fail("XML_ParseBuffer with len > capacity was expected to fail."); ++ if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_ARGUMENT) ++ fail("Expected XML_ERROR_INVALID_ARGUMENT."); ++ ++ /* Valid parse */ ++ memcpy(buffer, "", 7); ++ if (XML_ParseBuffer(parser, 7, XML_FALSE) != XML_STATUS_OK) ++ xml_failure(parser); ++ ++ /* Subsequent XML_ParseBuffer where len exceeds remaining capacity */ ++ if (XML_ParseBuffer(parser, 10000, isFinal) != XML_STATUS_ERROR) ++ fail("XML_ParseBuffer on subsequent chunk was expected to fail."); ++ if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_ARGUMENT) ++ fail("Expected XML_ERROR_INVALID_ARGUMENT."); ++ ++ XML_ParserFree(parser); ++ } ++} ++END_TEST ++ + /* Test odd corners of the XML_GetBuffer interface */ + static enum XML_Status + get_feature(enum XML_FeatureEnum feature_id, long *presult) { +@@ -7054,6 +7094,7 @@ make_basic_test_case(Suite *s) { + tcase_add_test(tc_basic, test_empty_parse); + tcase_add_test(tc_basic, test_negative_len_parse); + tcase_add_test(tc_basic, test_negative_len_parse_buffer); ++ tcase_add_test(tc_basic, test_parse_buffer_exceeds_buffer); + tcase_add_test(tc_basic, test_get_buffer_1); + tcase_add_test(tc_basic, test_get_buffer_2); + #if XML_CONTEXT_BYTES > 0 diff --git a/meta/recipes-core/expat/expat_2.8.3.bb b/meta/recipes-core/expat/expat_2.8.3.bb index 3a179db62c..02a8536440 100644 --- a/meta/recipes-core/expat/expat_2.8.3.bb +++ b/meta/recipes-core/expat/expat_2.8.3.bb @@ -19,6 +19,7 @@ SRC_URI = "${GITHUB_BASE_URI}/download/R_${VERSION_TAG}/expat-${PV}.tar.bz2 \ file://CVE-2026-93990-01.patch \ file://CVE-2026-93990-02.patch \ file://CVE-2026-102633.patch \ + file://CVE-2026-77214.patch \ " GITHUB_BASE_URI = "https://github.com/libexpat/libexpat/releases/"