| Message ID | 20251020195449.1928091-1-peter.marko@siemens.com |
|---|---|
| State | Superseded |
| Headers | show |
| Series | [scarthgap] expat: patch CVE-2025-59375 | expand |
On Tue, Oct 21, 2025 at 8:55 AM Peter Marko via lists.openembedded.org <peter.marko=siemens.com@lists.openembedded.org> wrote: > > From: Peter Marko <peter.marko@siemens.com> > > Pick patch from PR mentioning this CVE [1] > > It's a complex patch so I have checked diff of 2.6.4 and commit before > these patches landed. There were no changes in memory allocations. > Also version in scarthgap is still not that much different from current > upstream master. > Ptests pass. > CVE fix caused SIGBUS error on sparc32 which got fixed as part of https://github.com/libexpat/libexpat/issues/1046 Should we include that as well? Otherwise patches LGTM. > Also picked one documentation commit (-00) to resolve patch conflict. > Following conflicts were resolved manually: > * commit "mass-cppcheck.sh: Activate in-code suppression comments" was > skipped as it only edited github actions not yet available in 2.6.4 > * commit "lib: Implement tracking of dynamic memory allocations" > ale had conflict in github actions not yet available in 2.6.4 > * commit "fuzz: Be robust towards NULL return from XML_ExternalEntityParserCreate" > edited file "expat/fuzz/xml_lpm_fuzzer.cpp" which is not present in > our version yet. Since we're not using fuzzying, this is not needed. > * the final changelog commit needed lot conflict resolution actions > > [1] `https://github.com/libexpat/libexpat/pull/1034` > > Signed-off-by: Peter Marko <peter.marko@siemens.com> > --- > .../expat/expat/CVE-2025-59375-00.patch | 52 ++ > .../expat/expat/CVE-2025-59375-01.patch | 48 ++ > .../expat/expat/CVE-2025-59375-02.patch | 109 ++++ > .../expat/expat/CVE-2025-59375-03.patch | 127 ++++ > .../expat/expat/CVE-2025-59375-04.patch | 62 ++ > .../expat/expat/CVE-2025-59375-05.patch | 64 ++ > .../expat/expat/CVE-2025-59375-06.patch | 68 +++ > .../expat/expat/CVE-2025-59375-07.patch | 52 ++ > .../expat/expat/CVE-2025-59375-08.patch | 577 ++++++++++++++++++ > .../expat/expat/CVE-2025-59375-09.patch | 43 ++ > .../expat/expat/CVE-2025-59375-10.patch | 54 ++ > .../expat/expat/CVE-2025-59375-11.patch | 66 ++ > .../expat/expat/CVE-2025-59375-12.patch | 58 ++ > .../expat/expat/CVE-2025-59375-13.patch | 309 ++++++++++ > .../expat/expat/CVE-2025-59375-14.patch | 122 ++++ > .../expat/expat/CVE-2025-59375-15.patch | 70 +++ > .../expat/expat/CVE-2025-59375-16.patch | 146 +++++ > .../expat/expat/CVE-2025-59375-17.patch | 28 + > .../expat/expat/CVE-2025-59375-18.patch | 74 +++ > meta/recipes-core/expat/expat_2.6.4.bb | 19 + > 20 files changed, 2148 insertions(+) > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-00.patch > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-01.patch > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-02.patch > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-03.patch > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-04.patch > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-05.patch > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-06.patch > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-07.patch > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-08.patch > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-09.patch > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-10.patch > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-11.patch > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-12.patch > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-13.patch > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-14.patch > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-15.patch > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-16.patch > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-17.patch > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-18.patch > > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-00.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-00.patch > new file mode 100644 > index 00000000000..e3cbd0f6048 > --- /dev/null > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-00.patch > @@ -0,0 +1,52 @@ > +From 87321ac84a0d6cb42ee64a591adc79c1ec37fb5b Mon Sep 17 00:00:00 2001 > +From: Sebastian Pipping <sebastian@pipping.org> > +Date: Tue, 2 Sep 2025 20:52:29 +0200 > +Subject: [PATCH] xmlwf: Mention supported environment variables in --help > + output > + > +CVE: CVE-2025-59375 > +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/87321ac84a0d6cb42ee64a591adc79c1ec37fb5b] > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > +--- > + xmlwf/xmlwf.c | 8 ++++++++ > + xmlwf/xmlwf_helpgen.py | 8 ++++++++ > + 2 files changed, 16 insertions(+) > + > +diff --git a/xmlwf/xmlwf.c b/xmlwf/xmlwf.c > +index ec7e51c9..8cfc73ca 100644 > +--- a/xmlwf/xmlwf.c > ++++ b/xmlwf/xmlwf.c > +@@ -926,6 +926,14 @@ usage(const XML_Char *prog, int rc) { > + T(" -h, --help show this [h]elp message and exit\n") > + T(" -v, --version show program's [v]ersion number and exit\n") > + T("\n") > ++ T("environment variables:\n") > ++ T(" EXPAT_ACCOUNTING_DEBUG=(0|1|2|3)\n") > ++ T(" Control verbosity of accounting debugging (default: 0)\n") > ++ T(" EXPAT_ENTITY_DEBUG=(0|1)\n") > ++ T(" Control verbosity of entity debugging (default: 0)\n") > ++ T(" EXPAT_ENTROPY_DEBUG=(0|1)\n") > ++ T(" Control verbosity of entropy debugging (default: 0)\n") > ++ T("\n") > + T("exit status:\n") > + T(" 0 the input files are well-formed and the output (if requested) was written successfully\n") > + T(" 1 could not allocate data structures, signals a serious problem with execution environment\n") > +diff --git a/xmlwf/xmlwf_helpgen.py b/xmlwf/xmlwf_helpgen.py > +index c3257f0e..39a3dc13 100755 > +--- a/xmlwf/xmlwf_helpgen.py > ++++ b/xmlwf/xmlwf_helpgen.py > +@@ -32,6 +32,14 @@ > + import argparse > + > + epilog = """ > ++environment variables: > ++ EXPAT_ACCOUNTING_DEBUG=(0|1|2|3) > ++ Control verbosity of accounting debugging (default: 0) > ++ EXPAT_ENTITY_DEBUG=(0|1) > ++ Control verbosity of entity debugging (default: 0) > ++ EXPAT_ENTROPY_DEBUG=(0|1) > ++ Control verbosity of entropy debugging (default: 0) > ++ > + exit status: > + 0 the input files are well-formed and the output (if requested) was written successfully > + 1 could not allocate data structures, signals a serious problem with execution environment > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-01.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-01.patch > new file mode 100644 > index 00000000000..6708bbef45c > --- /dev/null > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-01.patch > @@ -0,0 +1,48 @@ > +From 0872c189db6e457084fca335662a9cb49e8ec4c7 Mon Sep 17 00:00:00 2001 > +From: Sebastian Pipping <sebastian@pipping.org> > +Date: Mon, 1 Sep 2025 18:06:59 +0200 > +Subject: [PATCH] lib: Make function dtdCreate use macro MALLOC > + > +.. and give its body access to the parser for upcoming changes > + > +CVE: CVE-2025-59375 > +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/0872c189db6e457084fca335662a9cb49e8ec4c7] > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > +--- > + lib/xmlparse.c | 9 +++++---- > + 1 file changed, 5 insertions(+), 4 deletions(-) > + > +diff --git a/lib/xmlparse.c b/lib/xmlparse.c > +index 25f786ec..b9d6eed1 100644 > +--- a/lib/xmlparse.c > ++++ b/lib/xmlparse.c > +@@ -555,7 +555,7 @@ static XML_Bool setContext(XML_Parser parser, const XML_Char *context); > + > + static void FASTCALL normalizePublicId(XML_Char *s); > + > +-static DTD *dtdCreate(const XML_Memory_Handling_Suite *ms); > ++static DTD *dtdCreate(XML_Parser parser); > + /* do not call if m_parentParser != NULL */ > + static void dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms); > + static void dtdDestroy(DTD *p, XML_Bool isDocEntity, > +@@ -1166,7 +1166,7 @@ parserCreate(const XML_Char *encodingName, > + if (dtd) > + parser->m_dtd = dtd; > + else { > +- parser->m_dtd = dtdCreate(&parser->m_mem); > ++ parser->m_dtd = dtdCreate(parser); > + if (parser->m_dtd == NULL) { > + FREE(parser, parser->m_dataBuf); > + FREE(parser, parser->m_atts); > +@@ -7126,8 +7126,9 @@ normalizePublicId(XML_Char *publicId) { > + } > + > + static DTD * > +-dtdCreate(const XML_Memory_Handling_Suite *ms) { > +- DTD *p = ms->malloc_fcn(sizeof(DTD)); > ++dtdCreate(XML_Parser parser) { > ++ const XML_Memory_Handling_Suite *const ms = &parser->m_mem; > ++ DTD *p = MALLOC(parser, sizeof(DTD)); > + if (p == NULL) > + return p; > + poolInit(&(p->pool), ms); > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-02.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-02.patch > new file mode 100644 > index 00000000000..b0543370ad8 > --- /dev/null > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-02.patch > @@ -0,0 +1,109 @@ > +From 8768dadae479d9f2e984b747fb2ba79bb78de94f Mon Sep 17 00:00:00 2001 > +From: Sebastian Pipping <sebastian@pipping.org> > +Date: Mon, 1 Sep 2025 18:10:26 +0200 > +Subject: [PATCH] lib: Make string pools use macros MALLOC, FREE, REALLOC > + > +CVE: CVE-2025-59375 > +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/8768dadae479d9f2e984b747fb2ba79bb78de94f] > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > +--- > + lib/xmlparse.c | 27 +++++++++++++-------------- > + 1 file changed, 13 insertions(+), 14 deletions(-) > + > +diff --git a/lib/xmlparse.c b/lib/xmlparse.c > +index b9d6eed1..a56c71ea 100644 > +--- a/lib/xmlparse.c > ++++ b/lib/xmlparse.c > +@@ -357,7 +357,7 @@ typedef struct { > + const XML_Char *end; > + XML_Char *ptr; > + XML_Char *start; > +- const XML_Memory_Handling_Suite *mem; > ++ XML_Parser parser; > + } STRING_POOL; > + > + /* The XML_Char before the name is used to determine whether > +@@ -574,8 +574,7 @@ static void FASTCALL hashTableIterInit(HASH_TABLE_ITER *iter, > + const HASH_TABLE *table); > + static NAMED *FASTCALL hashTableIterNext(HASH_TABLE_ITER *iter); > + > +-static void FASTCALL poolInit(STRING_POOL *pool, > +- const XML_Memory_Handling_Suite *ms); > ++static void FASTCALL poolInit(STRING_POOL *pool, XML_Parser parser); > + static void FASTCALL poolClear(STRING_POOL *pool); > + static void FASTCALL poolDestroy(STRING_POOL *pool); > + static XML_Char *poolAppend(STRING_POOL *pool, const ENCODING *enc, > +@@ -1200,8 +1199,8 @@ parserCreate(const XML_Char *encodingName, > + > + parser->m_protocolEncodingName = NULL; > + > +- poolInit(&parser->m_tempPool, &(parser->m_mem)); > +- poolInit(&parser->m_temp2Pool, &(parser->m_mem)); > ++ poolInit(&parser->m_tempPool, parser); > ++ poolInit(&parser->m_temp2Pool, parser); > + parserInit(parser, encodingName); > + > + if (encodingName && ! parser->m_protocolEncodingName) { > +@@ -7131,8 +7130,8 @@ dtdCreate(XML_Parser parser) { > + DTD *p = MALLOC(parser, sizeof(DTD)); > + if (p == NULL) > + return p; > +- poolInit(&(p->pool), ms); > +- poolInit(&(p->entityValuePool), ms); > ++ poolInit(&(p->pool), parser); > ++ poolInit(&(p->entityValuePool), parser); > + hashTableInit(&(p->generalEntities), ms); > + hashTableInit(&(p->elementTypes), ms); > + hashTableInit(&(p->attributeIds), ms); > +@@ -7596,13 +7595,13 @@ hashTableIterNext(HASH_TABLE_ITER *iter) { > + } > + > + static void FASTCALL > +-poolInit(STRING_POOL *pool, const XML_Memory_Handling_Suite *ms) { > ++poolInit(STRING_POOL *pool, XML_Parser parser) { > + pool->blocks = NULL; > + pool->freeBlocks = NULL; > + pool->start = NULL; > + pool->ptr = NULL; > + pool->end = NULL; > +- pool->mem = ms; > ++ pool->parser = parser; > + } > + > + static void FASTCALL > +@@ -7629,13 +7628,13 @@ poolDestroy(STRING_POOL *pool) { > + BLOCK *p = pool->blocks; > + while (p) { > + BLOCK *tem = p->next; > +- pool->mem->free_fcn(p); > ++ FREE(pool->parser, p); > + p = tem; > + } > + p = pool->freeBlocks; > + while (p) { > + BLOCK *tem = p->next; > +- pool->mem->free_fcn(p); > ++ FREE(pool->parser, p); > + p = tem; > + } > + } > +@@ -7790,8 +7789,8 @@ poolGrow(STRING_POOL *pool) { > + if (bytesToAllocate == 0) > + return XML_FALSE; > + > +- temp = (BLOCK *)pool->mem->realloc_fcn(pool->blocks, > +- (unsigned)bytesToAllocate); > ++ temp = (BLOCK *)REALLOC(pool->parser, pool->blocks, > ++ (unsigned)bytesToAllocate); > + if (temp == NULL) > + return XML_FALSE; > + pool->blocks = temp; > +@@ -7831,7 +7830,7 @@ poolGrow(STRING_POOL *pool) { > + if (bytesToAllocate == 0) > + return XML_FALSE; > + > +- tem = pool->mem->malloc_fcn(bytesToAllocate); > ++ tem = MALLOC(pool->parser, bytesToAllocate); > + if (! tem) > + return XML_FALSE; > + tem->size = blockSize; > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-03.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-03.patch > new file mode 100644 > index 00000000000..b8c2c595e1a > --- /dev/null > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-03.patch > @@ -0,0 +1,127 @@ > +From 4fc6f1ee9f2b282cfe446bf645c992e37f8c3e15 Mon Sep 17 00:00:00 2001 > +From: Sebastian Pipping <sebastian@pipping.org> > +Date: Mon, 1 Sep 2025 18:14:09 +0200 > +Subject: [PATCH] lib: Make function hash tables use macros MALLOC and FREE > + > +CVE: CVE-2025-59375 > +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/4fc6f1ee9f2b282cfe446bf645c992e37f8c3e15] > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > +--- > + lib/xmlparse.c | 34 ++++++++++++++++------------------ > + 1 file changed, 16 insertions(+), 18 deletions(-) > + > +diff --git a/lib/xmlparse.c b/lib/xmlparse.c > +index a56c71ea..a65b0265 100644 > +--- a/lib/xmlparse.c > ++++ b/lib/xmlparse.c > +@@ -234,7 +234,7 @@ typedef struct { > + unsigned char power; > + size_t size; > + size_t used; > +- const XML_Memory_Handling_Suite *mem; > ++ XML_Parser parser; > + } HASH_TABLE; > + > + static size_t keylen(KEY s); > +@@ -566,8 +566,7 @@ static int copyEntityTable(XML_Parser oldParser, HASH_TABLE *newTable, > + STRING_POOL *newPool, const HASH_TABLE *oldTable); > + static NAMED *lookup(XML_Parser parser, HASH_TABLE *table, KEY name, > + size_t createSize); > +-static void FASTCALL hashTableInit(HASH_TABLE *table, > +- const XML_Memory_Handling_Suite *ms); > ++static void FASTCALL hashTableInit(HASH_TABLE *table, XML_Parser parser); > + static void FASTCALL hashTableClear(HASH_TABLE *table); > + static void FASTCALL hashTableDestroy(HASH_TABLE *table); > + static void FASTCALL hashTableIterInit(HASH_TABLE_ITER *iter, > +@@ -7126,19 +7125,18 @@ normalizePublicId(XML_Char *publicId) { > + > + static DTD * > + dtdCreate(XML_Parser parser) { > +- const XML_Memory_Handling_Suite *const ms = &parser->m_mem; > + DTD *p = MALLOC(parser, sizeof(DTD)); > + if (p == NULL) > + return p; > + poolInit(&(p->pool), parser); > + poolInit(&(p->entityValuePool), parser); > +- hashTableInit(&(p->generalEntities), ms); > +- hashTableInit(&(p->elementTypes), ms); > +- hashTableInit(&(p->attributeIds), ms); > +- hashTableInit(&(p->prefixes), ms); > ++ hashTableInit(&(p->generalEntities), parser); > ++ hashTableInit(&(p->elementTypes), parser); > ++ hashTableInit(&(p->attributeIds), parser); > ++ hashTableInit(&(p->prefixes), parser); > + #ifdef XML_DTD > + p->paramEntityRead = XML_FALSE; > +- hashTableInit(&(p->paramEntities), ms); > ++ hashTableInit(&(p->paramEntities), parser); > + #endif /* XML_DTD */ > + p->defaultPrefix.name = NULL; > + p->defaultPrefix.binding = NULL; > +@@ -7473,7 +7471,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) { > + /* table->size is a power of 2 */ > + table->size = (size_t)1 << INIT_POWER; > + tsize = table->size * sizeof(NAMED *); > +- table->v = table->mem->malloc_fcn(tsize); > ++ table->v = MALLOC(table->parser, tsize); > + if (! table->v) { > + table->size = 0; > + return NULL; > +@@ -7513,7 +7511,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) { > + } > + > + size_t tsize = newSize * sizeof(NAMED *); > +- NAMED **newV = table->mem->malloc_fcn(tsize); > ++ NAMED **newV = MALLOC(table->parser, tsize); > + if (! newV) > + return NULL; > + memset(newV, 0, tsize); > +@@ -7529,7 +7527,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) { > + } > + newV[j] = table->v[i]; > + } > +- table->mem->free_fcn(table->v); > ++ FREE(table->parser, table->v); > + table->v = newV; > + table->power = newPower; > + table->size = newSize; > +@@ -7542,7 +7540,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) { > + } > + } > + } > +- table->v[i] = table->mem->malloc_fcn(createSize); > ++ table->v[i] = MALLOC(table->parser, createSize); > + if (! table->v[i]) > + return NULL; > + memset(table->v[i], 0, createSize); > +@@ -7555,7 +7553,7 @@ static void FASTCALL > + hashTableClear(HASH_TABLE *table) { > + size_t i; > + for (i = 0; i < table->size; i++) { > +- table->mem->free_fcn(table->v[i]); > ++ FREE(table->parser, table->v[i]); > + table->v[i] = NULL; > + } > + table->used = 0; > +@@ -7565,17 +7563,17 @@ static void FASTCALL > + hashTableDestroy(HASH_TABLE *table) { > + size_t i; > + for (i = 0; i < table->size; i++) > +- table->mem->free_fcn(table->v[i]); > +- table->mem->free_fcn(table->v); > ++ FREE(table->parser, table->v[i]); > ++ FREE(table->parser, table->v); > + } > + > + static void FASTCALL > +-hashTableInit(HASH_TABLE *p, const XML_Memory_Handling_Suite *ms) { > ++hashTableInit(HASH_TABLE *p, XML_Parser parser) { > + p->power = 0; > + p->size = 0; > + p->used = 0; > + p->v = NULL; > +- p->mem = ms; > ++ p->parser = parser; > + } > + > + static void FASTCALL > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-04.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-04.patch > new file mode 100644 > index 00000000000..78d9e2fc91b > --- /dev/null > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-04.patch > @@ -0,0 +1,62 @@ > +From 51487ad9d760faa4809b0f8e189d2f666317e41a Mon Sep 17 00:00:00 2001 > +From: Sebastian Pipping <sebastian@pipping.org> > +Date: Mon, 1 Sep 2025 17:45:50 +0200 > +Subject: [PATCH] lib: Make function copyString use macro MALLOC > + > +CVE: CVE-2025-59375 > +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/51487ad9d760faa4809b0f8e189d2f666317e41a] > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > +--- > + lib/xmlparse.c | 11 +++++------ > + 1 file changed, 5 insertions(+), 6 deletions(-) > + > +diff --git a/lib/xmlparse.c b/lib/xmlparse.c > +index a65b0265..c0576abd 100644 > +--- a/lib/xmlparse.c > ++++ b/lib/xmlparse.c > +@@ -593,8 +593,7 @@ static XML_Content *build_model(XML_Parser parser); > + static ELEMENT_TYPE *getElementType(XML_Parser parser, const ENCODING *enc, > + const char *ptr, const char *end); > + > +-static XML_Char *copyString(const XML_Char *s, > +- const XML_Memory_Handling_Suite *memsuite); > ++static XML_Char *copyString(const XML_Char *s, XML_Parser parser); > + > + static unsigned long generate_hash_secret_salt(XML_Parser parser); > + static XML_Bool startParsing(XML_Parser parser); > +@@ -1231,7 +1230,7 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) { > + parser->m_processor = prologInitProcessor; > + XmlPrologStateInit(&parser->m_prologState); > + if (encodingName != NULL) { > +- parser->m_protocolEncodingName = copyString(encodingName, &(parser->m_mem)); > ++ parser->m_protocolEncodingName = copyString(encodingName, parser); > + } > + parser->m_curBase = NULL; > + XmlInitEncoding(&parser->m_initEncoding, &parser->m_encoding, 0); > +@@ -1419,7 +1418,7 @@ XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName) { > + parser->m_protocolEncodingName = NULL; > + else { > + /* Copy the new encoding name into allocated memory */ > +- parser->m_protocolEncodingName = copyString(encodingName, &(parser->m_mem)); > ++ parser->m_protocolEncodingName = copyString(encodingName, parser); > + if (! parser->m_protocolEncodingName) > + return XML_STATUS_ERROR; > + } > +@@ -8064,7 +8063,7 @@ getElementType(XML_Parser parser, const ENCODING *enc, const char *ptr, > + } > + > + static XML_Char * > +-copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) { > ++copyString(const XML_Char *s, XML_Parser parser) { > + size_t charsRequired = 0; > + XML_Char *result; > + > +@@ -8076,7 +8075,7 @@ copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) { > + charsRequired++; > + > + /* Now allocate space for the copy */ > +- result = memsuite->malloc_fcn(charsRequired * sizeof(XML_Char)); > ++ result = MALLOC(parser, charsRequired * sizeof(XML_Char)); > + if (result == NULL) > + return NULL; > + /* Copy the original into place */ > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-05.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-05.patch > new file mode 100644 > index 00000000000..37b882fbf49 > --- /dev/null > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-05.patch > @@ -0,0 +1,64 @@ > +From b3f0bda5f5e979781469532f7c304f7e223568d5 Mon Sep 17 00:00:00 2001 > +From: Sebastian Pipping <sebastian@pipping.org> > +Date: Mon, 1 Sep 2025 17:48:02 +0200 > +Subject: [PATCH] lib: Make function dtdReset use macro FREE > + > +CVE: CVE-2025-59375 > +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/b3f0bda5f5e979781469532f7c304f7e223568d5] > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > +--- > + lib/xmlparse.c | 12 ++++++------ > + 1 file changed, 6 insertions(+), 6 deletions(-) > + > +diff --git a/lib/xmlparse.c b/lib/xmlparse.c > +index c0576abd..65fcce30 100644 > +--- a/lib/xmlparse.c > ++++ b/lib/xmlparse.c > +@@ -557,7 +557,7 @@ static void FASTCALL normalizePublicId(XML_Char *s); > + > + static DTD *dtdCreate(XML_Parser parser); > + /* do not call if m_parentParser != NULL */ > +-static void dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms); > ++static void dtdReset(DTD *p, XML_Parser parser); > + static void dtdDestroy(DTD *p, XML_Bool isDocEntity, > + const XML_Memory_Handling_Suite *ms); > + static int dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, > +@@ -1382,7 +1382,7 @@ XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) { > + FREE(parser, (void *)parser->m_protocolEncodingName); > + parser->m_protocolEncodingName = NULL; > + parserInit(parser, encodingName); > +- dtdReset(parser->m_dtd, &parser->m_mem); > ++ dtdReset(parser->m_dtd, parser); > + return XML_TRUE; > + } > + > +@@ -7155,7 +7155,7 @@ dtdCreate(XML_Parser parser) { > + } > + > + static void > +-dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) { > ++dtdReset(DTD *p, XML_Parser parser) { > + HASH_TABLE_ITER iter; > + hashTableIterInit(&iter, &(p->elementTypes)); > + for (;;) { > +@@ -7163,7 +7163,7 @@ dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) { > + if (! e) > + break; > + if (e->allocDefaultAtts != 0) > +- ms->free_fcn(e->defaultAtts); > ++ FREE(parser, e->defaultAtts); > + } > + hashTableClear(&(p->generalEntities)); > + #ifdef XML_DTD > +@@ -7180,9 +7180,9 @@ dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) { > + > + p->in_eldecl = XML_FALSE; > + > +- ms->free_fcn(p->scaffIndex); > ++ FREE(parser, p->scaffIndex); > + p->scaffIndex = NULL; > +- ms->free_fcn(p->scaffold); > ++ FREE(parser, p->scaffold); > + p->scaffold = NULL; > + > + p->scaffLevel = 0; > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-06.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-06.patch > new file mode 100644 > index 00000000000..04f975a458f > --- /dev/null > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-06.patch > @@ -0,0 +1,68 @@ > +From 53a3eda0ae2e0317afd071b72b41976053d82732 Mon Sep 17 00:00:00 2001 > +From: Sebastian Pipping <sebastian@pipping.org> > +Date: Mon, 1 Sep 2025 17:50:59 +0200 > +Subject: [PATCH] lib: Make function dtdDestroy use macro FREE > + > +CVE: CVE-2025-59375 > +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/53a3eda0ae2e0317afd071b72b41976053d82732] > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > +--- > + lib/xmlparse.c | 16 +++++++--------- > + 1 file changed, 7 insertions(+), 9 deletions(-) > + > +diff --git a/lib/xmlparse.c b/lib/xmlparse.c > +index 65fcce30..e7df97da 100644 > +--- a/lib/xmlparse.c > ++++ b/lib/xmlparse.c > +@@ -558,8 +558,7 @@ static void FASTCALL normalizePublicId(XML_Char *s); > + static DTD *dtdCreate(XML_Parser parser); > + /* do not call if m_parentParser != NULL */ > + static void dtdReset(DTD *p, XML_Parser parser); > +-static void dtdDestroy(DTD *p, XML_Bool isDocEntity, > +- const XML_Memory_Handling_Suite *ms); > ++static void dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser); > + static int dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, > + const XML_Memory_Handling_Suite *ms); > + static int copyEntityTable(XML_Parser oldParser, HASH_TABLE *newTable, > +@@ -1685,8 +1684,7 @@ XML_ParserFree(XML_Parser parser) { > + #else > + if (parser->m_dtd) > + #endif /* XML_DTD */ > +- dtdDestroy(parser->m_dtd, (XML_Bool)! parser->m_parentParser, > +- &parser->m_mem); > ++ dtdDestroy(parser->m_dtd, (XML_Bool)! parser->m_parentParser, parser); > + FREE(parser, (void *)parser->m_atts); > + #ifdef XML_ATTR_INFO > + FREE(parser, (void *)parser->m_attInfo); > +@@ -7196,7 +7194,7 @@ dtdReset(DTD *p, XML_Parser parser) { > + } > + > + static void > +-dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) { > ++dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser) { > + HASH_TABLE_ITER iter; > + hashTableIterInit(&iter, &(p->elementTypes)); > + for (;;) { > +@@ -7204,7 +7202,7 @@ dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) { > + if (! e) > + break; > + if (e->allocDefaultAtts != 0) > +- ms->free_fcn(e->defaultAtts); > ++ FREE(parser, e->defaultAtts); > + } > + hashTableDestroy(&(p->generalEntities)); > + #ifdef XML_DTD > +@@ -7216,10 +7214,10 @@ dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) { > + poolDestroy(&(p->pool)); > + poolDestroy(&(p->entityValuePool)); > + if (isDocEntity) { > +- ms->free_fcn(p->scaffIndex); > +- ms->free_fcn(p->scaffold); > ++ FREE(parser, p->scaffIndex); > ++ FREE(parser, p->scaffold); > + } > +- ms->free_fcn(p); > ++ FREE(parser, p); > + } > + > + /* Do a deep copy of the DTD. Return 0 for out of memory, non-zero otherwise. > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-07.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-07.patch > new file mode 100644 > index 00000000000..7eff0009d2c > --- /dev/null > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-07.patch > @@ -0,0 +1,52 @@ > +From 4e7a5d03daf672f20c73d40dc8970385c18b30d3 Mon Sep 17 00:00:00 2001 > +From: Sebastian Pipping <sebastian@pipping.org> > +Date: Mon, 1 Sep 2025 17:52:58 +0200 > +Subject: [PATCH] lib: Make function dtdCopy use macro MALLOC > + > +CVE: CVE-2025-59375 > +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/4e7a5d03daf672f20c73d40dc8970385c18b30d3] > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > +--- > + lib/xmlparse.c | 8 ++++---- > + 1 file changed, 4 insertions(+), 4 deletions(-) > + > +diff --git a/lib/xmlparse.c b/lib/xmlparse.c > +index e7df97da..9f0a8b3e 100644 > +--- a/lib/xmlparse.c > ++++ b/lib/xmlparse.c > +@@ -560,7 +560,7 @@ static DTD *dtdCreate(XML_Parser parser); > + static void dtdReset(DTD *p, XML_Parser parser); > + static void dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser); > + static int dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, > +- const XML_Memory_Handling_Suite *ms); > ++ XML_Parser parser); > + static int copyEntityTable(XML_Parser oldParser, HASH_TABLE *newTable, > + STRING_POOL *newPool, const HASH_TABLE *oldTable); > + static NAMED *lookup(XML_Parser parser, HASH_TABLE *table, KEY name, > +@@ -1572,7 +1572,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context, > + parser->m_prologState.inEntityValue = oldInEntityValue; > + if (context) { > + #endif /* XML_DTD */ > +- if (! dtdCopy(oldParser, parser->m_dtd, oldDtd, &parser->m_mem) > ++ if (! dtdCopy(oldParser, parser->m_dtd, oldDtd, parser) > + || ! setContext(parser, context)) { > + XML_ParserFree(parser); > + return NULL; > +@@ -7225,7 +7225,7 @@ dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser) { > + */ > + static int > + dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, > +- const XML_Memory_Handling_Suite *ms) { > ++ XML_Parser parser) { > + HASH_TABLE_ITER iter; > + > + /* Copy the prefix table. */ > +@@ -7306,7 +7306,7 @@ dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, > + } > + #endif > + newE->defaultAtts > +- = ms->malloc_fcn(oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE)); > ++ = MALLOC(parser, oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE)); > + if (! newE->defaultAtts) { > + return 0; > + } > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-08.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-08.patch > new file mode 100644 > index 00000000000..deda31bebcd > --- /dev/null > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-08.patch > @@ -0,0 +1,577 @@ > +From cfce28e171676fe6f70d17b97ed8a59eaeb83f15 Mon Sep 17 00:00:00 2001 > +From: Sebastian Pipping <sebastian@pipping.org> > +Date: Mon, 1 Sep 2025 17:34:58 +0200 > +Subject: [PATCH] lib: Implement tracking of dynamic memory allocations > + > +**PLEASE NOTE** that distributors intending to backport (or cherry-pick) > +this fix need to copy 99% of the related pull request, not just this > +commit, to not end up with a state that literally does both too much and > +too little at the same time. Appending ".diff" to the pull request URL > +could be of help. > + > +CVE: CVE-2025-59375 > +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/cfce28e171676fe6f70d17b97ed8a59eaeb83f15] > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > +--- > + lib/expat.h | 15 +- > + lib/internal.h | 5 + > + lib/libexpat.def.cmake | 3 + > + lib/xmlparse.c | 337 +++++++++++++++++++++++++++++++++++++++-- > + tests/basic_tests.c | 4 + > + tests/nsalloc_tests.c | 5 + > + xmlwf/xmlwf.c | 2 + > + xmlwf/xmlwf_helpgen.py | 2 + > + 8 files changed, 361 insertions(+), 12 deletions(-) > + > +diff --git a/lib/expat.h b/lib/expat.h > +index 610e1ddc..66a253c1 100644 > +--- a/lib/expat.h > ++++ b/lib/expat.h > +@@ -1032,7 +1032,10 @@ enum XML_FeatureEnum { > + XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT, > + XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT, > + /* Added in Expat 2.6.0. */ > +- XML_FEATURE_GE > ++ XML_FEATURE_GE, > ++ /* Added in Expat 2.7.2. */ > ++ XML_FEATURE_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT, > ++ XML_FEATURE_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT, > + /* Additional features must be added to the end of this enum. */ > + }; > + > +@@ -1057,6 +1060,16 @@ XML_SetBillionLaughsAttackProtectionMaximumAmplification( > + XMLPARSEAPI(XML_Bool) > + XML_SetBillionLaughsAttackProtectionActivationThreshold( > + XML_Parser parser, unsigned long long activationThresholdBytes); > ++ > ++/* Added in Expat 2.7.2. */ > ++XMLPARSEAPI(XML_Bool) > ++XML_SetAllocTrackerMaximumAmplification(XML_Parser parser, > ++ float maximumAmplificationFactor); > ++ > ++/* Added in Expat 2.7.2. */ > ++XMLPARSEAPI(XML_Bool) > ++XML_SetAllocTrackerActivationThreshold( > ++ XML_Parser parser, unsigned long long activationThresholdBytes); > + #endif > + > + /* Added in Expat 2.6.0. */ > +diff --git a/lib/internal.h b/lib/internal.h > +index 6bde6ae6..eb67cf50 100644 > +--- a/lib/internal.h > ++++ b/lib/internal.h > +@@ -145,6 +145,11 @@ > + 100.0f > + #define EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT \ > + 8388608 // 8 MiB, 2^23 > ++ > ++#define EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT 100.0f > ++#define EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT \ > ++ 67108864 // 64 MiB, 2^26 > ++ > + /* NOTE END */ > + > + #include "expat.h" // so we can use type XML_Parser below > +diff --git a/lib/libexpat.def.cmake b/lib/libexpat.def.cmake > +index 10ee9cd6..7a3a7ec0 100644 > +--- a/lib/libexpat.def.cmake > ++++ b/lib/libexpat.def.cmake > +@@ -79,3 +79,6 @@ EXPORTS > + @_EXPAT_COMMENT_DTD_OR_GE@ XML_SetBillionLaughsAttackProtectionMaximumAmplification @70 > + ; added with version 2.6.0 > + XML_SetReparseDeferralEnabled @71 > ++; added with version 2.7.2 > ++@_EXPAT_COMMENT_DTD_OR_GE@ XML_SetAllocTrackerMaximumAmplification @72 > ++@_EXPAT_COMMENT_DTD_OR_GE@ XML_SetAllocTrackerActivationThreshold @73 > +diff --git a/lib/xmlparse.c b/lib/xmlparse.c > +index 9f0a8b3e..fcf1cfdd 100644 > +--- a/lib/xmlparse.c > ++++ b/lib/xmlparse.c > +@@ -452,6 +452,14 @@ typedef struct accounting { > + unsigned long long activationThresholdBytes; > + } ACCOUNTING; > + > ++typedef struct MALLOC_TRACKER { > ++ XmlBigCount bytesAllocated; > ++ XmlBigCount peakBytesAllocated; // updated live only for debug level >=2 > ++ unsigned long debugLevel; > ++ float maximumAmplificationFactor; // >=1.0 > ++ XmlBigCount activationThresholdBytes; > ++} MALLOC_TRACKER; > ++ > + typedef struct entity_stats { > + unsigned int countEverOpened; > + unsigned int currentDepth; > +@@ -599,7 +607,8 @@ static XML_Bool startParsing(XML_Parser parser); > + > + static XML_Parser parserCreate(const XML_Char *encodingName, > + const XML_Memory_Handling_Suite *memsuite, > +- const XML_Char *nameSep, DTD *dtd); > ++ const XML_Char *nameSep, DTD *dtd, > ++ XML_Parser parentParser); > + > + static void parserInit(XML_Parser parser, const XML_Char *encodingName); > + > +@@ -769,14 +778,220 @@ struct XML_ParserStruct { > + unsigned long m_hash_secret_salt; > + #if XML_GE == 1 > + ACCOUNTING m_accounting; > ++ MALLOC_TRACKER m_alloc_tracker; > + ENTITY_STATS m_entity_stats; > + #endif > + XML_Bool m_reenter; > + }; > + > +-#define MALLOC(parser, s) (parser->m_mem.malloc_fcn((s))) > +-#define REALLOC(parser, p, s) (parser->m_mem.realloc_fcn((p), (s))) > +-#define FREE(parser, p) (parser->m_mem.free_fcn((p))) > ++#if XML_GE == 1 > ++# define MALLOC(parser, s) (expat_malloc((parser), (s), __LINE__)) > ++# define REALLOC(parser, p, s) (expat_realloc((parser), (p), (s), __LINE__)) > ++# define FREE(parser, p) (expat_free((parser), (p), __LINE__)) > ++#else > ++# define MALLOC(parser, s) (parser->m_mem.malloc_fcn((s))) > ++# define REALLOC(parser, p, s) (parser->m_mem.realloc_fcn((p), (s))) > ++# define FREE(parser, p) (parser->m_mem.free_fcn((p))) > ++#endif > ++ > ++#if XML_GE == 1 > ++static void > ++expat_heap_stat(XML_Parser rootParser, char operator, XmlBigCount absDiff, > ++ XmlBigCount newTotal, XmlBigCount peakTotal, int sourceLine) { > ++ // NOTE: This can be +infinity or -nan > ++ const float amplification > ++ = (float)newTotal / (float)rootParser->m_accounting.countBytesDirect; > ++ fprintf( > ++ stderr, > ++ "expat: Allocations(%p): Direct " EXPAT_FMT_ULL("10") ", allocated %c" EXPAT_FMT_ULL( > ++ "10") " to " EXPAT_FMT_ULL("10") " (" EXPAT_FMT_ULL("10") " peak), amplification %8.2f (xmlparse.c:%d)\n", > ++ (void *)rootParser, rootParser->m_accounting.countBytesDirect, operator, > ++ absDiff, newTotal, peakTotal, (double)amplification, sourceLine); > ++} > ++ > ++static bool > ++expat_heap_increase_tolerable(XML_Parser rootParser, XmlBigCount increase, > ++ int sourceLine) { > ++ assert(rootParser != NULL); > ++ assert(increase > 0); > ++ > ++ XmlBigCount newTotal = 0; > ++ bool tolerable = true; > ++ > ++ // Detect integer overflow > ++ if ((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated < increase) { > ++ tolerable = false; > ++ } else { > ++ newTotal = rootParser->m_alloc_tracker.bytesAllocated + increase; > ++ > ++ if (newTotal >= rootParser->m_alloc_tracker.activationThresholdBytes) { > ++ assert(newTotal > 0); > ++ // NOTE: This can be +infinity when dividing by zero but not -nan > ++ const float amplification > ++ = (float)newTotal / (float)rootParser->m_accounting.countBytesDirect; > ++ if (amplification > ++ > rootParser->m_alloc_tracker.maximumAmplificationFactor) { > ++ tolerable = false; > ++ } > ++ } > ++ } > ++ > ++ if (! tolerable && (rootParser->m_alloc_tracker.debugLevel >= 1)) { > ++ expat_heap_stat(rootParser, '+', increase, newTotal, newTotal, sourceLine); > ++ } > ++ > ++ return tolerable; > ++} > ++ > ++static void * > ++expat_malloc(XML_Parser parser, size_t size, int sourceLine) { > ++ // Detect integer overflow > ++ if (SIZE_MAX - size < sizeof(size_t)) { > ++ return NULL; > ++ } > ++ > ++ const XML_Parser rootParser = getRootParserOf(parser, NULL); > ++ assert(rootParser->m_parentParser == NULL); > ++ > ++ const size_t bytesToAllocate = sizeof(size_t) + size; > ++ > ++ if ((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated > ++ < bytesToAllocate) { > ++ return NULL; // i.e. signal integer overflow as out-of-memory > ++ } > ++ > ++ if (! expat_heap_increase_tolerable(rootParser, bytesToAllocate, > ++ sourceLine)) { > ++ return NULL; // i.e. signal violation as out-of-memory > ++ } > ++ > ++ // Actually allocate > ++ void *const mallocedPtr = parser->m_mem.malloc_fcn(bytesToAllocate); > ++ > ++ if (mallocedPtr == NULL) { > ++ return NULL; > ++ } > ++ > ++ // Update in-block recorded size > ++ *(size_t *)mallocedPtr = size; > ++ > ++ // Update accounting > ++ rootParser->m_alloc_tracker.bytesAllocated += bytesToAllocate; > ++ > ++ // Report as needed > ++ if (rootParser->m_alloc_tracker.debugLevel >= 2) { > ++ if (rootParser->m_alloc_tracker.bytesAllocated > ++ > rootParser->m_alloc_tracker.peakBytesAllocated) { > ++ rootParser->m_alloc_tracker.peakBytesAllocated > ++ = rootParser->m_alloc_tracker.bytesAllocated; > ++ } > ++ expat_heap_stat(rootParser, '+', bytesToAllocate, > ++ rootParser->m_alloc_tracker.bytesAllocated, > ++ rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine); > ++ } > ++ > ++ return (char *)mallocedPtr + sizeof(size_t); > ++} > ++ > ++static void > ++expat_free(XML_Parser parser, void *ptr, int sourceLine) { > ++ assert(parser != NULL); > ++ > ++ if (ptr == NULL) { > ++ return; > ++ } > ++ > ++ const XML_Parser rootParser = getRootParserOf(parser, NULL); > ++ assert(rootParser->m_parentParser == NULL); > ++ > ++ // Extract size (to the eyes of malloc_fcn/realloc_fcn) and > ++ // the original pointer returned by malloc/realloc > ++ void *const mallocedPtr = (char *)ptr - sizeof(size_t); > ++ const size_t bytesAllocated = sizeof(size_t) + *(size_t *)mallocedPtr; > ++ > ++ // Update accounting > ++ assert(rootParser->m_alloc_tracker.bytesAllocated >= bytesAllocated); > ++ rootParser->m_alloc_tracker.bytesAllocated -= bytesAllocated; > ++ > ++ // Report as needed > ++ if (rootParser->m_alloc_tracker.debugLevel >= 2) { > ++ expat_heap_stat(rootParser, '-', bytesAllocated, > ++ rootParser->m_alloc_tracker.bytesAllocated, > ++ rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine); > ++ } > ++ > ++ // NOTE: This may be freeing rootParser, so freeing has to come last > ++ parser->m_mem.free_fcn(mallocedPtr); > ++} > ++ > ++static void * > ++expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine) { > ++ assert(parser != NULL); > ++ > ++ if (ptr == NULL) { > ++ return expat_malloc(parser, size, sourceLine); > ++ } > ++ > ++ if (size == 0) { > ++ expat_free(parser, ptr, sourceLine); > ++ return NULL; > ++ } > ++ > ++ const XML_Parser rootParser = getRootParserOf(parser, NULL); > ++ assert(rootParser->m_parentParser == NULL); > ++ > ++ // Extract original size (to the eyes of the caller) and the original > ++ // pointer returned by malloc/realloc > ++ void *mallocedPtr = (char *)ptr - sizeof(size_t); > ++ const size_t prevSize = *(size_t *)mallocedPtr; > ++ > ++ // Classify upcoming change > ++ const bool isIncrease = (size > prevSize); > ++ const size_t absDiff > ++ = (size > prevSize) ? (size - prevSize) : (prevSize - size); > ++ > ++ // Ask for permission from accounting > ++ if (isIncrease) { > ++ if (! expat_heap_increase_tolerable(rootParser, absDiff, sourceLine)) { > ++ return NULL; // i.e. signal violation as out-of-memory > ++ } > ++ } > ++ > ++ // Actually allocate > ++ mallocedPtr = parser->m_mem.realloc_fcn(mallocedPtr, sizeof(size_t) + size); > ++ > ++ if (mallocedPtr == NULL) { > ++ return NULL; > ++ } > ++ > ++ // Update accounting > ++ if (isIncrease) { > ++ assert((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated > ++ >= absDiff); > ++ rootParser->m_alloc_tracker.bytesAllocated += absDiff; > ++ } else { // i.e. decrease > ++ assert(rootParser->m_alloc_tracker.bytesAllocated >= absDiff); > ++ rootParser->m_alloc_tracker.bytesAllocated -= absDiff; > ++ } > ++ > ++ // Report as needed > ++ if (rootParser->m_alloc_tracker.debugLevel >= 2) { > ++ if (rootParser->m_alloc_tracker.bytesAllocated > ++ > rootParser->m_alloc_tracker.peakBytesAllocated) { > ++ rootParser->m_alloc_tracker.peakBytesAllocated > ++ = rootParser->m_alloc_tracker.bytesAllocated; > ++ } > ++ expat_heap_stat(rootParser, isIncrease ? '+' : '-', absDiff, > ++ rootParser->m_alloc_tracker.bytesAllocated, > ++ rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine); > ++ } > ++ > ++ // Update in-block recorded size > ++ *(size_t *)mallocedPtr = size; > ++ > ++ return (char *)mallocedPtr + sizeof(size_t); > ++} > ++#endif // XML_GE == 1 > + > + XML_Parser XMLCALL > + XML_ParserCreate(const XML_Char *encodingName) { > +@@ -1096,19 +1311,40 @@ XML_Parser XMLCALL > + XML_ParserCreate_MM(const XML_Char *encodingName, > + const XML_Memory_Handling_Suite *memsuite, > + const XML_Char *nameSep) { > +- return parserCreate(encodingName, memsuite, nameSep, NULL); > ++ return parserCreate(encodingName, memsuite, nameSep, NULL, NULL); > + } > + > + static XML_Parser > + parserCreate(const XML_Char *encodingName, > + const XML_Memory_Handling_Suite *memsuite, const XML_Char *nameSep, > +- DTD *dtd) { > +- XML_Parser parser; > ++ DTD *dtd, XML_Parser parentParser) { > ++ XML_Parser parser = NULL; > ++ > ++#if XML_GE == 1 > ++ const size_t increase = sizeof(size_t) + sizeof(struct XML_ParserStruct); > ++ > ++ if (parentParser != NULL) { > ++ const XML_Parser rootParser = getRootParserOf(parentParser, NULL); > ++ if (! expat_heap_increase_tolerable(rootParser, increase, __LINE__)) { > ++ return NULL; > ++ } > ++ } > ++#else > ++ UNUSED_P(parentParser); > ++#endif > + > + if (memsuite) { > + XML_Memory_Handling_Suite *mtemp; > ++#if XML_GE == 1 > ++ void *const sizeAndParser = memsuite->malloc_fcn( > ++ sizeof(size_t) + sizeof(struct XML_ParserStruct)); > ++ if (sizeAndParser != NULL) { > ++ *(size_t *)sizeAndParser = sizeof(struct XML_ParserStruct); > ++ parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t)); > ++#else > + parser = memsuite->malloc_fcn(sizeof(struct XML_ParserStruct)); > + if (parser != NULL) { > ++#endif > + mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem); > + mtemp->malloc_fcn = memsuite->malloc_fcn; > + mtemp->realloc_fcn = memsuite->realloc_fcn; > +@@ -1116,18 +1352,67 @@ parserCreate(const XML_Char *encodingName, > + } > + } else { > + XML_Memory_Handling_Suite *mtemp; > ++#if XML_GE == 1 > ++ void *const sizeAndParser > ++ = (XML_Parser)malloc(sizeof(size_t) + sizeof(struct XML_ParserStruct)); > ++ if (sizeAndParser != NULL) { > ++ *(size_t *)sizeAndParser = sizeof(struct XML_ParserStruct); > ++ parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t)); > ++#else > + parser = (XML_Parser)malloc(sizeof(struct XML_ParserStruct)); > + if (parser != NULL) { > ++#endif > + mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem); > + mtemp->malloc_fcn = malloc; > + mtemp->realloc_fcn = realloc; > + mtemp->free_fcn = free; > + } > +- } > ++ } // cppcheck-suppress[memleak symbolName=sizeAndParser] // Cppcheck >=2.18.0 > + > + if (! parser) > + return parser; > + > ++#if XML_GE == 1 > ++ // Initialize .m_alloc_tracker > ++ memset(&parser->m_alloc_tracker, 0, sizeof(MALLOC_TRACKER)); > ++ if (parentParser == NULL) { > ++ parser->m_alloc_tracker.debugLevel > ++ = getDebugLevel("EXPAT_MALLOC_DEBUG", 0u); > ++ parser->m_alloc_tracker.maximumAmplificationFactor > ++ = EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT; > ++ parser->m_alloc_tracker.activationThresholdBytes > ++ = EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT; > ++ > ++ // NOTE: This initialization needs to come this early because these fields > ++ // are read by allocation tracking code > ++ parser->m_parentParser = NULL; > ++ parser->m_accounting.countBytesDirect = 0; > ++ } else { > ++ parser->m_parentParser = parentParser; > ++ } > ++ > ++ // Record XML_ParserStruct allocation we did a few lines up before > ++ const XML_Parser rootParser = getRootParserOf(parser, NULL); > ++ assert(rootParser->m_parentParser == NULL); > ++ assert(SIZE_MAX - rootParser->m_alloc_tracker.bytesAllocated >= increase); > ++ rootParser->m_alloc_tracker.bytesAllocated += increase; > ++ > ++ // Report on allocation > ++ if (rootParser->m_alloc_tracker.debugLevel >= 2) { > ++ if (rootParser->m_alloc_tracker.bytesAllocated > ++ > rootParser->m_alloc_tracker.peakBytesAllocated) { > ++ rootParser->m_alloc_tracker.peakBytesAllocated > ++ = rootParser->m_alloc_tracker.bytesAllocated; > ++ } > ++ > ++ expat_heap_stat(rootParser, '+', increase, > ++ rootParser->m_alloc_tracker.bytesAllocated, > ++ rootParser->m_alloc_tracker.peakBytesAllocated, __LINE__); > ++ } > ++#else > ++ parser->m_parentParser = NULL; > ++#endif // XML_GE == 1 > ++ > + parser->m_buffer = NULL; > + parser->m_bufferLim = NULL; > + > +@@ -1291,7 +1576,6 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) { > + parser->m_unknownEncodingMem = NULL; > + parser->m_unknownEncodingRelease = NULL; > + parser->m_unknownEncodingData = NULL; > +- parser->m_parentParser = NULL; > + parser->m_parsingStatus.parsing = XML_INITIALIZED; > + // Reentry can only be triggered inside m_processor calls > + parser->m_reenter = XML_FALSE; > +@@ -1526,9 +1810,10 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context, > + */ > + if (parser->m_ns) { > + XML_Char tmp[2] = {parser->m_namespaceSeparator, 0}; > +- parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd); > ++ parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd, oldParser); > + } else { > +- parser = parserCreate(encodingName, &parser->m_mem, NULL, newDtd); > ++ parser > ++ = parserCreate(encodingName, &parser->m_mem, NULL, newDtd, oldParser); > + } > + > + if (! parser) > +@@ -2708,6 +2993,13 @@ XML_GetFeatureList(void) { > + EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT}, > + /* Added in Expat 2.6.0. */ > + {XML_FEATURE_GE, XML_L("XML_GE"), 0}, > ++ /* Added in Expat 2.7.2. */ > ++ {XML_FEATURE_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT, > ++ XML_L("XML_AT_MAX_AMP"), > ++ (long int)EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT}, > ++ {XML_FEATURE_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT, > ++ XML_L("XML_AT_ACT_THRES"), > ++ (long int)EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT}, > + #endif > + {XML_FEATURE_END, NULL, 0}}; > + > +@@ -2736,6 +3028,29 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold( > + parser->m_accounting.activationThresholdBytes = activationThresholdBytes; > + return XML_TRUE; > + } > ++ > ++XML_Bool XMLCALL > ++XML_SetAllocTrackerMaximumAmplification(XML_Parser parser, > ++ float maximumAmplificationFactor) { > ++ if ((parser == NULL) || (parser->m_parentParser != NULL) > ++ || isnan(maximumAmplificationFactor) > ++ || (maximumAmplificationFactor < 1.0f)) { > ++ return XML_FALSE; > ++ } > ++ parser->m_alloc_tracker.maximumAmplificationFactor > ++ = maximumAmplificationFactor; > ++ return XML_TRUE; > ++} > ++ > ++XML_Bool XMLCALL > ++XML_SetAllocTrackerActivationThreshold( > ++ XML_Parser parser, unsigned long long activationThresholdBytes) { > ++ if ((parser == NULL) || (parser->m_parentParser != NULL)) { > ++ return XML_FALSE; > ++ } > ++ parser->m_alloc_tracker.activationThresholdBytes = activationThresholdBytes; > ++ return XML_TRUE; > ++} > + #endif /* XML_GE == 1 */ > + > + XML_Bool XMLCALL > +diff --git a/tests/basic_tests.c b/tests/basic_tests.c > +index 129db1d8..0231e094 100644 > +--- a/tests/basic_tests.c > ++++ b/tests/basic_tests.c > +@@ -3089,6 +3089,10 @@ START_TEST(test_buffer_can_grow_to_max) { > + for (int i = 0; i < num_prefixes; ++i) { > + set_subtest("\"%s\"", prefixes[i]); > + XML_Parser parser = XML_ParserCreate(NULL); > ++#if XML_GE == 1 > ++ assert_true(XML_SetAllocTrackerActivationThreshold(parser, (size_t)-1) > ++ == XML_TRUE); // i.e. deactivate > ++#endif > + const int prefix_len = (int)strlen(prefixes[i]); > + const enum XML_Status s > + = _XML_Parse_SINGLE_BYTES(parser, prefixes[i], prefix_len, XML_FALSE); > +diff --git a/tests/nsalloc_tests.c b/tests/nsalloc_tests.c > +index 48520f42..0a594e14 100644 > +--- a/tests/nsalloc_tests.c > ++++ b/tests/nsalloc_tests.c > +@@ -454,10 +454,15 @@ START_TEST(test_nsalloc_realloc_attributes) { > + nsalloc_teardown(); > + nsalloc_setup(); > + } > ++#if XML_GE == 1 > ++ assert_true( > ++ i == 0); // because expat_realloc relies on expat_malloc to some extent > ++#else > + if (i == 0) > + fail("Parsing worked despite failing reallocations"); > + else if (i == max_realloc_count) > + fail("Parsing failed at max reallocation count"); > ++#endif > + } > + END_TEST > + > +diff --git a/xmlwf/xmlwf.c b/xmlwf/xmlwf.c > +index 8cfc73ca..b9d0a7fc 100644 > +--- a/xmlwf/xmlwf.c > ++++ b/xmlwf/xmlwf.c > +@@ -933,6 +933,8 @@ usage(const XML_Char *prog, int rc) { > + T(" Control verbosity of entity debugging (default: 0)\n") > + T(" EXPAT_ENTROPY_DEBUG=(0|1)\n") > + T(" Control verbosity of entropy debugging (default: 0)\n") > ++ T(" EXPAT_MALLOC_DEBUG=(0|1|2)\n") > ++ T(" Control verbosity of allocation tracker (default: 0)\n") > + T("\n") > + T("exit status:\n") > + T(" 0 the input files are well-formed and the output (if requested) was written successfully\n") > +diff --git a/xmlwf/xmlwf_helpgen.py b/xmlwf/xmlwf_helpgen.py > +index 39a3dc13..2360820d 100755 > +--- a/xmlwf/xmlwf_helpgen.py > ++++ b/xmlwf/xmlwf_helpgen.py > +@@ -39,6 +39,8 @@ environment variables: > + Control verbosity of entity debugging (default: 0) > + EXPAT_ENTROPY_DEBUG=(0|1) > + Control verbosity of entropy debugging (default: 0) > ++ EXPAT_MALLOC_DEBUG=(0|1|2) > ++ Control verbosity of allocation tracker (default: 0) > + > + exit status: > + 0 the input files are well-formed and the output (if requested) was written successfully > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-09.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-09.patch > new file mode 100644 > index 00000000000..364c28183ac > --- /dev/null > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-09.patch > @@ -0,0 +1,43 @@ > +From 1270e5bc0836d296ac4970fc9e1cf53d83972083 Mon Sep 17 00:00:00 2001 > +From: Sebastian Pipping <sebastian@pipping.org> > +Date: Sun, 7 Sep 2025 12:18:08 +0200 > +Subject: [PATCH] lib: Make XML_MemFree and XML_FreeContentModel match their > + siblings > + > +.. XML_MemMalloc and XML_MemRealloc in structure, prior to upcoming changes > + > +CVE: CVE-2025-59375 > +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/1270e5bc0836d296ac4970fc9e1cf53d83972083] > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > +--- > + lib/xmlparse.c | 10 ++++++---- > + 1 file changed, 6 insertions(+), 4 deletions(-) > + > +diff --git a/lib/xmlparse.c b/lib/xmlparse.c > +index fcf1cfdd..5d27cd45 100644 > +--- a/lib/xmlparse.c > ++++ b/lib/xmlparse.c > +@@ -2772,8 +2772,9 @@ XML_GetCurrentColumnNumber(XML_Parser parser) { > + > + void XMLCALL > + XML_FreeContentModel(XML_Parser parser, XML_Content *model) { > +- if (parser != NULL) > +- FREE(parser, model); > ++ if (parser == NULL) > ++ return; > ++ FREE(parser, model); > + } > + > + void *XMLCALL > +@@ -2792,8 +2793,9 @@ XML_MemRealloc(XML_Parser parser, void *ptr, size_t size) { > + > + void XMLCALL > + XML_MemFree(XML_Parser parser, void *ptr) { > +- if (parser != NULL) > +- FREE(parser, ptr); > ++ if (parser == NULL) > ++ return; > ++ FREE(parser, ptr); > + } > + > + void XMLCALL > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-10.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-10.patch > new file mode 100644 > index 00000000000..fe5452000e1 > --- /dev/null > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-10.patch > @@ -0,0 +1,54 @@ > +From 96c7467281c72028aada525c1d3822512758b266 Mon Sep 17 00:00:00 2001 > +From: Sebastian Pipping <sebastian@pipping.org> > +Date: Sun, 7 Sep 2025 12:06:43 +0200 > +Subject: [PATCH] lib: Exclude XML_Mem* functions from allocation tracking > + > +.. so that allocations by the user application > +are not being limited. > + > +CVE: CVE-2025-59375 > +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/96c7467281c72028aada525c1d3822512758b266] > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > +--- > + lib/xmlparse.c | 16 +++++++++++++--- > + 1 file changed, 13 insertions(+), 3 deletions(-) > + > +diff --git a/lib/xmlparse.c b/lib/xmlparse.c > +index 5d27cd45..8145a049 100644 > +--- a/lib/xmlparse.c > ++++ b/lib/xmlparse.c > +@@ -2781,21 +2781,31 @@ void *XMLCALL > + XML_MemMalloc(XML_Parser parser, size_t size) { > + if (parser == NULL) > + return NULL; > +- return MALLOC(parser, size); > ++ > ++ // NOTE: We are avoiding MALLOC(..) here to not include > ++ // user allocations with allocation tracking and limiting. > ++ return parser->m_mem.malloc_fcn(size); > + } > + > + void *XMLCALL > + XML_MemRealloc(XML_Parser parser, void *ptr, size_t size) { > + if (parser == NULL) > + return NULL; > +- return REALLOC(parser, ptr, size); > ++ > ++ // NOTE: We are avoiding REALLOC(..) here to not include > ++ // user allocations with allocation tracking and limiting. > ++ return parser->m_mem.realloc_fcn(ptr, size); > + } > + > + void XMLCALL > + XML_MemFree(XML_Parser parser, void *ptr) { > + if (parser == NULL) > + return; > +- FREE(parser, ptr); > ++ > ++ // NOTE: We are avoiding FREE(..) here because XML_MemMalloc and > ++ // XML_MemRealloc are not using MALLOC(..) and REALLOC(..) > ++ // but plain .malloc_fcn(..) and .realloc_fcn(..), internally. > ++ parser->m_mem.free_fcn(ptr); > + } > + > + void XMLCALL > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-11.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-11.patch > new file mode 100644 > index 00000000000..be892a78046 > --- /dev/null > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-11.patch > @@ -0,0 +1,66 @@ > +From ae4086198d710a62a0a1560007b81307dba72909 Mon Sep 17 00:00:00 2001 > +From: Sebastian Pipping <sebastian@pipping.org> > +Date: Tue, 9 Sep 2025 21:34:28 +0200 > +Subject: [PATCH] lib: Exclude the main input buffer from allocation tracking > + > +.. so that control of the input buffer size remains with the > +application using Expat > + > +CVE: CVE-2025-59375 > +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/ae4086198d710a62a0a1560007b81307dba72909] > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > +--- > + lib/xmlparse.c | 19 +++++++++++++++---- > + 1 file changed, 15 insertions(+), 4 deletions(-) > + > +diff --git a/lib/xmlparse.c b/lib/xmlparse.c > +index 8145a049..00139b94 100644 > +--- a/lib/xmlparse.c > ++++ b/lib/xmlparse.c > +@@ -1975,7 +1975,10 @@ XML_ParserFree(XML_Parser parser) { > + FREE(parser, (void *)parser->m_attInfo); > + #endif > + FREE(parser, parser->m_groupConnector); > +- FREE(parser, parser->m_buffer); > ++ // NOTE: We are avoiding FREE(..) here because parser->m_buffer > ++ // is not being allocated with MALLOC(..) but with plain > ++ // .malloc_fcn(..). > ++ parser->m_mem.free_fcn(parser->m_buffer); > + FREE(parser, parser->m_dataBuf); > + FREE(parser, parser->m_nsAtts); > + FREE(parser, parser->m_unknownEncodingMem); > +@@ -2567,7 +2570,9 @@ XML_GetBuffer(XML_Parser parser, int len) { > + parser->m_errorCode = XML_ERROR_NO_MEMORY; > + return NULL; > + } > +- newBuf = (char *)MALLOC(parser, bufferSize); > ++ // NOTE: We are avoiding MALLOC(..) here to leave limiting > ++ // the input size to the application using Expat. > ++ newBuf = (char *)parser->m_mem.malloc_fcn(bufferSize); > + if (newBuf == 0) { > + parser->m_errorCode = XML_ERROR_NO_MEMORY; > + return NULL; > +@@ -2578,7 +2583,10 @@ XML_GetBuffer(XML_Parser parser, int len) { > + memcpy(newBuf, &parser->m_bufferPtr[-keep], > + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr) > + + keep); > +- FREE(parser, parser->m_buffer); > ++ // NOTE: We are avoiding FREE(..) here because parser->m_buffer > ++ // is not being allocated with MALLOC(..) but with plain > ++ // .malloc_fcn(..). > ++ parser->m_mem.free_fcn(parser->m_buffer); > + parser->m_buffer = newBuf; > + parser->m_bufferEnd > + = parser->m_buffer > +@@ -2594,7 +2602,10 @@ XML_GetBuffer(XML_Parser parser, int len) { > + if (parser->m_bufferPtr) { > + memcpy(newBuf, parser->m_bufferPtr, > + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr)); > +- FREE(parser, parser->m_buffer); > ++ // NOTE: We are avoiding FREE(..) here because parser->m_buffer > ++ // is not being allocated with MALLOC(..) but with plain > ++ // .malloc_fcn(..). > ++ parser->m_mem.free_fcn(parser->m_buffer); > + parser->m_bufferEnd > + = newBuf > + + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr); > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-12.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-12.patch > new file mode 100644 > index 00000000000..9e036a52849 > --- /dev/null > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-12.patch > @@ -0,0 +1,58 @@ > +From 7e35240dc97e9fd4f609e31f27c27b659535e436 Mon Sep 17 00:00:00 2001 > +From: Sebastian Pipping <sebastian@pipping.org> > +Date: Thu, 11 Sep 2025 00:27:05 +0200 > +Subject: [PATCH] lib: Exclude the content model from allocation tracking > + > +.. so that applications that are not using XML_FreeContentModel > +but plain free(..) or .free_fcn() to free the content model's > +memory are safe > + > +CVE: CVE-2025-59375 > +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/7e35240dc97e9fd4f609e31f27c27b659535e436] > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > +--- > + lib/xmlparse.c | 16 +++++++++++++--- > + 1 file changed, 13 insertions(+), 3 deletions(-) > + > +diff --git a/lib/xmlparse.c b/lib/xmlparse.c > +index 00139b94..d0b6e0cd 100644 > +--- a/lib/xmlparse.c > ++++ b/lib/xmlparse.c > +@@ -2785,7 +2785,10 @@ void XMLCALL > + XML_FreeContentModel(XML_Parser parser, XML_Content *model) { > + if (parser == NULL) > + return; > +- FREE(parser, model); > ++ > ++ // NOTE: We are avoiding FREE(..) here because the content model > ++ // has been created using plain .malloc_fcn(..) rather than MALLOC(..). > ++ parser->m_mem.free_fcn(model); > + } > + > + void *XMLCALL > +@@ -6063,8 +6066,12 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, > + case XML_ROLE_CONTENT_EMPTY: > + if (dtd->in_eldecl) { > + if (parser->m_elementDeclHandler) { > ++ // NOTE: We are avoiding MALLOC(..) here to so that > ++ // applications that are not using XML_FreeContentModel but > ++ // plain free(..) or .free_fcn() to free the content model's > ++ // memory are safe. > + XML_Content *content > +- = (XML_Content *)MALLOC(parser, sizeof(XML_Content)); > ++ = (XML_Content *)parser->m_mem.malloc_fcn(sizeof(XML_Content)); > + if (! content) > + return XML_ERROR_NO_MEMORY; > + content->quant = XML_CQUANT_NONE; > +@@ -8278,7 +8285,10 @@ build_model(XML_Parser parser) { > + const size_t allocsize = (dtd->scaffCount * sizeof(XML_Content) > + + (dtd->contentStringLen * sizeof(XML_Char))); > + > +- ret = (XML_Content *)MALLOC(parser, allocsize); > ++ // NOTE: We are avoiding MALLOC(..) here to so that > ++ // applications that are not using XML_FreeContentModel but plain > ++ // free(..) or .free_fcn() to free the content model's memory are safe. > ++ ret = (XML_Content *)parser->m_mem.malloc_fcn(allocsize); > + if (! ret) > + return NULL; > + > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-13.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-13.patch > new file mode 100644 > index 00000000000..209dd83a4b6 > --- /dev/null > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-13.patch > @@ -0,0 +1,309 @@ > +From 31f9053c3c46741f4daf2ea2bdea75f40f720d42 Mon Sep 17 00:00:00 2001 > +From: Sebastian Pipping <sebastian@pipping.org> > +Date: Tue, 2 Sep 2025 22:36:49 +0200 > +Subject: [PATCH] tests: Cover allocation tracking and limiting with tests > + > +CVE: CVE-2025-59375 > +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/31f9053c3c46741f4daf2ea2bdea75f40f720d42] > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > +--- > + lib/internal.h | 3 + > + lib/xmlparse.c | 12 +++ > + tests/alloc_tests.c | 214 ++++++++++++++++++++++++++++++++++++++++++++ > + 3 files changed, 229 insertions(+) > + > +diff --git a/lib/internal.h b/lib/internal.h > +index eb67cf50..6e087858 100644 > +--- a/lib/internal.h > ++++ b/lib/internal.h > +@@ -173,6 +173,9 @@ extern > + #endif > + XML_Bool g_reparseDeferralEnabledDefault; // written ONLY in runtests.c > + #if defined(XML_TESTING) > ++void *expat_malloc(XML_Parser parser, size_t size, int sourceLine); > ++void expat_free(XML_Parser parser, void *ptr, int sourceLine); > ++void *expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine); > + extern unsigned int g_bytesScanned; // used for testing only > + #endif > + > +diff --git a/lib/xmlparse.c b/lib/xmlparse.c > +index d0b6e0cd..6e9c6fb2 100644 > +--- a/lib/xmlparse.c > ++++ b/lib/xmlparse.c > +@@ -843,7 +843,11 @@ expat_heap_increase_tolerable(XML_Parser rootParser, XmlBigCount increase, > + return tolerable; > + } > + > ++# if defined(XML_TESTING) > ++void * > ++# else > + static void * > ++# endif > + expat_malloc(XML_Parser parser, size_t size, int sourceLine) { > + // Detect integer overflow > + if (SIZE_MAX - size < sizeof(size_t)) { > +@@ -893,7 +897,11 @@ expat_malloc(XML_Parser parser, size_t size, int sourceLine) { > + return (char *)mallocedPtr + sizeof(size_t); > + } > + > ++# if defined(XML_TESTING) > ++void > ++# else > + static void > ++# endif > + expat_free(XML_Parser parser, void *ptr, int sourceLine) { > + assert(parser != NULL); > + > +@@ -924,7 +932,11 @@ expat_free(XML_Parser parser, void *ptr, int sourceLine) { > + parser->m_mem.free_fcn(mallocedPtr); > + } > + > ++# if defined(XML_TESTING) > ++void * > ++# else > + static void * > ++# endif > + expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine) { > + assert(parser != NULL); > + > +diff --git a/tests/alloc_tests.c b/tests/alloc_tests.c > +index 4c3e2af4..275f92d5 100644 > +--- a/tests/alloc_tests.c > ++++ b/tests/alloc_tests.c > +@@ -46,10 +46,16 @@ > + # undef NDEBUG /* because test suite relies on assert(...) at the moment */ > + #endif > + > ++#include <math.h> /* NAN, INFINITY */ > ++#include <stdbool.h> > ++#include <stdint.h> /* for SIZE_MAX */ > + #include <string.h> > + #include <assert.h> > + > ++#include "expat_config.h" > ++ > + #include "expat.h" > ++#include "internal.h" > + #include "common.h" > + #include "minicheck.h" > + #include "dummy.h" > +@@ -2085,6 +2091,203 @@ START_TEST(test_alloc_reset_after_external_entity_parser_create_fail) { > + } > + END_TEST > + > ++START_TEST(test_alloc_tracker_size_recorded) { > ++ XML_Memory_Handling_Suite memsuite = {malloc, realloc, free}; > ++ > ++ bool values[] = {true, false}; > ++ for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++) { > ++ const bool useMemSuite = values[i]; > ++ set_subtest("useMemSuite=%d", (int)useMemSuite); > ++ XML_Parser parser = useMemSuite > ++ ? XML_ParserCreate_MM(NULL, &memsuite, XCS("|")) > ++ : XML_ParserCreate(NULL); > ++ > ++#if XML_GE == 1 > ++ void *ptr = expat_malloc(parser, 10, -1); > ++ > ++ assert_true(ptr != NULL); > ++ assert_true(*((size_t *)ptr - 1) == 10); > ++ > ++ assert_true(expat_realloc(parser, ptr, SIZE_MAX / 2, -1) == NULL); > ++ > ++ assert_true(*((size_t *)ptr - 1) == 10); // i.e. unchanged > ++ > ++ ptr = expat_realloc(parser, ptr, 20, -1); > ++ > ++ assert_true(ptr != NULL); > ++ assert_true(*((size_t *)ptr - 1) == 20); > ++ > ++ expat_free(parser, ptr, -1); > ++#endif > ++ > ++ XML_ParserFree(parser); > ++ } > ++} > ++END_TEST > ++ > ++START_TEST(test_alloc_tracker_maximum_amplification) { > ++ if (g_reparseDeferralEnabledDefault == XML_TRUE) { > ++ return; > ++ } > ++ > ++ XML_Parser parser = XML_ParserCreate(NULL); > ++ > ++ // Get .m_accounting.countBytesDirect from 0 to 3 > ++ const char *const chunk = "<e>"; > ++ assert_true(_XML_Parse_SINGLE_BYTES(parser, chunk, (int)strlen(chunk), > ++ /*isFinal=*/XML_FALSE) > ++ == XML_STATUS_OK); > ++ > ++#if XML_GE == 1 > ++ // Stop activation threshold from interfering > ++ assert_true(XML_SetAllocTrackerActivationThreshold(parser, 0) == XML_TRUE); > ++ > ++ // Exceed maximum amplification: should be rejected. > ++ assert_true(expat_malloc(parser, 1000, -1) == NULL); > ++ > ++ // Increase maximum amplification, and try the same amount once more: should > ++ // work. > ++ assert_true(XML_SetAllocTrackerMaximumAmplification(parser, 3000.0f) > ++ == XML_TRUE); > ++ > ++ void *const ptr = expat_malloc(parser, 1000, -1); > ++ assert_true(ptr != NULL); > ++ expat_free(parser, ptr, -1); > ++#endif > ++ > ++ XML_ParserFree(parser); > ++} > ++END_TEST > ++ > ++START_TEST(test_alloc_tracker_threshold) { > ++ XML_Parser parser = XML_ParserCreate(NULL); > ++ > ++#if XML_GE == 1 > ++ // Exceed maximum amplification *before* (default) threshold: should work. > ++ void *const ptr = expat_malloc(parser, 1000, -1); > ++ assert_true(ptr != NULL); > ++ expat_free(parser, ptr, -1); > ++ > ++ // Exceed maximum amplification *after* threshold: should be rejected. > ++ assert_true(XML_SetAllocTrackerActivationThreshold(parser, 999) == XML_TRUE); > ++ assert_true(expat_malloc(parser, 1000, -1) == NULL); > ++#endif > ++ > ++ XML_ParserFree(parser); > ++} > ++END_TEST > ++ > ++START_TEST(test_alloc_tracker_getbuffer_unlimited) { > ++ XML_Parser parser = XML_ParserCreate(NULL); > ++ > ++#if XML_GE == 1 > ++ // Artificially lower threshold > ++ assert_true(XML_SetAllocTrackerActivationThreshold(parser, 0) == XML_TRUE); > ++ > ++ // Self-test: Prove that threshold is as rejecting as expected > ++ assert_true(expat_malloc(parser, 1000, -1) == NULL); > ++#endif > ++ // XML_GetBuffer should be allowed to pass, though > ++ assert_true(XML_GetBuffer(parser, 1000) != NULL); > ++ > ++ XML_ParserFree(parser); > ++} > ++END_TEST > ++ > ++START_TEST(test_alloc_tracker_api) { > ++ XML_Parser parserWithoutParent = XML_ParserCreate(NULL); > ++ XML_Parser parserWithParent = XML_ExternalEntityParserCreate( > ++ parserWithoutParent, XCS("entity123"), NULL); > ++ if (parserWithoutParent == NULL) > ++ fail("parserWithoutParent is NULL"); > ++ if (parserWithParent == NULL) > ++ fail("parserWithParent is NULL"); > ++ > ++#if XML_GE == 1 > ++ // XML_SetAllocTrackerMaximumAmplification, error cases > ++ if (XML_SetAllocTrackerMaximumAmplification(NULL, 123.0f) == XML_TRUE) > ++ fail("Call with NULL parser is NOT supposed to succeed"); > ++ if (XML_SetAllocTrackerMaximumAmplification(parserWithParent, 123.0f) > ++ == XML_TRUE) > ++ fail("Call with non-root parser is NOT supposed to succeed"); > ++ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, NAN) > ++ == XML_TRUE) > ++ fail("Call with NaN limit is NOT supposed to succeed"); > ++ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, -1.0f) > ++ == XML_TRUE) > ++ fail("Call with negative limit is NOT supposed to succeed"); > ++ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, 0.9f) > ++ == XML_TRUE) > ++ fail("Call with positive limit <1.0 is NOT supposed to succeed"); > ++ > ++ // XML_SetAllocTrackerMaximumAmplification, success cases > ++ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, 1.0f) > ++ == XML_FALSE) > ++ fail("Call with positive limit >=1.0 is supposed to succeed"); > ++ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, 123456.789f) > ++ == XML_FALSE) > ++ fail("Call with positive limit >=1.0 is supposed to succeed"); > ++ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, INFINITY) > ++ == XML_FALSE) > ++ fail("Call with positive limit >=1.0 is supposed to succeed"); > ++ > ++ // XML_SetAllocTrackerActivationThreshold, error cases > ++ if (XML_SetAllocTrackerActivationThreshold(NULL, 123) == XML_TRUE) > ++ fail("Call with NULL parser is NOT supposed to succeed"); > ++ if (XML_SetAllocTrackerActivationThreshold(parserWithParent, 123) == XML_TRUE) > ++ fail("Call with non-root parser is NOT supposed to succeed"); > ++ > ++ // XML_SetAllocTrackerActivationThreshold, success cases > ++ if (XML_SetAllocTrackerActivationThreshold(parserWithoutParent, 123) > ++ == XML_FALSE) > ++ fail("Call with non-NULL parentless parser is supposed to succeed"); > ++#endif // XML_GE == 1 > ++ > ++ XML_ParserFree(parserWithParent); > ++ XML_ParserFree(parserWithoutParent); > ++} > ++END_TEST > ++ > ++START_TEST(test_mem_api_cycle) { > ++ XML_Parser parser = XML_ParserCreate(NULL); > ++ > ++ void *ptr = XML_MemMalloc(parser, 10); > ++ > ++ assert_true(ptr != NULL); > ++ memset(ptr, 'x', 10); // assert writability, with ASan in mind > ++ > ++ ptr = XML_MemRealloc(parser, ptr, 20); > ++ > ++ assert_true(ptr != NULL); > ++ memset(ptr, 'y', 20); // assert writability, with ASan in mind > ++ > ++ XML_MemFree(parser, ptr); > ++ > ++ XML_ParserFree(parser); > ++} > ++END_TEST > ++ > ++START_TEST(test_mem_api_unlimited) { > ++ XML_Parser parser = XML_ParserCreate(NULL); > ++ > ++#if XML_GE == 1 > ++ assert_true(XML_SetAllocTrackerActivationThreshold(parser, 0) == XML_TRUE); > ++#endif > ++ > ++ void *ptr = XML_MemMalloc(parser, 1000); > ++ > ++ assert_true(ptr != NULL); > ++ > ++ ptr = XML_MemRealloc(parser, ptr, 2000); > ++ > ++ assert_true(ptr != NULL); > ++ > ++ XML_MemFree(parser, ptr); > ++ > ++ XML_ParserFree(parser); > ++} > ++END_TEST > ++ > + void > + make_alloc_test_case(Suite *s) { > + TCase *tc_alloc = tcase_create("allocation tests"); > +@@ -2151,4 +2354,15 @@ make_alloc_test_case(Suite *s) { > + > + tcase_add_test__ifdef_xml_dtd( > + tc_alloc, test_alloc_reset_after_external_entity_parser_create_fail); > ++ > ++ tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_tracker_size_recorded); > ++ tcase_add_test__ifdef_xml_dtd(tc_alloc, > ++ test_alloc_tracker_maximum_amplification); > ++ tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_tracker_threshold); > ++ tcase_add_test__ifdef_xml_dtd(tc_alloc, > ++ test_alloc_tracker_getbuffer_unlimited); > ++ tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_tracker_api); > ++ > ++ tcase_add_test(tc_alloc, test_mem_api_cycle); > ++ tcase_add_test__ifdef_xml_dtd(tc_alloc, test_mem_api_unlimited); > + } > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-14.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-14.patch > new file mode 100644 > index 00000000000..a339cc3f4b2 > --- /dev/null > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-14.patch > @@ -0,0 +1,122 @@ > +From 78366891a586f293aeff60a14a55e4afe1169586 Mon Sep 17 00:00:00 2001 > +From: Sebastian Pipping <sebastian@pipping.org> > +Date: Tue, 2 Sep 2025 16:44:00 +0200 > +Subject: [PATCH] xmlwf: Wire allocation tracker config to existing arguments > + -a and -b > + > +CVE: CVE-2025-59375 > +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/78366891a586f293aeff60a14a55e4afe1169586] > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > +--- > + doc/xmlwf.xml | 26 ++++++++++++++++++++------ > + xmlwf/xmlwf.c | 7 +++++-- > + xmlwf/xmlwf_helpgen.py | 4 ++-- > + 3 files changed, 27 insertions(+), 10 deletions(-) > + > +diff --git a/doc/xmlwf.xml b/doc/xmlwf.xml > +index 17e9cf51..65d8ae9b 100644 > +--- a/doc/xmlwf.xml > ++++ b/doc/xmlwf.xml > +@@ -158,19 +158,31 @@ supports both. > + <listitem> > + <para> > + Sets the maximum tolerated amplification factor > +- for protection against billion laughs attacks (default: 100.0). > ++ for protection against amplification attacks > ++ like the billion laughs attack > ++ (default: 100.0 > ++ for the sum of direct and indirect output and also > ++ for allocations of dynamic memory). > + The amplification factor is calculated as .. > + </para> > + <literallayout> > + amplification := (direct + indirect) / direct > + </literallayout> > + <para> > +- .. while parsing, whereas > ++ .. with regard to use of entities and .. > ++ </para> > ++ <literallayout> > ++ amplification := allocated / direct > ++ </literallayout> > ++ <para> > ++ .. with regard to dynamic memory while parsing. > + <direct> is the number of bytes read > +- from the primary document in parsing and > ++ from the primary document in parsing, > + <indirect> is the number of bytes > + added by expanding entities and reading of external DTD files, > +- combined. > ++ combined, and > ++ <allocated> is the total number of bytes of dynamic memory > ++ allocated (and not freed) per hierarchy of parsers. > + </para> > + <para> > + <emphasis>NOTE</emphasis>: > +@@ -185,8 +197,10 @@ supports both. > + <listitem> > + <para> > + Sets the number of output bytes (including amplification) > +- needed to activate protection against billion laughs attacks > +- (default: 8 MiB). > ++ needed to activate protection against amplification attacks > ++ like billion laughs > ++ (default: 8 MiB for the sum of direct and indirect output, > ++ and 64 MiB for allocations of dynamic memory). > + This can be thought of as an "activation threshold". > + </para> > + <para> > +diff --git a/xmlwf/xmlwf.c b/xmlwf/xmlwf.c > +index b9d0a7fc..14206d9e 100644 > +--- a/xmlwf/xmlwf.c > ++++ b/xmlwf/xmlwf.c > +@@ -913,11 +913,11 @@ usage(const XML_Char *prog, int rc) { > + T(" -t write no XML output for [t]iming of plain parsing\n") > + T(" -N enable adding doctype and [n]otation declarations\n") > + T("\n") > +- T("billion laughs attack protection:\n") > ++ T("amplification attack protection (e.g. billion laughs):\n") > + T(" NOTE: If you ever need to increase these values for non-attack payload, please file a bug report.\n") > + T("\n") > + T(" -a FACTOR set maximum tolerated [a]mplification factor (default: 100.0)\n") > +- T(" -b BYTES set number of output [b]ytes needed to activate (default: 8 MiB)\n") > ++ T(" -b BYTES set number of output [b]ytes needed to activate (default: 8 MiB/64 MiB)\n") > + T("\n") > + T("reparse deferral:\n") > + T(" -q disable reparse deferral, and allow [q]uadratic parse runtime with large tokens\n") > +@@ -1181,12 +1181,15 @@ tmain(int argc, XML_Char **argv) { > + #if XML_GE == 1 > + XML_SetBillionLaughsAttackProtectionMaximumAmplification( > + parser, attackMaximumAmplification); > ++ XML_SetAllocTrackerMaximumAmplification(parser, > ++ attackMaximumAmplification); > + #endif > + } > + if (attackThresholdGiven) { > + #if XML_GE == 1 > + XML_SetBillionLaughsAttackProtectionActivationThreshold( > + parser, attackThresholdBytes); > ++ XML_SetAllocTrackerActivationThreshold(parser, attackThresholdBytes); > + #else > + (void)attackThresholdBytes; // silence -Wunused-but-set-variable > + #endif > +diff --git a/xmlwf/xmlwf_helpgen.py b/xmlwf/xmlwf_helpgen.py > +index 2360820d..e91c285c 100755 > +--- a/xmlwf/xmlwf_helpgen.py > ++++ b/xmlwf/xmlwf_helpgen.py > +@@ -84,13 +84,13 @@ output_mode.add_argument('-m', action='store_true', help='write [m]eta XML, not > + output_mode.add_argument('-t', action='store_true', help='write no XML output for [t]iming of plain parsing') > + output_related.add_argument('-N', action='store_true', help='enable adding doctype and [n]otation declarations') > + > +-billion_laughs = parser.add_argument_group('billion laughs attack protection', > ++billion_laughs = parser.add_argument_group('amplification attack protection (e.g. billion laughs)', > + description='NOTE: ' > + 'If you ever need to increase these values ' > + 'for non-attack payload, please file a bug report.') > + billion_laughs.add_argument('-a', metavar='FACTOR', > + help='set maximum tolerated [a]mplification factor (default: 100.0)') > +-billion_laughs.add_argument('-b', metavar='BYTES', help='set number of output [b]ytes needed to activate (default: 8 MiB)') > ++billion_laughs.add_argument('-b', metavar='BYTES', help='set number of output [b]ytes needed to activate (default: 8 MiB/64 MiB)') > + > + reparse_deferral = parser.add_argument_group('reparse deferral') > + reparse_deferral.add_argument('-q', metavar='FACTOR', > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-15.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-15.patch > new file mode 100644 > index 00000000000..8d068441928 > --- /dev/null > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-15.patch > @@ -0,0 +1,70 @@ > +From 5ae51be57ed0ca1e87582881d07ea9c29c4f7c05 Mon Sep 17 00:00:00 2001 > +From: Sebastian Pipping <sebastian@pipping.org> > +Date: Wed, 3 Sep 2025 17:06:41 +0200 > +Subject: [PATCH] fuzz: Be robust towards NULL return from > + XML_ExternalEntityParserCreate > + > +CVE: CVE-2025-59375 > +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/5ae51be57ed0ca1e87582881d07ea9c29c4f7c05] > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > +--- > + fuzz/xml_parse_fuzzer.c | 14 ++++++++------ > + fuzz/xml_parsebuffer_fuzzer.c | 14 ++++++++------ > + 2 files changed, 16 insertions(+), 12 deletions(-) > + > +diff --git a/fuzz/xml_parse_fuzzer.c b/fuzz/xml_parse_fuzzer.c > +index 90c38549..29ab33ff 100644 > +--- a/fuzz/xml_parse_fuzzer.c > ++++ b/fuzz/xml_parse_fuzzer.c > +@@ -89,15 +89,17 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { > + > + XML_Parser externalEntityParser > + = XML_ExternalEntityParserCreate(parentParser, "e1", NULL); > +- assert(externalEntityParser); > +- ParseOneInput(externalEntityParser, data, size); > +- XML_ParserFree(externalEntityParser); > ++ if (externalEntityParser != NULL) { > ++ ParseOneInput(externalEntityParser, data, size); > ++ XML_ParserFree(externalEntityParser); > ++ } > + > + XML_Parser externalDtdParser > + = XML_ExternalEntityParserCreate(parentParser, NULL, NULL); > +- assert(externalDtdParser); > +- ParseOneInput(externalDtdParser, data, size); > +- XML_ParserFree(externalDtdParser); > ++ if (externalDtdParser != NULL) { > ++ ParseOneInput(externalDtdParser, data, size); > ++ XML_ParserFree(externalDtdParser); > ++ } > + > + // finally frees this parser which served as parent > + XML_ParserFree(parentParser); > +diff --git a/fuzz/xml_parsebuffer_fuzzer.c b/fuzz/xml_parsebuffer_fuzzer.c > +index 0db67dce..38b9981b 100644 > +--- a/fuzz/xml_parsebuffer_fuzzer.c > ++++ b/fuzz/xml_parsebuffer_fuzzer.c > +@@ -101,15 +101,17 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { > + > + XML_Parser externalEntityParser > + = XML_ExternalEntityParserCreate(parentParser, "e1", NULL); > +- assert(externalEntityParser); > +- ParseOneInput(externalEntityParser, data, size); > +- XML_ParserFree(externalEntityParser); > ++ if (externalEntityParser != NULL) { > ++ ParseOneInput(externalEntityParser, data, size); > ++ XML_ParserFree(externalEntityParser); > ++ } > + > + XML_Parser externalDtdParser > + = XML_ExternalEntityParserCreate(parentParser, NULL, NULL); > +- assert(externalDtdParser); > +- ParseOneInput(externalDtdParser, data, size); > +- XML_ParserFree(externalDtdParser); > ++ if (externalDtdParser != NULL) { > ++ ParseOneInput(externalDtdParser, data, size); > ++ XML_ParserFree(externalDtdParser); > ++ } > + > + // finally frees this parser which served as parent > + XML_ParserFree(parentParser); > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-16.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-16.patch > new file mode 100644 > index 00000000000..a276347d83d > --- /dev/null > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-16.patch > @@ -0,0 +1,146 @@ > +From d6246c31a1238d065b4d9690d3bac740326f6485 Mon Sep 17 00:00:00 2001 > +From: Sebastian Pipping <sebastian@pipping.org> > +Date: Wed, 3 Sep 2025 01:28:03 +0200 > +Subject: [PATCH] docs: Document the two allocation tracking API functions > + > +CVE: CVE-2025-59375 > +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/d6246c31a1238d065b4d9690d3bac740326f6485] > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > +--- > + doc/reference.html | 116 +++++++++++++++++++++++++++++++++++++++++++++ > + 1 file changed, 116 insertions(+) > + > +diff --git a/doc/reference.html b/doc/reference.html > +index 89476710..81da4e6c 100644 > +--- a/doc/reference.html > ++++ b/doc/reference.html > +@@ -157,6 +157,8 @@ interface.</p> > + <ul> > + <li><a href="#XML_SetBillionLaughsAttackProtectionMaximumAmplification">XML_SetBillionLaughsAttackProtectionMaximumAmplification</a></li> > + <li><a href="#XML_SetBillionLaughsAttackProtectionActivationThreshold">XML_SetBillionLaughsAttackProtectionActivationThreshold</a></li> > ++ <li><a href="#XML_SetAllocTrackerMaximumAmplification">XML_SetAllocTrackerMaximumAmplification</a></li> > ++ <li><a href="#XML_SetAllocTrackerActivationThreshold">XML_SetAllocTrackerActivationThreshold</a></li> > + <li><a href="#XML_SetReparseDeferralEnabled">XML_SetReparseDeferralEnabled</a></li> > + </ul> > + </li> > +@@ -2262,6 +2264,120 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold(XML_Parser p, > + </p> > + </div> > + > ++<h4 id="XML_SetAllocTrackerMaximumAmplification">XML_SetAllocTrackerMaximumAmplification</h4> > ++<pre class="fcndec"> > ++/* Added in Expat 2.7.2. */ > ++XML_Bool > ++XML_SetAllocTrackerMaximumAmplification(XML_Parser p, > ++ float maximumAmplificationFactor); > ++</pre> > ++<div class="fcndef"> > ++ <p> > ++ Sets the maximum tolerated amplification factor > ++ between direct input and bytes of dynamic memory allocated > ++ (default: <code>100.0</code>) > ++ of parser <code>p</code> to <code>maximumAmplificationFactor</code>, and > ++ returns <code>XML_TRUE</code> upon success and <code>XML_FALSE</code> upon error. > ++ </p> > ++ > ++ <p> > ++ <strong>Note:</strong> > ++ There are three types of allocations that intentionally bypass tracking and limiting: > ++ </p> > ++ <ul> > ++ <li> > ++ application calls to functions > ++ <code><a href="#XML_MemMalloc">XML_MemMalloc</a></code> > ++ and > ++ <code><a href="#XML_MemRealloc">XML_MemRealloc</a></code> > ++ — > ++ <em>healthy</em> use of these two functions continues to be a responsibility > ++ of the application using Expat > ++ —, > ++ </li> > ++ <li> > ++ the main character buffer used by functions > ++ <code><a href="#XML_GetBuffer">XML_GetBuffer</a></code> > ++ and > ++ <code><a href="#XML_ParseBuffer">XML_ParseBuffer</a></code> > ++ (and thus also by plain > ++ <code><a href="#XML_Parse">XML_Parse</a></code>), and > ++ </li> > ++ <li> > ++ the <a href="#XML_SetElementDeclHandler">content model memory</a> > ++ (that is passed to the > ++ <a href="#XML_SetElementDeclHandler">element declaration handler</a> > ++ and freed by a call to > ++ <code><a href="#XML_FreeContentModel">XML_FreeContentModel</a></code>). > ++ </li> > ++ </ul> > ++ > ++ <p>The amplification factor is calculated as ..</p> > ++ <pre>amplification := allocated / direct</pre> > ++ <p> > ++ .. while parsing, whereas > ++ <code>direct</code> is the number of bytes read from the primary document in parsing and > ++ <code>allocated</code> is the number of bytes of dynamic memory allocated in the parser hierarchy. > ++ </p> > ++ > ++ <p>For a call to <code>XML_SetAllocTrackerMaximumAmplification</code> to succeed:</p> > ++ <ul> > ++ <li>parser <code>p</code> must be a non-<code>NULL</code> root parser (without any parent parsers) and</li> > ++ <li><code>maximumAmplificationFactor</code> must be non-<code>NaN</code> and greater than or equal to <code>1.0</code>.</li> > ++ </ul> > ++ > ++ <p> > ++ <strong>Note:</strong> > ++ If you ever need to increase this value for non-attack payload, > ++ please <a href="https://github.com/libexpat/libexpat/issues">file a bug report</a>. > ++ </p> > ++ > ++ <p> > ++ <strong>Note:</strong> > ++ Amplifications factors greater than 100 can been observed near the start of parsing > ++ even with benign files in practice. > ++ > ++ So if you do reduce the maximum allowed amplification, > ++ please make sure that the activation threshold is still big enough > ++ to not end up with undesired false positives (i.e. benign files being rejected). > ++ </p> > ++</div> > ++ > ++<h4 id="XML_SetAllocTrackerActivationThreshold">XML_SetAllocTrackerActivationThreshold</h4> > ++<pre class="fcndec"> > ++/* Added in Expat 2.7.2. */ > ++XML_Bool > ++XML_SetAllocTrackerActivationThreshold(XML_Parser p, > ++ unsigned long long activationThresholdBytes); > ++</pre> > ++<div class="fcndef"> > ++ <p> > ++ Sets number of allocated bytes of dynamic memory > ++ needed to activate protection against disproportionate use of RAM > ++ (default: <code>64 MiB</code>) > ++ of parser <code>p</code> to <code>activationThresholdBytes</code>, and > ++ returns <code>XML_TRUE</code> upon success and <code>XML_FALSE</code> upon error. > ++ </p> > ++ > ++ <p> > ++ <strong>Note:</strong> > ++ For types of allocations that intentionally bypass tracking and limiting, please see > ++ <code><a href="#XML_SetAllocTrackerMaximumAmplification">XML_SetAllocTrackerMaximumAmplification</a></code> > ++ above. > ++ </p> > ++ > ++ <p>For a call to <code>XML_SetAllocTrackerActivationThreshold</code> to succeed:</p> > ++ <ul> > ++ <li>parser <code>p</code> must be a non-<code>NULL</code> root parser (without any parent parsers).</li> > ++ </ul> > ++ > ++ <p> > ++ <strong>Note:</strong> > ++ If you ever need to increase this value for non-attack payload, > ++ please <a href="https://github.com/libexpat/libexpat/issues">file a bug report</a>. > ++ </p> > ++</div> > ++ > + <h4 id="XML_SetReparseDeferralEnabled">XML_SetReparseDeferralEnabled</h4> > + <pre class="fcndec"> > + /* Added in Expat 2.6.0. */ > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-17.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-17.patch > new file mode 100644 > index 00000000000..ca0e3a34f73 > --- /dev/null > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-17.patch > @@ -0,0 +1,28 @@ > +From a6a2a49367f03f5d8a73c9027b45b59953ca27d8 Mon Sep 17 00:00:00 2001 > +From: Sebastian Pipping <sebastian@pipping.org> > +Date: Wed, 10 Sep 2025 19:52:39 +0200 > +Subject: [PATCH] docs: Promote the contract to call XML_FreeContentModel > + > +.. when registering a custom element declaration handler > +(via a call to function XML_SetElementDeclHandler) > + > +CVE: CVE-2025-59375 > +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/a6a2a49367f03f5d8a73c9027b45b59953ca27d8] > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > +--- > + doc/reference.html | 2 +- > + 1 file changed, 1 insertion(+), 1 deletion(-) > + > +diff --git a/doc/reference.html b/doc/reference.html > +index 81da4e6c..564fc1b2 100644 > +--- a/doc/reference.html > ++++ b/doc/reference.html > +@@ -1902,7 +1902,7 @@ struct XML_cp { > + <p>Sets a handler for element declarations in a DTD. The handler gets > + called with the name of the element in the declaration and a pointer > + to a structure that contains the element model. It's the user code's > +-responsibility to free model when finished with it. See <code> > ++responsibility to free model when finished with via a call to <code> > + <a href="#XML_FreeContentModel">XML_FreeContentModel</a></code>. > + There is no need to free the model from the handler, it can be kept > + around and freed at a later stage.</p> > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-18.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-18.patch > new file mode 100644 > index 00000000000..c29b3018254 > --- /dev/null > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-18.patch > @@ -0,0 +1,74 @@ > +From a21a3a8299e1ee0b0ae5ae2886a0746d088cf135 Mon Sep 17 00:00:00 2001 > +From: Sebastian Pipping <sebastian@pipping.org> > +Date: Sun, 7 Sep 2025 16:00:35 +0200 > +Subject: [PATCH] Changes: Document allocation tracking > + > +CVE: CVE-2025-59375 > +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/a21a3a8299e1ee0b0ae5ae2886a0746d088cf135] > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > +--- > + Changes | 37 +++++++++++++++++++++++++++++++++++++ > + 1 file changed, 37 insertions(+) > + > +diff --git a/Changes b/Changes > +index cb752151..ceb5c5dc 100644 > +--- a/Changes > ++++ b/Changes > +@@ -30,6 +30,36 @@ > + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! > + > + Patches: > ++ Security fixes: > ++ #1018 #1034 CVE-2025-59375 -- Disallow use of disproportional amounts of > ++ dynamic memory from within an Expat parser (e.g. previously > ++ a ~250 KiB sized document was able to cause allocation of > ++ ~800 MiB from the heap, i.e. an "amplification" of factor > ++ ~3,300); once a threshold (that defaults to 64 MiB) is > ++ reached, a maximum amplification factor (that defaults to > ++ 100.0) is enforced, and violating documents are rejected > ++ with an out-of-memory error. > ++ There are two new API functions to fine-tune this new > ++ behavior: > ++ - XML_SetAllocTrackerActivationThreshold > ++ - XML_SetAllocTrackerMaximumAmplification . > ++ If you ever need to increase these defaults for non-attack > ++ XML payload, please file a bug report with libexpat. > ++ There is also a new environment variable > ++ EXPAT_MALLOC_DEBUG=(0|1|2) to control the verbosity > ++ of allocations debugging at runtime, disabled by default. > ++ Known impact is (reliable and easy) denial of service: > ++ CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H/E:H/RL:O/RC:C > ++ (Base Score: 7.5, Temporal Score: 7.2) > ++ Please note that a layer of compression around XML can > ++ significantly reduce the minimum attack payload size. > ++ Distributors intending to backport (or cherry-pick) the > ++ fix need to copy 99% of the related pull request, not just > ++ the "lib: Implement tracking of dynamic memory allocations" > ++ commit, to not end up with a state that literally does both > ++ too much and too little at the same time. Appending ".diff" > ++ to the pull request URL could be of help. > ++ > + Bug fixes: > + #980 #989 Restore event pointer behavior from Expat 2.6.4 > + (that the fix to CVE-2024-8176 changed in 2.7.0); > +@@ -39,6 +69,10 @@ Patches: > + - XML_GetCurrentColumnNumber > + - XML_GetCurrentLineNumber > + - XML_GetInputContext > ++ #1034 docs: Promote the contract to call function > ++ XML_FreeContentModel when registering a custom > ++ element declaration handler (via a call to function > ++ XML_SetElementDeclHandler) > + > + Special thanks to: > + Berkay Eren Ürün > +@@ -71,6 +105,9 @@ Patches: > + Linutronix > + Red Hat > + Siemens > ++ and > ++ OSS-Fuzz / ClusterFuzz > ++ Perl XML::Parser > + > + Release 2.6.4 Wed November 6 2024 > + Security fixes: > diff --git a/meta/recipes-core/expat/expat_2.6.4.bb b/meta/recipes-core/expat/expat_2.6.4.bb > index 816beaa8a3d..fdfe2b850a7 100644 > --- a/meta/recipes-core/expat/expat_2.6.4.bb > +++ b/meta/recipes-core/expat/expat_2.6.4.bb > @@ -16,6 +16,25 @@ SRC_URI = "${GITHUB_BASE_URI}/download/R_${VERSION_TAG}/expat-${PV}.tar.bz2 \ > file://CVE-2024-8176-03.patch \ > file://CVE-2024-8176-04.patch \ > file://CVE-2024-8176-05.patch \ > + file://CVE-2025-59375-00.patch \ > + file://CVE-2025-59375-01.patch \ > + file://CVE-2025-59375-02.patch \ > + file://CVE-2025-59375-03.patch \ > + file://CVE-2025-59375-04.patch \ > + file://CVE-2025-59375-05.patch \ > + file://CVE-2025-59375-06.patch \ > + file://CVE-2025-59375-07.patch \ > + file://CVE-2025-59375-08.patch \ > + file://CVE-2025-59375-09.patch \ > + file://CVE-2025-59375-10.patch \ > + file://CVE-2025-59375-11.patch \ > + file://CVE-2025-59375-12.patch \ > + file://CVE-2025-59375-13.patch \ > + file://CVE-2025-59375-14.patch \ > + file://CVE-2025-59375-15.patch \ > + file://CVE-2025-59375-16.patch \ > + file://CVE-2025-59375-17.patch \ > + file://CVE-2025-59375-18.patch \ > " > > GITHUB_BASE_URI = "https://github.com/libexpat/libexpat/releases/" > > -=-=-=-=-=-=-=-=-=-=-=- > Links: You receive all messages sent to this group. > View/Reply Online (#225131): https://lists.openembedded.org/g/openembedded-core/message/225131 > Mute This Topic: https://lists.openembedded.org/mt/115861217/3619737 > Group Owner: openembedded-core+owner@lists.openembedded.org > Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub [ankur.tyagi85@gmail.com] > -=-=-=-=-=-=-=-=-=-=-=- >
> -----Original Message----- > From: Ankur Tyagi <ankur.tyagi85@gmail.com> > Sent: Tuesday, October 21, 2025 7:43 > To: Marko, Peter (FT D EU SK BFS1) <Peter.Marko@siemens.com> > Cc: openembedded-core@lists.openembedded.org > Subject: Re: [OE-core][scarthgap][PATCH] expat: patch CVE-2025-59375 > > On Tue, Oct 21, 2025 at 8:55 AM Peter Marko via lists.openembedded.org > <peter.marko=siemens.com@lists.openembedded.org> wrote: > > > > From: Peter Marko <peter.marko@siemens.com> > > > > Pick patch from PR mentioning this CVE [1] > > > > It's a complex patch so I have checked diff of 2.6.4 and commit before > > these patches landed. There were no changes in memory allocations. > > Also version in scarthgap is still not that much different from current > > upstream master. > > Ptests pass. > > > > CVE fix caused SIGBUS error on sparc32 which got fixed as part of > https://github.com/libexpat/libexpat/issues/1046 > Should we include that as well? Thanks for notifying me. CVE patch regressions certainly need to be fixed. So I'll send a v2 later today or tomorrow. Peter > > Otherwise patches LGTM. > > > Also picked one documentation commit (-00) to resolve patch conflict. > > Following conflicts were resolved manually: > > * commit "mass-cppcheck.sh: Activate in-code suppression comments" was > > skipped as it only edited github actions not yet available in 2.6.4 > > * commit "lib: Implement tracking of dynamic memory allocations" > > ale had conflict in github actions not yet available in 2.6.4 > > * commit "fuzz: Be robust towards NULL return from > XML_ExternalEntityParserCreate" > > edited file "expat/fuzz/xml_lpm_fuzzer.cpp" which is not present in > > our version yet. Since we're not using fuzzying, this is not needed. > > * the final changelog commit needed lot conflict resolution actions > > > > [1] `https://github.com/libexpat/libexpat/pull/1034` > > > > Signed-off-by: Peter Marko <peter.marko@siemens.com> > > --- > > .../expat/expat/CVE-2025-59375-00.patch | 52 ++ > > .../expat/expat/CVE-2025-59375-01.patch | 48 ++ > > .../expat/expat/CVE-2025-59375-02.patch | 109 ++++ > > .../expat/expat/CVE-2025-59375-03.patch | 127 ++++ > > .../expat/expat/CVE-2025-59375-04.patch | 62 ++ > > .../expat/expat/CVE-2025-59375-05.patch | 64 ++ > > .../expat/expat/CVE-2025-59375-06.patch | 68 +++ > > .../expat/expat/CVE-2025-59375-07.patch | 52 ++ > > .../expat/expat/CVE-2025-59375-08.patch | 577 ++++++++++++++++++ > > .../expat/expat/CVE-2025-59375-09.patch | 43 ++ > > .../expat/expat/CVE-2025-59375-10.patch | 54 ++ > > .../expat/expat/CVE-2025-59375-11.patch | 66 ++ > > .../expat/expat/CVE-2025-59375-12.patch | 58 ++ > > .../expat/expat/CVE-2025-59375-13.patch | 309 ++++++++++ > > .../expat/expat/CVE-2025-59375-14.patch | 122 ++++ > > .../expat/expat/CVE-2025-59375-15.patch | 70 +++ > > .../expat/expat/CVE-2025-59375-16.patch | 146 +++++ > > .../expat/expat/CVE-2025-59375-17.patch | 28 + > > .../expat/expat/CVE-2025-59375-18.patch | 74 +++ > > meta/recipes-core/expat/expat_2.6.4.bb | 19 + > > 20 files changed, 2148 insertions(+) > > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-00.patch > > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-01.patch > > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-02.patch > > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-03.patch > > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-04.patch > > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-05.patch > > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-06.patch > > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-07.patch > > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-08.patch > > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-09.patch > > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-10.patch > > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-11.patch > > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-12.patch > > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-13.patch > > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-14.patch > > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-15.patch > > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-16.patch > > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-17.patch > > create mode 100644 meta/recipes-core/expat/expat/CVE-2025-59375-18.patch > > > > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-00.patch > b/meta/recipes-core/expat/expat/CVE-2025-59375-00.patch > > new file mode 100644 > > index 00000000000..e3cbd0f6048 > > --- /dev/null > > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-00.patch > > @@ -0,0 +1,52 @@ > > +From 87321ac84a0d6cb42ee64a591adc79c1ec37fb5b Mon Sep 17 00:00:00 > 2001 > > +From: Sebastian Pipping <sebastian@pipping.org> > > +Date: Tue, 2 Sep 2025 20:52:29 +0200 > > +Subject: [PATCH] xmlwf: Mention supported environment variables in --help > > + output > > + > > +CVE: CVE-2025-59375 > > +Upstream-Status: Backport > [https://github.com/libexpat/libexpat/commit/87321ac84a0d6cb42ee64a591adc79c > 1ec37fb5b] > > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > > +--- > > + xmlwf/xmlwf.c | 8 ++++++++ > > + xmlwf/xmlwf_helpgen.py | 8 ++++++++ > > + 2 files changed, 16 insertions(+) > > + > > +diff --git a/xmlwf/xmlwf.c b/xmlwf/xmlwf.c > > +index ec7e51c9..8cfc73ca 100644 > > +--- a/xmlwf/xmlwf.c > > ++++ b/xmlwf/xmlwf.c > > +@@ -926,6 +926,14 @@ usage(const XML_Char *prog, int rc) { > > + T(" -h, --help show this [h]elp message and exit\n") > > + T(" -v, --version show program's [v]ersion number and exit\n") > > + T("\n") > > ++ T("environment variables:\n") > > ++ T(" EXPAT_ACCOUNTING_DEBUG=(0|1|2|3)\n") > > ++ T(" Control verbosity of accounting debugging (default: 0)\n") > > ++ T(" EXPAT_ENTITY_DEBUG=(0|1)\n") > > ++ T(" Control verbosity of entity debugging (default: 0)\n") > > ++ T(" EXPAT_ENTROPY_DEBUG=(0|1)\n") > > ++ T(" Control verbosity of entropy debugging (default: 0)\n") > > ++ T("\n") > > + T("exit status:\n") > > + T(" 0 the input files are well-formed and the output (if requested) > was written successfully\n") > > + T(" 1 could not allocate data structures, signals a serious > problem with execution environment\n") > > +diff --git a/xmlwf/xmlwf_helpgen.py b/xmlwf/xmlwf_helpgen.py > > +index c3257f0e..39a3dc13 100755 > > +--- a/xmlwf/xmlwf_helpgen.py > > ++++ b/xmlwf/xmlwf_helpgen.py > > +@@ -32,6 +32,14 @@ > > + import argparse > > + > > + epilog = """ > > ++environment variables: > > ++ EXPAT_ACCOUNTING_DEBUG=(0|1|2|3) > > ++ Control verbosity of accounting debugging (default: 0) > > ++ EXPAT_ENTITY_DEBUG=(0|1) > > ++ Control verbosity of entity debugging (default: 0) > > ++ EXPAT_ENTROPY_DEBUG=(0|1) > > ++ Control verbosity of entropy debugging (default: 0) > > ++ > > + exit status: > > + 0 the input files are well-formed and the output (if requested) was > written successfully > > + 1 could not allocate data structures, signals a serious problem with > execution environment > > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-01.patch > b/meta/recipes-core/expat/expat/CVE-2025-59375-01.patch > > new file mode 100644 > > index 00000000000..6708bbef45c > > --- /dev/null > > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-01.patch > > @@ -0,0 +1,48 @@ > > +From 0872c189db6e457084fca335662a9cb49e8ec4c7 Mon Sep 17 00:00:00 > 2001 > > +From: Sebastian Pipping <sebastian@pipping.org> > > +Date: Mon, 1 Sep 2025 18:06:59 +0200 > > +Subject: [PATCH] lib: Make function dtdCreate use macro MALLOC > > + > > +.. and give its body access to the parser for upcoming changes > > + > > +CVE: CVE-2025-59375 > > +Upstream-Status: Backport > [https://github.com/libexpat/libexpat/commit/0872c189db6e457084fca335662a9cb4 > 9e8ec4c7] > > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > > +--- > > + lib/xmlparse.c | 9 +++++---- > > + 1 file changed, 5 insertions(+), 4 deletions(-) > > + > > +diff --git a/lib/xmlparse.c b/lib/xmlparse.c > > +index 25f786ec..b9d6eed1 100644 > > +--- a/lib/xmlparse.c > > ++++ b/lib/xmlparse.c > > +@@ -555,7 +555,7 @@ static XML_Bool setContext(XML_Parser parser, const > XML_Char *context); > > + > > + static void FASTCALL normalizePublicId(XML_Char *s); > > + > > +-static DTD *dtdCreate(const XML_Memory_Handling_Suite *ms); > > ++static DTD *dtdCreate(XML_Parser parser); > > + /* do not call if m_parentParser != NULL */ > > + static void dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms); > > + static void dtdDestroy(DTD *p, XML_Bool isDocEntity, > > +@@ -1166,7 +1166,7 @@ parserCreate(const XML_Char *encodingName, > > + if (dtd) > > + parser->m_dtd = dtd; > > + else { > > +- parser->m_dtd = dtdCreate(&parser->m_mem); > > ++ parser->m_dtd = dtdCreate(parser); > > + if (parser->m_dtd == NULL) { > > + FREE(parser, parser->m_dataBuf); > > + FREE(parser, parser->m_atts); > > +@@ -7126,8 +7126,9 @@ normalizePublicId(XML_Char *publicId) { > > + } > > + > > + static DTD * > > +-dtdCreate(const XML_Memory_Handling_Suite *ms) { > > +- DTD *p = ms->malloc_fcn(sizeof(DTD)); > > ++dtdCreate(XML_Parser parser) { > > ++ const XML_Memory_Handling_Suite *const ms = &parser->m_mem; > > ++ DTD *p = MALLOC(parser, sizeof(DTD)); > > + if (p == NULL) > > + return p; > > + poolInit(&(p->pool), ms); > > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-02.patch > b/meta/recipes-core/expat/expat/CVE-2025-59375-02.patch > > new file mode 100644 > > index 00000000000..b0543370ad8 > > --- /dev/null > > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-02.patch > > @@ -0,0 +1,109 @@ > > +From 8768dadae479d9f2e984b747fb2ba79bb78de94f Mon Sep 17 00:00:00 > 2001 > > +From: Sebastian Pipping <sebastian@pipping.org> > > +Date: Mon, 1 Sep 2025 18:10:26 +0200 > > +Subject: [PATCH] lib: Make string pools use macros MALLOC, FREE, > REALLOC > > + > > +CVE: CVE-2025-59375 > > +Upstream-Status: Backport > [https://github.com/libexpat/libexpat/commit/8768dadae479d9f2e984b747fb2ba79 > bb78de94f] > > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > > +--- > > + lib/xmlparse.c | 27 +++++++++++++-------------- > > + 1 file changed, 13 insertions(+), 14 deletions(-) > > + > > +diff --git a/lib/xmlparse.c b/lib/xmlparse.c > > +index b9d6eed1..a56c71ea 100644 > > +--- a/lib/xmlparse.c > > ++++ b/lib/xmlparse.c > > +@@ -357,7 +357,7 @@ typedef struct { > > + const XML_Char *end; > > + XML_Char *ptr; > > + XML_Char *start; > > +- const XML_Memory_Handling_Suite *mem; > > ++ XML_Parser parser; > > + } STRING_POOL; > > + > > + /* The XML_Char before the name is used to determine whether > > +@@ -574,8 +574,7 @@ static void FASTCALL > hashTableIterInit(HASH_TABLE_ITER *iter, > > + const HASH_TABLE *table); > > + static NAMED *FASTCALL hashTableIterNext(HASH_TABLE_ITER *iter); > > + > > +-static void FASTCALL poolInit(STRING_POOL *pool, > > +- const XML_Memory_Handling_Suite *ms); > > ++static void FASTCALL poolInit(STRING_POOL *pool, XML_Parser parser); > > + static void FASTCALL poolClear(STRING_POOL *pool); > > + static void FASTCALL poolDestroy(STRING_POOL *pool); > > + static XML_Char *poolAppend(STRING_POOL *pool, const ENCODING *enc, > > +@@ -1200,8 +1199,8 @@ parserCreate(const XML_Char *encodingName, > > + > > + parser->m_protocolEncodingName = NULL; > > + > > +- poolInit(&parser->m_tempPool, &(parser->m_mem)); > > +- poolInit(&parser->m_temp2Pool, &(parser->m_mem)); > > ++ poolInit(&parser->m_tempPool, parser); > > ++ poolInit(&parser->m_temp2Pool, parser); > > + parserInit(parser, encodingName); > > + > > + if (encodingName && ! parser->m_protocolEncodingName) { > > +@@ -7131,8 +7130,8 @@ dtdCreate(XML_Parser parser) { > > + DTD *p = MALLOC(parser, sizeof(DTD)); > > + if (p == NULL) > > + return p; > > +- poolInit(&(p->pool), ms); > > +- poolInit(&(p->entityValuePool), ms); > > ++ poolInit(&(p->pool), parser); > > ++ poolInit(&(p->entityValuePool), parser); > > + hashTableInit(&(p->generalEntities), ms); > > + hashTableInit(&(p->elementTypes), ms); > > + hashTableInit(&(p->attributeIds), ms); > > +@@ -7596,13 +7595,13 @@ hashTableIterNext(HASH_TABLE_ITER *iter) { > > + } > > + > > + static void FASTCALL > > +-poolInit(STRING_POOL *pool, const XML_Memory_Handling_Suite *ms) { > > ++poolInit(STRING_POOL *pool, XML_Parser parser) { > > + pool->blocks = NULL; > > + pool->freeBlocks = NULL; > > + pool->start = NULL; > > + pool->ptr = NULL; > > + pool->end = NULL; > > +- pool->mem = ms; > > ++ pool->parser = parser; > > + } > > + > > + static void FASTCALL > > +@@ -7629,13 +7628,13 @@ poolDestroy(STRING_POOL *pool) { > > + BLOCK *p = pool->blocks; > > + while (p) { > > + BLOCK *tem = p->next; > > +- pool->mem->free_fcn(p); > > ++ FREE(pool->parser, p); > > + p = tem; > > + } > > + p = pool->freeBlocks; > > + while (p) { > > + BLOCK *tem = p->next; > > +- pool->mem->free_fcn(p); > > ++ FREE(pool->parser, p); > > + p = tem; > > + } > > + } > > +@@ -7790,8 +7789,8 @@ poolGrow(STRING_POOL *pool) { > > + if (bytesToAllocate == 0) > > + return XML_FALSE; > > + > > +- temp = (BLOCK *)pool->mem->realloc_fcn(pool->blocks, > > +- (unsigned)bytesToAllocate); > > ++ temp = (BLOCK *)REALLOC(pool->parser, pool->blocks, > > ++ (unsigned)bytesToAllocate); > > + if (temp == NULL) > > + return XML_FALSE; > > + pool->blocks = temp; > > +@@ -7831,7 +7830,7 @@ poolGrow(STRING_POOL *pool) { > > + if (bytesToAllocate == 0) > > + return XML_FALSE; > > + > > +- tem = pool->mem->malloc_fcn(bytesToAllocate); > > ++ tem = MALLOC(pool->parser, bytesToAllocate); > > + if (! tem) > > + return XML_FALSE; > > + tem->size = blockSize; > > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-03.patch > b/meta/recipes-core/expat/expat/CVE-2025-59375-03.patch > > new file mode 100644 > > index 00000000000..b8c2c595e1a > > --- /dev/null > > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-03.patch > > @@ -0,0 +1,127 @@ > > +From 4fc6f1ee9f2b282cfe446bf645c992e37f8c3e15 Mon Sep 17 00:00:00 2001 > > +From: Sebastian Pipping <sebastian@pipping.org> > > +Date: Mon, 1 Sep 2025 18:14:09 +0200 > > +Subject: [PATCH] lib: Make function hash tables use macros MALLOC and > FREE > > + > > +CVE: CVE-2025-59375 > > +Upstream-Status: Backport > [https://github.com/libexpat/libexpat/commit/4fc6f1ee9f2b282cfe446bf645c992e37 > f8c3e15] > > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > > +--- > > + lib/xmlparse.c | 34 ++++++++++++++++------------------ > > + 1 file changed, 16 insertions(+), 18 deletions(-) > > + > > +diff --git a/lib/xmlparse.c b/lib/xmlparse.c > > +index a56c71ea..a65b0265 100644 > > +--- a/lib/xmlparse.c > > ++++ b/lib/xmlparse.c > > +@@ -234,7 +234,7 @@ typedef struct { > > + unsigned char power; > > + size_t size; > > + size_t used; > > +- const XML_Memory_Handling_Suite *mem; > > ++ XML_Parser parser; > > + } HASH_TABLE; > > + > > + static size_t keylen(KEY s); > > +@@ -566,8 +566,7 @@ static int copyEntityTable(XML_Parser oldParser, > HASH_TABLE *newTable, > > + STRING_POOL *newPool, const HASH_TABLE *oldTable); > > + static NAMED *lookup(XML_Parser parser, HASH_TABLE *table, KEY name, > > + size_t createSize); > > +-static void FASTCALL hashTableInit(HASH_TABLE *table, > > +- const XML_Memory_Handling_Suite *ms); > > ++static void FASTCALL hashTableInit(HASH_TABLE *table, XML_Parser > parser); > > + static void FASTCALL hashTableClear(HASH_TABLE *table); > > + static void FASTCALL hashTableDestroy(HASH_TABLE *table); > > + static void FASTCALL hashTableIterInit(HASH_TABLE_ITER *iter, > > +@@ -7126,19 +7125,18 @@ normalizePublicId(XML_Char *publicId) { > > + > > + static DTD * > > + dtdCreate(XML_Parser parser) { > > +- const XML_Memory_Handling_Suite *const ms = &parser->m_mem; > > + DTD *p = MALLOC(parser, sizeof(DTD)); > > + if (p == NULL) > > + return p; > > + poolInit(&(p->pool), parser); > > + poolInit(&(p->entityValuePool), parser); > > +- hashTableInit(&(p->generalEntities), ms); > > +- hashTableInit(&(p->elementTypes), ms); > > +- hashTableInit(&(p->attributeIds), ms); > > +- hashTableInit(&(p->prefixes), ms); > > ++ hashTableInit(&(p->generalEntities), parser); > > ++ hashTableInit(&(p->elementTypes), parser); > > ++ hashTableInit(&(p->attributeIds), parser); > > ++ hashTableInit(&(p->prefixes), parser); > > + #ifdef XML_DTD > > + p->paramEntityRead = XML_FALSE; > > +- hashTableInit(&(p->paramEntities), ms); > > ++ hashTableInit(&(p->paramEntities), parser); > > + #endif /* XML_DTD */ > > + p->defaultPrefix.name = NULL; > > + p->defaultPrefix.binding = NULL; > > +@@ -7473,7 +7471,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, > KEY name, size_t createSize) { > > + /* table->size is a power of 2 */ > > + table->size = (size_t)1 << INIT_POWER; > > + tsize = table->size * sizeof(NAMED *); > > +- table->v = table->mem->malloc_fcn(tsize); > > ++ table->v = MALLOC(table->parser, tsize); > > + if (! table->v) { > > + table->size = 0; > > + return NULL; > > +@@ -7513,7 +7511,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, > KEY name, size_t createSize) { > > + } > > + > > + size_t tsize = newSize * sizeof(NAMED *); > > +- NAMED **newV = table->mem->malloc_fcn(tsize); > > ++ NAMED **newV = MALLOC(table->parser, tsize); > > + if (! newV) > > + return NULL; > > + memset(newV, 0, tsize); > > +@@ -7529,7 +7527,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, > KEY name, size_t createSize) { > > + } > > + newV[j] = table->v[i]; > > + } > > +- table->mem->free_fcn(table->v); > > ++ FREE(table->parser, table->v); > > + table->v = newV; > > + table->power = newPower; > > + table->size = newSize; > > +@@ -7542,7 +7540,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, > KEY name, size_t createSize) { > > + } > > + } > > + } > > +- table->v[i] = table->mem->malloc_fcn(createSize); > > ++ table->v[i] = MALLOC(table->parser, createSize); > > + if (! table->v[i]) > > + return NULL; > > + memset(table->v[i], 0, createSize); > > +@@ -7555,7 +7553,7 @@ static void FASTCALL > > + hashTableClear(HASH_TABLE *table) { > > + size_t i; > > + for (i = 0; i < table->size; i++) { > > +- table->mem->free_fcn(table->v[i]); > > ++ FREE(table->parser, table->v[i]); > > + table->v[i] = NULL; > > + } > > + table->used = 0; > > +@@ -7565,17 +7563,17 @@ static void FASTCALL > > + hashTableDestroy(HASH_TABLE *table) { > > + size_t i; > > + for (i = 0; i < table->size; i++) > > +- table->mem->free_fcn(table->v[i]); > > +- table->mem->free_fcn(table->v); > > ++ FREE(table->parser, table->v[i]); > > ++ FREE(table->parser, table->v); > > + } > > + > > + static void FASTCALL > > +-hashTableInit(HASH_TABLE *p, const XML_Memory_Handling_Suite *ms) { > > ++hashTableInit(HASH_TABLE *p, XML_Parser parser) { > > + p->power = 0; > > + p->size = 0; > > + p->used = 0; > > + p->v = NULL; > > +- p->mem = ms; > > ++ p->parser = parser; > > + } > > + > > + static void FASTCALL > > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-04.patch > b/meta/recipes-core/expat/expat/CVE-2025-59375-04.patch > > new file mode 100644 > > index 00000000000..78d9e2fc91b > > --- /dev/null > > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-04.patch > > @@ -0,0 +1,62 @@ > > +From 51487ad9d760faa4809b0f8e189d2f666317e41a Mon Sep 17 00:00:00 > 2001 > > +From: Sebastian Pipping <sebastian@pipping.org> > > +Date: Mon, 1 Sep 2025 17:45:50 +0200 > > +Subject: [PATCH] lib: Make function copyString use macro MALLOC > > + > > +CVE: CVE-2025-59375 > > +Upstream-Status: Backport > [https://github.com/libexpat/libexpat/commit/51487ad9d760faa4809b0f8e189d2f66 > 6317e41a] > > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > > +--- > > + lib/xmlparse.c | 11 +++++------ > > + 1 file changed, 5 insertions(+), 6 deletions(-) > > + > > +diff --git a/lib/xmlparse.c b/lib/xmlparse.c > > +index a65b0265..c0576abd 100644 > > +--- a/lib/xmlparse.c > > ++++ b/lib/xmlparse.c > > +@@ -593,8 +593,7 @@ static XML_Content *build_model(XML_Parser parser); > > + static ELEMENT_TYPE *getElementType(XML_Parser parser, const > ENCODING *enc, > > + const char *ptr, const char *end); > > + > > +-static XML_Char *copyString(const XML_Char *s, > > +- const XML_Memory_Handling_Suite *memsuite); > > ++static XML_Char *copyString(const XML_Char *s, XML_Parser parser); > > + > > + static unsigned long generate_hash_secret_salt(XML_Parser parser); > > + static XML_Bool startParsing(XML_Parser parser); > > +@@ -1231,7 +1230,7 @@ parserInit(XML_Parser parser, const XML_Char > *encodingName) { > > + parser->m_processor = prologInitProcessor; > > + XmlPrologStateInit(&parser->m_prologState); > > + if (encodingName != NULL) { > > +- parser->m_protocolEncodingName = copyString(encodingName, &(parser- > >m_mem)); > > ++ parser->m_protocolEncodingName = copyString(encodingName, parser); > > + } > > + parser->m_curBase = NULL; > > + XmlInitEncoding(&parser->m_initEncoding, &parser->m_encoding, 0); > > +@@ -1419,7 +1418,7 @@ XML_SetEncoding(XML_Parser parser, const > XML_Char *encodingName) { > > + parser->m_protocolEncodingName = NULL; > > + else { > > + /* Copy the new encoding name into allocated memory */ > > +- parser->m_protocolEncodingName = copyString(encodingName, &(parser- > >m_mem)); > > ++ parser->m_protocolEncodingName = copyString(encodingName, parser); > > + if (! parser->m_protocolEncodingName) > > + return XML_STATUS_ERROR; > > + } > > +@@ -8064,7 +8063,7 @@ getElementType(XML_Parser parser, const > ENCODING *enc, const char *ptr, > > + } > > + > > + static XML_Char * > > +-copyString(const XML_Char *s, const XML_Memory_Handling_Suite > *memsuite) { > > ++copyString(const XML_Char *s, XML_Parser parser) { > > + size_t charsRequired = 0; > > + XML_Char *result; > > + > > +@@ -8076,7 +8075,7 @@ copyString(const XML_Char *s, const > XML_Memory_Handling_Suite *memsuite) { > > + charsRequired++; > > + > > + /* Now allocate space for the copy */ > > +- result = memsuite->malloc_fcn(charsRequired * sizeof(XML_Char)); > > ++ result = MALLOC(parser, charsRequired * sizeof(XML_Char)); > > + if (result == NULL) > > + return NULL; > > + /* Copy the original into place */ > > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-05.patch > b/meta/recipes-core/expat/expat/CVE-2025-59375-05.patch > > new file mode 100644 > > index 00000000000..37b882fbf49 > > --- /dev/null > > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-05.patch > > @@ -0,0 +1,64 @@ > > +From b3f0bda5f5e979781469532f7c304f7e223568d5 Mon Sep 17 00:00:00 2001 > > +From: Sebastian Pipping <sebastian@pipping.org> > > +Date: Mon, 1 Sep 2025 17:48:02 +0200 > > +Subject: [PATCH] lib: Make function dtdReset use macro FREE > > + > > +CVE: CVE-2025-59375 > > +Upstream-Status: Backport > [https://github.com/libexpat/libexpat/commit/b3f0bda5f5e979781469532f7c304f7e2 > 23568d5] > > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > > +--- > > + lib/xmlparse.c | 12 ++++++------ > > + 1 file changed, 6 insertions(+), 6 deletions(-) > > + > > +diff --git a/lib/xmlparse.c b/lib/xmlparse.c > > +index c0576abd..65fcce30 100644 > > +--- a/lib/xmlparse.c > > ++++ b/lib/xmlparse.c > > +@@ -557,7 +557,7 @@ static void FASTCALL normalizePublicId(XML_Char *s); > > + > > + static DTD *dtdCreate(XML_Parser parser); > > + /* do not call if m_parentParser != NULL */ > > +-static void dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms); > > ++static void dtdReset(DTD *p, XML_Parser parser); > > + static void dtdDestroy(DTD *p, XML_Bool isDocEntity, > > + const XML_Memory_Handling_Suite *ms); > > + static int dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, > > +@@ -1382,7 +1382,7 @@ XML_ParserReset(XML_Parser parser, const > XML_Char *encodingName) { > > + FREE(parser, (void *)parser->m_protocolEncodingName); > > + parser->m_protocolEncodingName = NULL; > > + parserInit(parser, encodingName); > > +- dtdReset(parser->m_dtd, &parser->m_mem); > > ++ dtdReset(parser->m_dtd, parser); > > + return XML_TRUE; > > + } > > + > > +@@ -7155,7 +7155,7 @@ dtdCreate(XML_Parser parser) { > > + } > > + > > + static void > > +-dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) { > > ++dtdReset(DTD *p, XML_Parser parser) { > > + HASH_TABLE_ITER iter; > > + hashTableIterInit(&iter, &(p->elementTypes)); > > + for (;;) { > > +@@ -7163,7 +7163,7 @@ dtdReset(DTD *p, const > XML_Memory_Handling_Suite *ms) { > > + if (! e) > > + break; > > + if (e->allocDefaultAtts != 0) > > +- ms->free_fcn(e->defaultAtts); > > ++ FREE(parser, e->defaultAtts); > > + } > > + hashTableClear(&(p->generalEntities)); > > + #ifdef XML_DTD > > +@@ -7180,9 +7180,9 @@ dtdReset(DTD *p, const > XML_Memory_Handling_Suite *ms) { > > + > > + p->in_eldecl = XML_FALSE; > > + > > +- ms->free_fcn(p->scaffIndex); > > ++ FREE(parser, p->scaffIndex); > > + p->scaffIndex = NULL; > > +- ms->free_fcn(p->scaffold); > > ++ FREE(parser, p->scaffold); > > + p->scaffold = NULL; > > + > > + p->scaffLevel = 0; > > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-06.patch > b/meta/recipes-core/expat/expat/CVE-2025-59375-06.patch > > new file mode 100644 > > index 00000000000..04f975a458f > > --- /dev/null > > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-06.patch > > @@ -0,0 +1,68 @@ > > +From 53a3eda0ae2e0317afd071b72b41976053d82732 Mon Sep 17 00:00:00 > 2001 > > +From: Sebastian Pipping <sebastian@pipping.org> > > +Date: Mon, 1 Sep 2025 17:50:59 +0200 > > +Subject: [PATCH] lib: Make function dtdDestroy use macro FREE > > + > > +CVE: CVE-2025-59375 > > +Upstream-Status: Backport > [https://github.com/libexpat/libexpat/commit/53a3eda0ae2e0317afd071b72b419760 > 53d82732] > > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > > +--- > > + lib/xmlparse.c | 16 +++++++--------- > > + 1 file changed, 7 insertions(+), 9 deletions(-) > > + > > +diff --git a/lib/xmlparse.c b/lib/xmlparse.c > > +index 65fcce30..e7df97da 100644 > > +--- a/lib/xmlparse.c > > ++++ b/lib/xmlparse.c > > +@@ -558,8 +558,7 @@ static void FASTCALL normalizePublicId(XML_Char *s); > > + static DTD *dtdCreate(XML_Parser parser); > > + /* do not call if m_parentParser != NULL */ > > + static void dtdReset(DTD *p, XML_Parser parser); > > +-static void dtdDestroy(DTD *p, XML_Bool isDocEntity, > > +- const XML_Memory_Handling_Suite *ms); > > ++static void dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser); > > + static int dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, > > + const XML_Memory_Handling_Suite *ms); > > + static int copyEntityTable(XML_Parser oldParser, HASH_TABLE *newTable, > > +@@ -1685,8 +1684,7 @@ XML_ParserFree(XML_Parser parser) { > > + #else > > + if (parser->m_dtd) > > + #endif /* XML_DTD */ > > +- dtdDestroy(parser->m_dtd, (XML_Bool)! parser->m_parentParser, > > +- &parser->m_mem); > > ++ dtdDestroy(parser->m_dtd, (XML_Bool)! parser->m_parentParser, parser); > > + FREE(parser, (void *)parser->m_atts); > > + #ifdef XML_ATTR_INFO > > + FREE(parser, (void *)parser->m_attInfo); > > +@@ -7196,7 +7194,7 @@ dtdReset(DTD *p, XML_Parser parser) { > > + } > > + > > + static void > > +-dtdDestroy(DTD *p, XML_Bool isDocEntity, const > XML_Memory_Handling_Suite *ms) { > > ++dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser) { > > + HASH_TABLE_ITER iter; > > + hashTableIterInit(&iter, &(p->elementTypes)); > > + for (;;) { > > +@@ -7204,7 +7202,7 @@ dtdDestroy(DTD *p, XML_Bool isDocEntity, const > XML_Memory_Handling_Suite *ms) { > > + if (! e) > > + break; > > + if (e->allocDefaultAtts != 0) > > +- ms->free_fcn(e->defaultAtts); > > ++ FREE(parser, e->defaultAtts); > > + } > > + hashTableDestroy(&(p->generalEntities)); > > + #ifdef XML_DTD > > +@@ -7216,10 +7214,10 @@ dtdDestroy(DTD *p, XML_Bool isDocEntity, const > XML_Memory_Handling_Suite *ms) { > > + poolDestroy(&(p->pool)); > > + poolDestroy(&(p->entityValuePool)); > > + if (isDocEntity) { > > +- ms->free_fcn(p->scaffIndex); > > +- ms->free_fcn(p->scaffold); > > ++ FREE(parser, p->scaffIndex); > > ++ FREE(parser, p->scaffold); > > + } > > +- ms->free_fcn(p); > > ++ FREE(parser, p); > > + } > > + > > + /* Do a deep copy of the DTD. Return 0 for out of memory, non-zero > otherwise. > > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-07.patch > b/meta/recipes-core/expat/expat/CVE-2025-59375-07.patch > > new file mode 100644 > > index 00000000000..7eff0009d2c > > --- /dev/null > > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-07.patch > > @@ -0,0 +1,52 @@ > > +From 4e7a5d03daf672f20c73d40dc8970385c18b30d3 Mon Sep 17 00:00:00 > 2001 > > +From: Sebastian Pipping <sebastian@pipping.org> > > +Date: Mon, 1 Sep 2025 17:52:58 +0200 > > +Subject: [PATCH] lib: Make function dtdCopy use macro MALLOC > > + > > +CVE: CVE-2025-59375 > > +Upstream-Status: Backport > [https://github.com/libexpat/libexpat/commit/4e7a5d03daf672f20c73d40dc8970385 > c18b30d3] > > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > > +--- > > + lib/xmlparse.c | 8 ++++---- > > + 1 file changed, 4 insertions(+), 4 deletions(-) > > + > > +diff --git a/lib/xmlparse.c b/lib/xmlparse.c > > +index e7df97da..9f0a8b3e 100644 > > +--- a/lib/xmlparse.c > > ++++ b/lib/xmlparse.c > > +@@ -560,7 +560,7 @@ static DTD *dtdCreate(XML_Parser parser); > > + static void dtdReset(DTD *p, XML_Parser parser); > > + static void dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser); > > + static int dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, > > +- const XML_Memory_Handling_Suite *ms); > > ++ XML_Parser parser); > > + static int copyEntityTable(XML_Parser oldParser, HASH_TABLE *newTable, > > + STRING_POOL *newPool, const HASH_TABLE *oldTable); > > + static NAMED *lookup(XML_Parser parser, HASH_TABLE *table, KEY name, > > +@@ -1572,7 +1572,7 @@ XML_ExternalEntityParserCreate(XML_Parser > oldParser, const XML_Char *context, > > + parser->m_prologState.inEntityValue = oldInEntityValue; > > + if (context) { > > + #endif /* XML_DTD */ > > +- if (! dtdCopy(oldParser, parser->m_dtd, oldDtd, &parser->m_mem) > > ++ if (! dtdCopy(oldParser, parser->m_dtd, oldDtd, parser) > > + || ! setContext(parser, context)) { > > + XML_ParserFree(parser); > > + return NULL; > > +@@ -7225,7 +7225,7 @@ dtdDestroy(DTD *p, XML_Bool isDocEntity, > XML_Parser parser) { > > + */ > > + static int > > + dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, > > +- const XML_Memory_Handling_Suite *ms) { > > ++ XML_Parser parser) { > > + HASH_TABLE_ITER iter; > > + > > + /* Copy the prefix table. */ > > +@@ -7306,7 +7306,7 @@ dtdCopy(XML_Parser oldParser, DTD *newDtd, > const DTD *oldDtd, > > + } > > + #endif > > + newE->defaultAtts > > +- = ms->malloc_fcn(oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE)); > > ++ = MALLOC(parser, oldE->nDefaultAtts * > sizeof(DEFAULT_ATTRIBUTE)); > > + if (! newE->defaultAtts) { > > + return 0; > > + } > > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-08.patch > b/meta/recipes-core/expat/expat/CVE-2025-59375-08.patch > > new file mode 100644 > > index 00000000000..deda31bebcd > > --- /dev/null > > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-08.patch > > @@ -0,0 +1,577 @@ > > +From cfce28e171676fe6f70d17b97ed8a59eaeb83f15 Mon Sep 17 00:00:00 2001 > > +From: Sebastian Pipping <sebastian@pipping.org> > > +Date: Mon, 1 Sep 2025 17:34:58 +0200 > > +Subject: [PATCH] lib: Implement tracking of dynamic memory allocations > > + > > +**PLEASE NOTE** that distributors intending to backport (or cherry-pick) > > +this fix need to copy 99% of the related pull request, not just this > > +commit, to not end up with a state that literally does both too much and > > +too little at the same time. Appending ".diff" to the pull request URL > > +could be of help. > > + > > +CVE: CVE-2025-59375 > > +Upstream-Status: Backport > [https://github.com/libexpat/libexpat/commit/cfce28e171676fe6f70d17b97ed8a59e > aeb83f15] > > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > > +--- > > + lib/expat.h | 15 +- > > + lib/internal.h | 5 + > > + lib/libexpat.def.cmake | 3 + > > + lib/xmlparse.c | 337 +++++++++++++++++++++++++++++++++++++++- > - > > + tests/basic_tests.c | 4 + > > + tests/nsalloc_tests.c | 5 + > > + xmlwf/xmlwf.c | 2 + > > + xmlwf/xmlwf_helpgen.py | 2 + > > + 8 files changed, 361 insertions(+), 12 deletions(-) > > + > > +diff --git a/lib/expat.h b/lib/expat.h > > +index 610e1ddc..66a253c1 100644 > > +--- a/lib/expat.h > > ++++ b/lib/expat.h > > +@@ -1032,7 +1032,10 @@ enum XML_FeatureEnum { > > + > XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFIC > ATION_DEFAULT, > > + > XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRES > HOLD_DEFAULT, > > + /* Added in Expat 2.6.0. */ > > +- XML_FEATURE_GE > > ++ XML_FEATURE_GE, > > ++ /* Added in Expat 2.7.2. */ > > ++ XML_FEATURE_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT, > > ++ XML_FEATURE_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT, > > + /* Additional features must be added to the end of this enum. */ > > + }; > > + > > +@@ -1057,6 +1060,16 @@ > XML_SetBillionLaughsAttackProtectionMaximumAmplification( > > + XMLPARSEAPI(XML_Bool) > > + XML_SetBillionLaughsAttackProtectionActivationThreshold( > > + XML_Parser parser, unsigned long long activationThresholdBytes); > > ++ > > ++/* Added in Expat 2.7.2. */ > > ++XMLPARSEAPI(XML_Bool) > > ++XML_SetAllocTrackerMaximumAmplification(XML_Parser parser, > > ++ float maximumAmplificationFactor); > > ++ > > ++/* Added in Expat 2.7.2. */ > > ++XMLPARSEAPI(XML_Bool) > > ++XML_SetAllocTrackerActivationThreshold( > > ++ XML_Parser parser, unsigned long long activationThresholdBytes); > > + #endif > > + > > + /* Added in Expat 2.6.0. */ > > +diff --git a/lib/internal.h b/lib/internal.h > > +index 6bde6ae6..eb67cf50 100644 > > +--- a/lib/internal.h > > ++++ b/lib/internal.h > > +@@ -145,6 +145,11 @@ > > + 100.0f > > + #define > EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_D > EFAULT \ > > + 8388608 // 8 MiB, 2^23 > > ++ > > ++#define EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT > 100.0f > > ++#define EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT > \ > > ++ 67108864 // 64 MiB, 2^26 > > ++ > > + /* NOTE END */ > > + > > + #include "expat.h" // so we can use type XML_Parser below > > +diff --git a/lib/libexpat.def.cmake b/lib/libexpat.def.cmake > > +index 10ee9cd6..7a3a7ec0 100644 > > +--- a/lib/libexpat.def.cmake > > ++++ b/lib/libexpat.def.cmake > > +@@ -79,3 +79,6 @@ EXPORTS > > + @_EXPAT_COMMENT_DTD_OR_GE@ > XML_SetBillionLaughsAttackProtectionMaximumAmplification @70 > > + ; added with version 2.6.0 > > + XML_SetReparseDeferralEnabled @71 > > ++; added with version 2.7.2 > > ++@_EXPAT_COMMENT_DTD_OR_GE@ > XML_SetAllocTrackerMaximumAmplification @72 > > ++@_EXPAT_COMMENT_DTD_OR_GE@ > XML_SetAllocTrackerActivationThreshold @73 > > +diff --git a/lib/xmlparse.c b/lib/xmlparse.c > > +index 9f0a8b3e..fcf1cfdd 100644 > > +--- a/lib/xmlparse.c > > ++++ b/lib/xmlparse.c > > +@@ -452,6 +452,14 @@ typedef struct accounting { > > + unsigned long long activationThresholdBytes; > > + } ACCOUNTING; > > + > > ++typedef struct MALLOC_TRACKER { > > ++ XmlBigCount bytesAllocated; > > ++ XmlBigCount peakBytesAllocated; // updated live only for debug level >=2 > > ++ unsigned long debugLevel; > > ++ float maximumAmplificationFactor; // >=1.0 > > ++ XmlBigCount activationThresholdBytes; > > ++} MALLOC_TRACKER; > > ++ > > + typedef struct entity_stats { > > + unsigned int countEverOpened; > > + unsigned int currentDepth; > > +@@ -599,7 +607,8 @@ static XML_Bool startParsing(XML_Parser parser); > > + > > + static XML_Parser parserCreate(const XML_Char *encodingName, > > + const XML_Memory_Handling_Suite *memsuite, > > +- const XML_Char *nameSep, DTD *dtd); > > ++ const XML_Char *nameSep, DTD *dtd, > > ++ XML_Parser parentParser); > > + > > + static void parserInit(XML_Parser parser, const XML_Char *encodingName); > > + > > +@@ -769,14 +778,220 @@ struct XML_ParserStruct { > > + unsigned long m_hash_secret_salt; > > + #if XML_GE == 1 > > + ACCOUNTING m_accounting; > > ++ MALLOC_TRACKER m_alloc_tracker; > > + ENTITY_STATS m_entity_stats; > > + #endif > > + XML_Bool m_reenter; > > + }; > > + > > +-#define MALLOC(parser, s) (parser->m_mem.malloc_fcn((s))) > > +-#define REALLOC(parser, p, s) (parser->m_mem.realloc_fcn((p), (s))) > > +-#define FREE(parser, p) (parser->m_mem.free_fcn((p))) > > ++#if XML_GE == 1 > > ++# define MALLOC(parser, s) (expat_malloc((parser), (s), __LINE__)) > > ++# define REALLOC(parser, p, s) (expat_realloc((parser), (p), (s), __LINE__)) > > ++# define FREE(parser, p) (expat_free((parser), (p), __LINE__)) > > ++#else > > ++# define MALLOC(parser, s) (parser->m_mem.malloc_fcn((s))) > > ++# define REALLOC(parser, p, s) (parser->m_mem.realloc_fcn((p), (s))) > > ++# define FREE(parser, p) (parser->m_mem.free_fcn((p))) > > ++#endif > > ++ > > ++#if XML_GE == 1 > > ++static void > > ++expat_heap_stat(XML_Parser rootParser, char operator, XmlBigCount > absDiff, > > ++ XmlBigCount newTotal, XmlBigCount peakTotal, int sourceLine) { > > ++ // NOTE: This can be +infinity or -nan > > ++ const float amplification > > ++ = (float)newTotal / (float)rootParser->m_accounting.countBytesDirect; > > ++ fprintf( > > ++ stderr, > > ++ "expat: Allocations(%p): Direct " EXPAT_FMT_ULL("10") ", allocated %c" > EXPAT_FMT_ULL( > > ++ "10") " to " EXPAT_FMT_ULL("10") " (" EXPAT_FMT_ULL("10") " peak), > amplification %8.2f (xmlparse.c:%d)\n", > > ++ (void *)rootParser, rootParser->m_accounting.countBytesDirect, operator, > > ++ absDiff, newTotal, peakTotal, (double)amplification, sourceLine); > > ++} > > ++ > > ++static bool > > ++expat_heap_increase_tolerable(XML_Parser rootParser, XmlBigCount > increase, > > ++ int sourceLine) { > > ++ assert(rootParser != NULL); > > ++ assert(increase > 0); > > ++ > > ++ XmlBigCount newTotal = 0; > > ++ bool tolerable = true; > > ++ > > ++ // Detect integer overflow > > ++ if ((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated < increase) > { > > ++ tolerable = false; > > ++ } else { > > ++ newTotal = rootParser->m_alloc_tracker.bytesAllocated + increase; > > ++ > > ++ if (newTotal >= rootParser->m_alloc_tracker.activationThresholdBytes) { > > ++ assert(newTotal > 0); > > ++ // NOTE: This can be +infinity when dividing by zero but not -nan > > ++ const float amplification > > ++ = (float)newTotal / (float)rootParser->m_accounting.countBytesDirect; > > ++ if (amplification > > ++ > rootParser->m_alloc_tracker.maximumAmplificationFactor) { > > ++ tolerable = false; > > ++ } > > ++ } > > ++ } > > ++ > > ++ if (! tolerable && (rootParser->m_alloc_tracker.debugLevel >= 1)) { > > ++ expat_heap_stat(rootParser, '+', increase, newTotal, newTotal, sourceLine); > > ++ } > > ++ > > ++ return tolerable; > > ++} > > ++ > > ++static void * > > ++expat_malloc(XML_Parser parser, size_t size, int sourceLine) { > > ++ // Detect integer overflow > > ++ if (SIZE_MAX - size < sizeof(size_t)) { > > ++ return NULL; > > ++ } > > ++ > > ++ const XML_Parser rootParser = getRootParserOf(parser, NULL); > > ++ assert(rootParser->m_parentParser == NULL); > > ++ > > ++ const size_t bytesToAllocate = sizeof(size_t) + size; > > ++ > > ++ if ((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated > > ++ < bytesToAllocate) { > > ++ return NULL; // i.e. signal integer overflow as out-of-memory > > ++ } > > ++ > > ++ if (! expat_heap_increase_tolerable(rootParser, bytesToAllocate, > > ++ sourceLine)) { > > ++ return NULL; // i.e. signal violation as out-of-memory > > ++ } > > ++ > > ++ // Actually allocate > > ++ void *const mallocedPtr = parser->m_mem.malloc_fcn(bytesToAllocate); > > ++ > > ++ if (mallocedPtr == NULL) { > > ++ return NULL; > > ++ } > > ++ > > ++ // Update in-block recorded size > > ++ *(size_t *)mallocedPtr = size; > > ++ > > ++ // Update accounting > > ++ rootParser->m_alloc_tracker.bytesAllocated += bytesToAllocate; > > ++ > > ++ // Report as needed > > ++ if (rootParser->m_alloc_tracker.debugLevel >= 2) { > > ++ if (rootParser->m_alloc_tracker.bytesAllocated > > ++ > rootParser->m_alloc_tracker.peakBytesAllocated) { > > ++ rootParser->m_alloc_tracker.peakBytesAllocated > > ++ = rootParser->m_alloc_tracker.bytesAllocated; > > ++ } > > ++ expat_heap_stat(rootParser, '+', bytesToAllocate, > > ++ rootParser->m_alloc_tracker.bytesAllocated, > > ++ rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine); > > ++ } > > ++ > > ++ return (char *)mallocedPtr + sizeof(size_t); > > ++} > > ++ > > ++static void > > ++expat_free(XML_Parser parser, void *ptr, int sourceLine) { > > ++ assert(parser != NULL); > > ++ > > ++ if (ptr == NULL) { > > ++ return; > > ++ } > > ++ > > ++ const XML_Parser rootParser = getRootParserOf(parser, NULL); > > ++ assert(rootParser->m_parentParser == NULL); > > ++ > > ++ // Extract size (to the eyes of malloc_fcn/realloc_fcn) and > > ++ // the original pointer returned by malloc/realloc > > ++ void *const mallocedPtr = (char *)ptr - sizeof(size_t); > > ++ const size_t bytesAllocated = sizeof(size_t) + *(size_t *)mallocedPtr; > > ++ > > ++ // Update accounting > > ++ assert(rootParser->m_alloc_tracker.bytesAllocated >= bytesAllocated); > > ++ rootParser->m_alloc_tracker.bytesAllocated -= bytesAllocated; > > ++ > > ++ // Report as needed > > ++ if (rootParser->m_alloc_tracker.debugLevel >= 2) { > > ++ expat_heap_stat(rootParser, '-', bytesAllocated, > > ++ rootParser->m_alloc_tracker.bytesAllocated, > > ++ rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine); > > ++ } > > ++ > > ++ // NOTE: This may be freeing rootParser, so freeing has to come last > > ++ parser->m_mem.free_fcn(mallocedPtr); > > ++} > > ++ > > ++static void * > > ++expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine) { > > ++ assert(parser != NULL); > > ++ > > ++ if (ptr == NULL) { > > ++ return expat_malloc(parser, size, sourceLine); > > ++ } > > ++ > > ++ if (size == 0) { > > ++ expat_free(parser, ptr, sourceLine); > > ++ return NULL; > > ++ } > > ++ > > ++ const XML_Parser rootParser = getRootParserOf(parser, NULL); > > ++ assert(rootParser->m_parentParser == NULL); > > ++ > > ++ // Extract original size (to the eyes of the caller) and the original > > ++ // pointer returned by malloc/realloc > > ++ void *mallocedPtr = (char *)ptr - sizeof(size_t); > > ++ const size_t prevSize = *(size_t *)mallocedPtr; > > ++ > > ++ // Classify upcoming change > > ++ const bool isIncrease = (size > prevSize); > > ++ const size_t absDiff > > ++ = (size > prevSize) ? (size - prevSize) : (prevSize - size); > > ++ > > ++ // Ask for permission from accounting > > ++ if (isIncrease) { > > ++ if (! expat_heap_increase_tolerable(rootParser, absDiff, sourceLine)) { > > ++ return NULL; // i.e. signal violation as out-of-memory > > ++ } > > ++ } > > ++ > > ++ // Actually allocate > > ++ mallocedPtr = parser->m_mem.realloc_fcn(mallocedPtr, sizeof(size_t) + > size); > > ++ > > ++ if (mallocedPtr == NULL) { > > ++ return NULL; > > ++ } > > ++ > > ++ // Update accounting > > ++ if (isIncrease) { > > ++ assert((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated > > ++ >= absDiff); > > ++ rootParser->m_alloc_tracker.bytesAllocated += absDiff; > > ++ } else { // i.e. decrease > > ++ assert(rootParser->m_alloc_tracker.bytesAllocated >= absDiff); > > ++ rootParser->m_alloc_tracker.bytesAllocated -= absDiff; > > ++ } > > ++ > > ++ // Report as needed > > ++ if (rootParser->m_alloc_tracker.debugLevel >= 2) { > > ++ if (rootParser->m_alloc_tracker.bytesAllocated > > ++ > rootParser->m_alloc_tracker.peakBytesAllocated) { > > ++ rootParser->m_alloc_tracker.peakBytesAllocated > > ++ = rootParser->m_alloc_tracker.bytesAllocated; > > ++ } > > ++ expat_heap_stat(rootParser, isIncrease ? '+' : '-', absDiff, > > ++ rootParser->m_alloc_tracker.bytesAllocated, > > ++ rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine); > > ++ } > > ++ > > ++ // Update in-block recorded size > > ++ *(size_t *)mallocedPtr = size; > > ++ > > ++ return (char *)mallocedPtr + sizeof(size_t); > > ++} > > ++#endif // XML_GE == 1 > > + > > + XML_Parser XMLCALL > > + XML_ParserCreate(const XML_Char *encodingName) { > > +@@ -1096,19 +1311,40 @@ XML_Parser XMLCALL > > + XML_ParserCreate_MM(const XML_Char *encodingName, > > + const XML_Memory_Handling_Suite *memsuite, > > + const XML_Char *nameSep) { > > +- return parserCreate(encodingName, memsuite, nameSep, NULL); > > ++ return parserCreate(encodingName, memsuite, nameSep, NULL, NULL); > > + } > > + > > + static XML_Parser > > + parserCreate(const XML_Char *encodingName, > > + const XML_Memory_Handling_Suite *memsuite, const XML_Char > *nameSep, > > +- DTD *dtd) { > > +- XML_Parser parser; > > ++ DTD *dtd, XML_Parser parentParser) { > > ++ XML_Parser parser = NULL; > > ++ > > ++#if XML_GE == 1 > > ++ const size_t increase = sizeof(size_t) + sizeof(struct XML_ParserStruct); > > ++ > > ++ if (parentParser != NULL) { > > ++ const XML_Parser rootParser = getRootParserOf(parentParser, NULL); > > ++ if (! expat_heap_increase_tolerable(rootParser, increase, __LINE__)) { > > ++ return NULL; > > ++ } > > ++ } > > ++#else > > ++ UNUSED_P(parentParser); > > ++#endif > > + > > + if (memsuite) { > > + XML_Memory_Handling_Suite *mtemp; > > ++#if XML_GE == 1 > > ++ void *const sizeAndParser = memsuite->malloc_fcn( > > ++ sizeof(size_t) + sizeof(struct XML_ParserStruct)); > > ++ if (sizeAndParser != NULL) { > > ++ *(size_t *)sizeAndParser = sizeof(struct XML_ParserStruct); > > ++ parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t)); > > ++#else > > + parser = memsuite->malloc_fcn(sizeof(struct XML_ParserStruct)); > > + if (parser != NULL) { > > ++#endif > > + mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem); > > + mtemp->malloc_fcn = memsuite->malloc_fcn; > > + mtemp->realloc_fcn = memsuite->realloc_fcn; > > +@@ -1116,18 +1352,67 @@ parserCreate(const XML_Char *encodingName, > > + } > > + } else { > > + XML_Memory_Handling_Suite *mtemp; > > ++#if XML_GE == 1 > > ++ void *const sizeAndParser > > ++ = (XML_Parser)malloc(sizeof(size_t) + sizeof(struct XML_ParserStruct)); > > ++ if (sizeAndParser != NULL) { > > ++ *(size_t *)sizeAndParser = sizeof(struct XML_ParserStruct); > > ++ parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t)); > > ++#else > > + parser = (XML_Parser)malloc(sizeof(struct XML_ParserStruct)); > > + if (parser != NULL) { > > ++#endif > > + mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem); > > + mtemp->malloc_fcn = malloc; > > + mtemp->realloc_fcn = realloc; > > + mtemp->free_fcn = free; > > + } > > +- } > > ++ } // cppcheck-suppress[memleak symbolName=sizeAndParser] // Cppcheck > >=2.18.0 > > + > > + if (! parser) > > + return parser; > > + > > ++#if XML_GE == 1 > > ++ // Initialize .m_alloc_tracker > > ++ memset(&parser->m_alloc_tracker, 0, sizeof(MALLOC_TRACKER)); > > ++ if (parentParser == NULL) { > > ++ parser->m_alloc_tracker.debugLevel > > ++ = getDebugLevel("EXPAT_MALLOC_DEBUG", 0u); > > ++ parser->m_alloc_tracker.maximumAmplificationFactor > > ++ = EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT; > > ++ parser->m_alloc_tracker.activationThresholdBytes > > ++ = EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT; > > ++ > > ++ // NOTE: This initialization needs to come this early because these fields > > ++ // are read by allocation tracking code > > ++ parser->m_parentParser = NULL; > > ++ parser->m_accounting.countBytesDirect = 0; > > ++ } else { > > ++ parser->m_parentParser = parentParser; > > ++ } > > ++ > > ++ // Record XML_ParserStruct allocation we did a few lines up before > > ++ const XML_Parser rootParser = getRootParserOf(parser, NULL); > > ++ assert(rootParser->m_parentParser == NULL); > > ++ assert(SIZE_MAX - rootParser->m_alloc_tracker.bytesAllocated >= > increase); > > ++ rootParser->m_alloc_tracker.bytesAllocated += increase; > > ++ > > ++ // Report on allocation > > ++ if (rootParser->m_alloc_tracker.debugLevel >= 2) { > > ++ if (rootParser->m_alloc_tracker.bytesAllocated > > ++ > rootParser->m_alloc_tracker.peakBytesAllocated) { > > ++ rootParser->m_alloc_tracker.peakBytesAllocated > > ++ = rootParser->m_alloc_tracker.bytesAllocated; > > ++ } > > ++ > > ++ expat_heap_stat(rootParser, '+', increase, > > ++ rootParser->m_alloc_tracker.bytesAllocated, > > ++ rootParser->m_alloc_tracker.peakBytesAllocated, __LINE__); > > ++ } > > ++#else > > ++ parser->m_parentParser = NULL; > > ++#endif // XML_GE == 1 > > ++ > > + parser->m_buffer = NULL; > > + parser->m_bufferLim = NULL; > > + > > +@@ -1291,7 +1576,6 @@ parserInit(XML_Parser parser, const XML_Char > *encodingName) { > > + parser->m_unknownEncodingMem = NULL; > > + parser->m_unknownEncodingRelease = NULL; > > + parser->m_unknownEncodingData = NULL; > > +- parser->m_parentParser = NULL; > > + parser->m_parsingStatus.parsing = XML_INITIALIZED; > > + // Reentry can only be triggered inside m_processor calls > > + parser->m_reenter = XML_FALSE; > > +@@ -1526,9 +1810,10 @@ XML_ExternalEntityParserCreate(XML_Parser > oldParser, const XML_Char *context, > > + */ > > + if (parser->m_ns) { > > + XML_Char tmp[2] = {parser->m_namespaceSeparator, 0}; > > +- parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd); > > ++ parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd, > oldParser); > > + } else { > > +- parser = parserCreate(encodingName, &parser->m_mem, NULL, newDtd); > > ++ parser > > ++ = parserCreate(encodingName, &parser->m_mem, NULL, newDtd, > oldParser); > > + } > > + > > + if (! parser) > > +@@ -2708,6 +2993,13 @@ XML_GetFeatureList(void) { > > + > EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_D > EFAULT}, > > + /* Added in Expat 2.6.0. */ > > + {XML_FEATURE_GE, XML_L("XML_GE"), 0}, > > ++ /* Added in Expat 2.7.2. */ > > ++ > {XML_FEATURE_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT, > > ++ XML_L("XML_AT_MAX_AMP"), > > ++ (long > int)EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT}, > > ++ > {XML_FEATURE_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT, > > ++ XML_L("XML_AT_ACT_THRES"), > > ++ (long > int)EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT}, > > + #endif > > + {XML_FEATURE_END, NULL, 0}}; > > + > > +@@ -2736,6 +3028,29 @@ > XML_SetBillionLaughsAttackProtectionActivationThreshold( > > + parser->m_accounting.activationThresholdBytes = activationThresholdBytes; > > + return XML_TRUE; > > + } > > ++ > > ++XML_Bool XMLCALL > > ++XML_SetAllocTrackerMaximumAmplification(XML_Parser parser, > > ++ float maximumAmplificationFactor) { > > ++ if ((parser == NULL) || (parser->m_parentParser != NULL) > > ++ || isnan(maximumAmplificationFactor) > > ++ || (maximumAmplificationFactor < 1.0f)) { > > ++ return XML_FALSE; > > ++ } > > ++ parser->m_alloc_tracker.maximumAmplificationFactor > > ++ = maximumAmplificationFactor; > > ++ return XML_TRUE; > > ++} > > ++ > > ++XML_Bool XMLCALL > > ++XML_SetAllocTrackerActivationThreshold( > > ++ XML_Parser parser, unsigned long long activationThresholdBytes) { > > ++ if ((parser == NULL) || (parser->m_parentParser != NULL)) { > > ++ return XML_FALSE; > > ++ } > > ++ parser->m_alloc_tracker.activationThresholdBytes = > activationThresholdBytes; > > ++ return XML_TRUE; > > ++} > > + #endif /* XML_GE == 1 */ > > + > > + XML_Bool XMLCALL > > +diff --git a/tests/basic_tests.c b/tests/basic_tests.c > > +index 129db1d8..0231e094 100644 > > +--- a/tests/basic_tests.c > > ++++ b/tests/basic_tests.c > > +@@ -3089,6 +3089,10 @@ START_TEST(test_buffer_can_grow_to_max) { > > + for (int i = 0; i < num_prefixes; ++i) { > > + set_subtest("\"%s\"", prefixes[i]); > > + XML_Parser parser = XML_ParserCreate(NULL); > > ++#if XML_GE == 1 > > ++ assert_true(XML_SetAllocTrackerActivationThreshold(parser, (size_t)-1) > > ++ == XML_TRUE); // i.e. deactivate > > ++#endif > > + const int prefix_len = (int)strlen(prefixes[i]); > > + const enum XML_Status s > > + = _XML_Parse_SINGLE_BYTES(parser, prefixes[i], prefix_len, > XML_FALSE); > > +diff --git a/tests/nsalloc_tests.c b/tests/nsalloc_tests.c > > +index 48520f42..0a594e14 100644 > > +--- a/tests/nsalloc_tests.c > > ++++ b/tests/nsalloc_tests.c > > +@@ -454,10 +454,15 @@ START_TEST(test_nsalloc_realloc_attributes) { > > + nsalloc_teardown(); > > + nsalloc_setup(); > > + } > > ++#if XML_GE == 1 > > ++ assert_true( > > ++ i == 0); // because expat_realloc relies on expat_malloc to some extent > > ++#else > > + if (i == 0) > > + fail("Parsing worked despite failing reallocations"); > > + else if (i == max_realloc_count) > > + fail("Parsing failed at max reallocation count"); > > ++#endif > > + } > > + END_TEST > > + > > +diff --git a/xmlwf/xmlwf.c b/xmlwf/xmlwf.c > > +index 8cfc73ca..b9d0a7fc 100644 > > +--- a/xmlwf/xmlwf.c > > ++++ b/xmlwf/xmlwf.c > > +@@ -933,6 +933,8 @@ usage(const XML_Char *prog, int rc) { > > + T(" Control verbosity of entity debugging (default: 0)\n") > > + T(" EXPAT_ENTROPY_DEBUG=(0|1)\n") > > + T(" Control verbosity of entropy debugging (default: 0)\n") > > ++ T(" EXPAT_MALLOC_DEBUG=(0|1|2)\n") > > ++ T(" Control verbosity of allocation tracker (default: 0)\n") > > + T("\n") > > + T("exit status:\n") > > + T(" 0 the input files are well-formed and the output (if requested) > was written successfully\n") > > +diff --git a/xmlwf/xmlwf_helpgen.py b/xmlwf/xmlwf_helpgen.py > > +index 39a3dc13..2360820d 100755 > > +--- a/xmlwf/xmlwf_helpgen.py > > ++++ b/xmlwf/xmlwf_helpgen.py > > +@@ -39,6 +39,8 @@ environment variables: > > + Control verbosity of entity debugging (default: 0) > > + EXPAT_ENTROPY_DEBUG=(0|1) > > + Control verbosity of entropy debugging (default: 0) > > ++ EXPAT_MALLOC_DEBUG=(0|1|2) > > ++ Control verbosity of allocation tracker (default: 0) > > + > > + exit status: > > + 0 the input files are well-formed and the output (if requested) was > written successfully > > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-09.patch > b/meta/recipes-core/expat/expat/CVE-2025-59375-09.patch > > new file mode 100644 > > index 00000000000..364c28183ac > > --- /dev/null > > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-09.patch > > @@ -0,0 +1,43 @@ > > +From 1270e5bc0836d296ac4970fc9e1cf53d83972083 Mon Sep 17 00:00:00 > 2001 > > +From: Sebastian Pipping <sebastian@pipping.org> > > +Date: Sun, 7 Sep 2025 12:18:08 +0200 > > +Subject: [PATCH] lib: Make XML_MemFree and XML_FreeContentModel match > their > > + siblings > > + > > +.. XML_MemMalloc and XML_MemRealloc in structure, prior to upcoming > changes > > + > > +CVE: CVE-2025-59375 > > +Upstream-Status: Backport > [https://github.com/libexpat/libexpat/commit/1270e5bc0836d296ac4970fc9e1cf53d > 83972083] > > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > > +--- > > + lib/xmlparse.c | 10 ++++++---- > > + 1 file changed, 6 insertions(+), 4 deletions(-) > > + > > +diff --git a/lib/xmlparse.c b/lib/xmlparse.c > > +index fcf1cfdd..5d27cd45 100644 > > +--- a/lib/xmlparse.c > > ++++ b/lib/xmlparse.c > > +@@ -2772,8 +2772,9 @@ XML_GetCurrentColumnNumber(XML_Parser parser) > { > > + > > + void XMLCALL > > + XML_FreeContentModel(XML_Parser parser, XML_Content *model) { > > +- if (parser != NULL) > > +- FREE(parser, model); > > ++ if (parser == NULL) > > ++ return; > > ++ FREE(parser, model); > > + } > > + > > + void *XMLCALL > > +@@ -2792,8 +2793,9 @@ XML_MemRealloc(XML_Parser parser, void *ptr, > size_t size) { > > + > > + void XMLCALL > > + XML_MemFree(XML_Parser parser, void *ptr) { > > +- if (parser != NULL) > > +- FREE(parser, ptr); > > ++ if (parser == NULL) > > ++ return; > > ++ FREE(parser, ptr); > > + } > > + > > + void XMLCALL > > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-10.patch > b/meta/recipes-core/expat/expat/CVE-2025-59375-10.patch > > new file mode 100644 > > index 00000000000..fe5452000e1 > > --- /dev/null > > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-10.patch > > @@ -0,0 +1,54 @@ > > +From 96c7467281c72028aada525c1d3822512758b266 Mon Sep 17 00:00:00 > 2001 > > +From: Sebastian Pipping <sebastian@pipping.org> > > +Date: Sun, 7 Sep 2025 12:06:43 +0200 > > +Subject: [PATCH] lib: Exclude XML_Mem* functions from allocation tracking > > + > > +.. so that allocations by the user application > > +are not being limited. > > + > > +CVE: CVE-2025-59375 > > +Upstream-Status: Backport > [https://github.com/libexpat/libexpat/commit/96c7467281c72028aada525c1d382251 > 2758b266] > > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > > +--- > > + lib/xmlparse.c | 16 +++++++++++++--- > > + 1 file changed, 13 insertions(+), 3 deletions(-) > > + > > +diff --git a/lib/xmlparse.c b/lib/xmlparse.c > > +index 5d27cd45..8145a049 100644 > > +--- a/lib/xmlparse.c > > ++++ b/lib/xmlparse.c > > +@@ -2781,21 +2781,31 @@ void *XMLCALL > > + XML_MemMalloc(XML_Parser parser, size_t size) { > > + if (parser == NULL) > > + return NULL; > > +- return MALLOC(parser, size); > > ++ > > ++ // NOTE: We are avoiding MALLOC(..) here to not include > > ++ // user allocations with allocation tracking and limiting. > > ++ return parser->m_mem.malloc_fcn(size); > > + } > > + > > + void *XMLCALL > > + XML_MemRealloc(XML_Parser parser, void *ptr, size_t size) { > > + if (parser == NULL) > > + return NULL; > > +- return REALLOC(parser, ptr, size); > > ++ > > ++ // NOTE: We are avoiding REALLOC(..) here to not include > > ++ // user allocations with allocation tracking and limiting. > > ++ return parser->m_mem.realloc_fcn(ptr, size); > > + } > > + > > + void XMLCALL > > + XML_MemFree(XML_Parser parser, void *ptr) { > > + if (parser == NULL) > > + return; > > +- FREE(parser, ptr); > > ++ > > ++ // NOTE: We are avoiding FREE(..) here because XML_MemMalloc and > > ++ // XML_MemRealloc are not using MALLOC(..) and REALLOC(..) > > ++ // but plain .malloc_fcn(..) and .realloc_fcn(..), internally. > > ++ parser->m_mem.free_fcn(ptr); > > + } > > + > > + void XMLCALL > > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-11.patch > b/meta/recipes-core/expat/expat/CVE-2025-59375-11.patch > > new file mode 100644 > > index 00000000000..be892a78046 > > --- /dev/null > > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-11.patch > > @@ -0,0 +1,66 @@ > > +From ae4086198d710a62a0a1560007b81307dba72909 Mon Sep 17 00:00:00 > 2001 > > +From: Sebastian Pipping <sebastian@pipping.org> > > +Date: Tue, 9 Sep 2025 21:34:28 +0200 > > +Subject: [PATCH] lib: Exclude the main input buffer from allocation tracking > > + > > +.. so that control of the input buffer size remains with the > > +application using Expat > > + > > +CVE: CVE-2025-59375 > > +Upstream-Status: Backport > [https://github.com/libexpat/libexpat/commit/ae4086198d710a62a0a1560007b81307 > dba72909] > > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > > +--- > > + lib/xmlparse.c | 19 +++++++++++++++---- > > + 1 file changed, 15 insertions(+), 4 deletions(-) > > + > > +diff --git a/lib/xmlparse.c b/lib/xmlparse.c > > +index 8145a049..00139b94 100644 > > +--- a/lib/xmlparse.c > > ++++ b/lib/xmlparse.c > > +@@ -1975,7 +1975,10 @@ XML_ParserFree(XML_Parser parser) { > > + FREE(parser, (void *)parser->m_attInfo); > > + #endif > > + FREE(parser, parser->m_groupConnector); > > +- FREE(parser, parser->m_buffer); > > ++ // NOTE: We are avoiding FREE(..) here because parser->m_buffer > > ++ // is not being allocated with MALLOC(..) but with plain > > ++ // .malloc_fcn(..). > > ++ parser->m_mem.free_fcn(parser->m_buffer); > > + FREE(parser, parser->m_dataBuf); > > + FREE(parser, parser->m_nsAtts); > > + FREE(parser, parser->m_unknownEncodingMem); > > +@@ -2567,7 +2570,9 @@ XML_GetBuffer(XML_Parser parser, int len) { > > + parser->m_errorCode = XML_ERROR_NO_MEMORY; > > + return NULL; > > + } > > +- newBuf = (char *)MALLOC(parser, bufferSize); > > ++ // NOTE: We are avoiding MALLOC(..) here to leave limiting > > ++ // the input size to the application using Expat. > > ++ newBuf = (char *)parser->m_mem.malloc_fcn(bufferSize); > > + if (newBuf == 0) { > > + parser->m_errorCode = XML_ERROR_NO_MEMORY; > > + return NULL; > > +@@ -2578,7 +2583,10 @@ XML_GetBuffer(XML_Parser parser, int len) { > > + memcpy(newBuf, &parser->m_bufferPtr[-keep], > > + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser- > >m_bufferPtr) > > + + keep); > > +- FREE(parser, parser->m_buffer); > > ++ // NOTE: We are avoiding FREE(..) here because parser->m_buffer > > ++ // is not being allocated with MALLOC(..) but with plain > > ++ // .malloc_fcn(..). > > ++ parser->m_mem.free_fcn(parser->m_buffer); > > + parser->m_buffer = newBuf; > > + parser->m_bufferEnd > > + = parser->m_buffer > > +@@ -2594,7 +2602,10 @@ XML_GetBuffer(XML_Parser parser, int len) { > > + if (parser->m_bufferPtr) { > > + memcpy(newBuf, parser->m_bufferPtr, > > + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser- > >m_bufferPtr)); > > +- FREE(parser, parser->m_buffer); > > ++ // NOTE: We are avoiding FREE(..) here because parser->m_buffer > > ++ // is not being allocated with MALLOC(..) but with plain > > ++ // .malloc_fcn(..). > > ++ parser->m_mem.free_fcn(parser->m_buffer); > > + parser->m_bufferEnd > > + = newBuf > > + + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser- > >m_bufferPtr); > > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-12.patch > b/meta/recipes-core/expat/expat/CVE-2025-59375-12.patch > > new file mode 100644 > > index 00000000000..9e036a52849 > > --- /dev/null > > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-12.patch > > @@ -0,0 +1,58 @@ > > +From 7e35240dc97e9fd4f609e31f27c27b659535e436 Mon Sep 17 00:00:00 > 2001 > > +From: Sebastian Pipping <sebastian@pipping.org> > > +Date: Thu, 11 Sep 2025 00:27:05 +0200 > > +Subject: [PATCH] lib: Exclude the content model from allocation tracking > > + > > +.. so that applications that are not using XML_FreeContentModel > > +but plain free(..) or .free_fcn() to free the content model's > > +memory are safe > > + > > +CVE: CVE-2025-59375 > > +Upstream-Status: Backport > [https://github.com/libexpat/libexpat/commit/7e35240dc97e9fd4f609e31f27c27b65 > 9535e436] > > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > > +--- > > + lib/xmlparse.c | 16 +++++++++++++--- > > + 1 file changed, 13 insertions(+), 3 deletions(-) > > + > > +diff --git a/lib/xmlparse.c b/lib/xmlparse.c > > +index 00139b94..d0b6e0cd 100644 > > +--- a/lib/xmlparse.c > > ++++ b/lib/xmlparse.c > > +@@ -2785,7 +2785,10 @@ void XMLCALL > > + XML_FreeContentModel(XML_Parser parser, XML_Content *model) { > > + if (parser == NULL) > > + return; > > +- FREE(parser, model); > > ++ > > ++ // NOTE: We are avoiding FREE(..) here because the content model > > ++ // has been created using plain .malloc_fcn(..) rather than MALLOC(..). > > ++ parser->m_mem.free_fcn(model); > > + } > > + > > + void *XMLCALL > > +@@ -6063,8 +6066,12 @@ doProlog(XML_Parser parser, const ENCODING > *enc, const char *s, const char *end, > > + case XML_ROLE_CONTENT_EMPTY: > > + if (dtd->in_eldecl) { > > + if (parser->m_elementDeclHandler) { > > ++ // NOTE: We are avoiding MALLOC(..) here to so that > > ++ // applications that are not using XML_FreeContentModel but > > ++ // plain free(..) or .free_fcn() to free the content model's > > ++ // memory are safe. > > + XML_Content *content > > +- = (XML_Content *)MALLOC(parser, sizeof(XML_Content)); > > ++ = (XML_Content *)parser- > >m_mem.malloc_fcn(sizeof(XML_Content)); > > + if (! content) > > + return XML_ERROR_NO_MEMORY; > > + content->quant = XML_CQUANT_NONE; > > +@@ -8278,7 +8285,10 @@ build_model(XML_Parser parser) { > > + const size_t allocsize = (dtd->scaffCount * sizeof(XML_Content) > > + + (dtd->contentStringLen * sizeof(XML_Char))); > > + > > +- ret = (XML_Content *)MALLOC(parser, allocsize); > > ++ // NOTE: We are avoiding MALLOC(..) here to so that > > ++ // applications that are not using XML_FreeContentModel but plain > > ++ // free(..) or .free_fcn() to free the content model's memory are safe. > > ++ ret = (XML_Content *)parser->m_mem.malloc_fcn(allocsize); > > + if (! ret) > > + return NULL; > > + > > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-13.patch > b/meta/recipes-core/expat/expat/CVE-2025-59375-13.patch > > new file mode 100644 > > index 00000000000..209dd83a4b6 > > --- /dev/null > > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-13.patch > > @@ -0,0 +1,309 @@ > > +From 31f9053c3c46741f4daf2ea2bdea75f40f720d42 Mon Sep 17 00:00:00 2001 > > +From: Sebastian Pipping <sebastian@pipping.org> > > +Date: Tue, 2 Sep 2025 22:36:49 +0200 > > +Subject: [PATCH] tests: Cover allocation tracking and limiting with tests > > + > > +CVE: CVE-2025-59375 > > +Upstream-Status: Backport > [https://github.com/libexpat/libexpat/commit/31f9053c3c46741f4daf2ea2bdea75f40 > f720d42] > > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > > +--- > > + lib/internal.h | 3 + > > + lib/xmlparse.c | 12 +++ > > + tests/alloc_tests.c | 214 > ++++++++++++++++++++++++++++++++++++++++++++ > > + 3 files changed, 229 insertions(+) > > + > > +diff --git a/lib/internal.h b/lib/internal.h > > +index eb67cf50..6e087858 100644 > > +--- a/lib/internal.h > > ++++ b/lib/internal.h > > +@@ -173,6 +173,9 @@ extern > > + #endif > > + XML_Bool g_reparseDeferralEnabledDefault; // written ONLY in runtests.c > > + #if defined(XML_TESTING) > > ++void *expat_malloc(XML_Parser parser, size_t size, int sourceLine); > > ++void expat_free(XML_Parser parser, void *ptr, int sourceLine); > > ++void *expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine); > > + extern unsigned int g_bytesScanned; // used for testing only > > + #endif > > + > > +diff --git a/lib/xmlparse.c b/lib/xmlparse.c > > +index d0b6e0cd..6e9c6fb2 100644 > > +--- a/lib/xmlparse.c > > ++++ b/lib/xmlparse.c > > +@@ -843,7 +843,11 @@ expat_heap_increase_tolerable(XML_Parser > rootParser, XmlBigCount increase, > > + return tolerable; > > + } > > + > > ++# if defined(XML_TESTING) > > ++void * > > ++# else > > + static void * > > ++# endif > > + expat_malloc(XML_Parser parser, size_t size, int sourceLine) { > > + // Detect integer overflow > > + if (SIZE_MAX - size < sizeof(size_t)) { > > +@@ -893,7 +897,11 @@ expat_malloc(XML_Parser parser, size_t size, int > sourceLine) { > > + return (char *)mallocedPtr + sizeof(size_t); > > + } > > + > > ++# if defined(XML_TESTING) > > ++void > > ++# else > > + static void > > ++# endif > > + expat_free(XML_Parser parser, void *ptr, int sourceLine) { > > + assert(parser != NULL); > > + > > +@@ -924,7 +932,11 @@ expat_free(XML_Parser parser, void *ptr, int > sourceLine) { > > + parser->m_mem.free_fcn(mallocedPtr); > > + } > > + > > ++# if defined(XML_TESTING) > > ++void * > > ++# else > > + static void * > > ++# endif > > + expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine) { > > + assert(parser != NULL); > > + > > +diff --git a/tests/alloc_tests.c b/tests/alloc_tests.c > > +index 4c3e2af4..275f92d5 100644 > > +--- a/tests/alloc_tests.c > > ++++ b/tests/alloc_tests.c > > +@@ -46,10 +46,16 @@ > > + # undef NDEBUG /* because test suite relies on assert(...) at the moment */ > > + #endif > > + > > ++#include <math.h> /* NAN, INFINITY */ > > ++#include <stdbool.h> > > ++#include <stdint.h> /* for SIZE_MAX */ > > + #include <string.h> > > + #include <assert.h> > > + > > ++#include "expat_config.h" > > ++ > > + #include "expat.h" > > ++#include "internal.h" > > + #include "common.h" > > + #include "minicheck.h" > > + #include "dummy.h" > > +@@ -2085,6 +2091,203 @@ > START_TEST(test_alloc_reset_after_external_entity_parser_create_fail) { > > + } > > + END_TEST > > + > > ++START_TEST(test_alloc_tracker_size_recorded) { > > ++ XML_Memory_Handling_Suite memsuite = {malloc, realloc, free}; > > ++ > > ++ bool values[] = {true, false}; > > ++ for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++) { > > ++ const bool useMemSuite = values[i]; > > ++ set_subtest("useMemSuite=%d", (int)useMemSuite); > > ++ XML_Parser parser = useMemSuite > > ++ ? XML_ParserCreate_MM(NULL, &memsuite, XCS("|")) > > ++ : XML_ParserCreate(NULL); > > ++ > > ++#if XML_GE == 1 > > ++ void *ptr = expat_malloc(parser, 10, -1); > > ++ > > ++ assert_true(ptr != NULL); > > ++ assert_true(*((size_t *)ptr - 1) == 10); > > ++ > > ++ assert_true(expat_realloc(parser, ptr, SIZE_MAX / 2, -1) == NULL); > > ++ > > ++ assert_true(*((size_t *)ptr - 1) == 10); // i.e. unchanged > > ++ > > ++ ptr = expat_realloc(parser, ptr, 20, -1); > > ++ > > ++ assert_true(ptr != NULL); > > ++ assert_true(*((size_t *)ptr - 1) == 20); > > ++ > > ++ expat_free(parser, ptr, -1); > > ++#endif > > ++ > > ++ XML_ParserFree(parser); > > ++ } > > ++} > > ++END_TEST > > ++ > > ++START_TEST(test_alloc_tracker_maximum_amplification) { > > ++ if (g_reparseDeferralEnabledDefault == XML_TRUE) { > > ++ return; > > ++ } > > ++ > > ++ XML_Parser parser = XML_ParserCreate(NULL); > > ++ > > ++ // Get .m_accounting.countBytesDirect from 0 to 3 > > ++ const char *const chunk = "<e>"; > > ++ assert_true(_XML_Parse_SINGLE_BYTES(parser, chunk, (int)strlen(chunk), > > ++ /*isFinal=*/XML_FALSE) > > ++ == XML_STATUS_OK); > > ++ > > ++#if XML_GE == 1 > > ++ // Stop activation threshold from interfering > > ++ assert_true(XML_SetAllocTrackerActivationThreshold(parser, 0) == > XML_TRUE); > > ++ > > ++ // Exceed maximum amplification: should be rejected. > > ++ assert_true(expat_malloc(parser, 1000, -1) == NULL); > > ++ > > ++ // Increase maximum amplification, and try the same amount once more: > should > > ++ // work. > > ++ assert_true(XML_SetAllocTrackerMaximumAmplification(parser, 3000.0f) > > ++ == XML_TRUE); > > ++ > > ++ void *const ptr = expat_malloc(parser, 1000, -1); > > ++ assert_true(ptr != NULL); > > ++ expat_free(parser, ptr, -1); > > ++#endif > > ++ > > ++ XML_ParserFree(parser); > > ++} > > ++END_TEST > > ++ > > ++START_TEST(test_alloc_tracker_threshold) { > > ++ XML_Parser parser = XML_ParserCreate(NULL); > > ++ > > ++#if XML_GE == 1 > > ++ // Exceed maximum amplification *before* (default) threshold: should work. > > ++ void *const ptr = expat_malloc(parser, 1000, -1); > > ++ assert_true(ptr != NULL); > > ++ expat_free(parser, ptr, -1); > > ++ > > ++ // Exceed maximum amplification *after* threshold: should be rejected. > > ++ assert_true(XML_SetAllocTrackerActivationThreshold(parser, 999) == > XML_TRUE); > > ++ assert_true(expat_malloc(parser, 1000, -1) == NULL); > > ++#endif > > ++ > > ++ XML_ParserFree(parser); > > ++} > > ++END_TEST > > ++ > > ++START_TEST(test_alloc_tracker_getbuffer_unlimited) { > > ++ XML_Parser parser = XML_ParserCreate(NULL); > > ++ > > ++#if XML_GE == 1 > > ++ // Artificially lower threshold > > ++ assert_true(XML_SetAllocTrackerActivationThreshold(parser, 0) == > XML_TRUE); > > ++ > > ++ // Self-test: Prove that threshold is as rejecting as expected > > ++ assert_true(expat_malloc(parser, 1000, -1) == NULL); > > ++#endif > > ++ // XML_GetBuffer should be allowed to pass, though > > ++ assert_true(XML_GetBuffer(parser, 1000) != NULL); > > ++ > > ++ XML_ParserFree(parser); > > ++} > > ++END_TEST > > ++ > > ++START_TEST(test_alloc_tracker_api) { > > ++ XML_Parser parserWithoutParent = XML_ParserCreate(NULL); > > ++ XML_Parser parserWithParent = XML_ExternalEntityParserCreate( > > ++ parserWithoutParent, XCS("entity123"), NULL); > > ++ if (parserWithoutParent == NULL) > > ++ fail("parserWithoutParent is NULL"); > > ++ if (parserWithParent == NULL) > > ++ fail("parserWithParent is NULL"); > > ++ > > ++#if XML_GE == 1 > > ++ // XML_SetAllocTrackerMaximumAmplification, error cases > > ++ if (XML_SetAllocTrackerMaximumAmplification(NULL, 123.0f) == > XML_TRUE) > > ++ fail("Call with NULL parser is NOT supposed to succeed"); > > ++ if (XML_SetAllocTrackerMaximumAmplification(parserWithParent, 123.0f) > > ++ == XML_TRUE) > > ++ fail("Call with non-root parser is NOT supposed to succeed"); > > ++ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, NAN) > > ++ == XML_TRUE) > > ++ fail("Call with NaN limit is NOT supposed to succeed"); > > ++ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, -1.0f) > > ++ == XML_TRUE) > > ++ fail("Call with negative limit is NOT supposed to succeed"); > > ++ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, 0.9f) > > ++ == XML_TRUE) > > ++ fail("Call with positive limit <1.0 is NOT supposed to succeed"); > > ++ > > ++ // XML_SetAllocTrackerMaximumAmplification, success cases > > ++ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, 1.0f) > > ++ == XML_FALSE) > > ++ fail("Call with positive limit >=1.0 is supposed to succeed"); > > ++ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, > 123456.789f) > > ++ == XML_FALSE) > > ++ fail("Call with positive limit >=1.0 is supposed to succeed"); > > ++ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, > INFINITY) > > ++ == XML_FALSE) > > ++ fail("Call with positive limit >=1.0 is supposed to succeed"); > > ++ > > ++ // XML_SetAllocTrackerActivationThreshold, error cases > > ++ if (XML_SetAllocTrackerActivationThreshold(NULL, 123) == XML_TRUE) > > ++ fail("Call with NULL parser is NOT supposed to succeed"); > > ++ if (XML_SetAllocTrackerActivationThreshold(parserWithParent, 123) == > XML_TRUE) > > ++ fail("Call with non-root parser is NOT supposed to succeed"); > > ++ > > ++ // XML_SetAllocTrackerActivationThreshold, success cases > > ++ if (XML_SetAllocTrackerActivationThreshold(parserWithoutParent, 123) > > ++ == XML_FALSE) > > ++ fail("Call with non-NULL parentless parser is supposed to succeed"); > > ++#endif // XML_GE == 1 > > ++ > > ++ XML_ParserFree(parserWithParent); > > ++ XML_ParserFree(parserWithoutParent); > > ++} > > ++END_TEST > > ++ > > ++START_TEST(test_mem_api_cycle) { > > ++ XML_Parser parser = XML_ParserCreate(NULL); > > ++ > > ++ void *ptr = XML_MemMalloc(parser, 10); > > ++ > > ++ assert_true(ptr != NULL); > > ++ memset(ptr, 'x', 10); // assert writability, with ASan in mind > > ++ > > ++ ptr = XML_MemRealloc(parser, ptr, 20); > > ++ > > ++ assert_true(ptr != NULL); > > ++ memset(ptr, 'y', 20); // assert writability, with ASan in mind > > ++ > > ++ XML_MemFree(parser, ptr); > > ++ > > ++ XML_ParserFree(parser); > > ++} > > ++END_TEST > > ++ > > ++START_TEST(test_mem_api_unlimited) { > > ++ XML_Parser parser = XML_ParserCreate(NULL); > > ++ > > ++#if XML_GE == 1 > > ++ assert_true(XML_SetAllocTrackerActivationThreshold(parser, 0) == > XML_TRUE); > > ++#endif > > ++ > > ++ void *ptr = XML_MemMalloc(parser, 1000); > > ++ > > ++ assert_true(ptr != NULL); > > ++ > > ++ ptr = XML_MemRealloc(parser, ptr, 2000); > > ++ > > ++ assert_true(ptr != NULL); > > ++ > > ++ XML_MemFree(parser, ptr); > > ++ > > ++ XML_ParserFree(parser); > > ++} > > ++END_TEST > > ++ > > + void > > + make_alloc_test_case(Suite *s) { > > + TCase *tc_alloc = tcase_create("allocation tests"); > > +@@ -2151,4 +2354,15 @@ make_alloc_test_case(Suite *s) { > > + > > + tcase_add_test__ifdef_xml_dtd( > > + tc_alloc, test_alloc_reset_after_external_entity_parser_create_fail); > > ++ > > ++ tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_tracker_size_recorded); > > ++ tcase_add_test__ifdef_xml_dtd(tc_alloc, > > ++ test_alloc_tracker_maximum_amplification); > > ++ tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_tracker_threshold); > > ++ tcase_add_test__ifdef_xml_dtd(tc_alloc, > > ++ test_alloc_tracker_getbuffer_unlimited); > > ++ tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_tracker_api); > > ++ > > ++ tcase_add_test(tc_alloc, test_mem_api_cycle); > > ++ tcase_add_test__ifdef_xml_dtd(tc_alloc, test_mem_api_unlimited); > > + } > > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-14.patch > b/meta/recipes-core/expat/expat/CVE-2025-59375-14.patch > > new file mode 100644 > > index 00000000000..a339cc3f4b2 > > --- /dev/null > > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-14.patch > > @@ -0,0 +1,122 @@ > > +From 78366891a586f293aeff60a14a55e4afe1169586 Mon Sep 17 00:00:00 2001 > > +From: Sebastian Pipping <sebastian@pipping.org> > > +Date: Tue, 2 Sep 2025 16:44:00 +0200 > > +Subject: [PATCH] xmlwf: Wire allocation tracker config to existing arguments > > + -a and -b > > + > > +CVE: CVE-2025-59375 > > +Upstream-Status: Backport > [https://github.com/libexpat/libexpat/commit/78366891a586f293aeff60a14a55e4afe > 1169586] > > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > > +--- > > + doc/xmlwf.xml | 26 ++++++++++++++++++++------ > > + xmlwf/xmlwf.c | 7 +++++-- > > + xmlwf/xmlwf_helpgen.py | 4 ++-- > > + 3 files changed, 27 insertions(+), 10 deletions(-) > > + > > +diff --git a/doc/xmlwf.xml b/doc/xmlwf.xml > > +index 17e9cf51..65d8ae9b 100644 > > +--- a/doc/xmlwf.xml > > ++++ b/doc/xmlwf.xml > > +@@ -158,19 +158,31 @@ supports both. > > + <listitem> > > + <para> > > + Sets the maximum tolerated amplification factor > > +- for protection against billion laughs attacks (default: 100.0). > > ++ for protection against amplification attacks > > ++ like the billion laughs attack > > ++ (default: 100.0 > > ++ for the sum of direct and indirect output and also > > ++ for allocations of dynamic memory). > > + The amplification factor is calculated as .. > > + </para> > > + <literallayout> > > + amplification := (direct + indirect) / direct > > + </literallayout> > > + <para> > > +- .. while parsing, whereas > > ++ .. with regard to use of entities and .. > > ++ </para> > > ++ <literallayout> > > ++ amplification := allocated / direct > > ++ </literallayout> > > ++ <para> > > ++ .. with regard to dynamic memory while parsing. > > + <direct> is the number of bytes read > > +- from the primary document in parsing and > > ++ from the primary document in parsing, > > + <indirect> is the number of bytes > > + added by expanding entities and reading of external DTD files, > > +- combined. > > ++ combined, and > > ++ <allocated> is the total number of bytes of dynamic memory > > ++ allocated (and not freed) per hierarchy of parsers. > > + </para> > > + <para> > > + <emphasis>NOTE</emphasis>: > > +@@ -185,8 +197,10 @@ supports both. > > + <listitem> > > + <para> > > + Sets the number of output bytes (including amplification) > > +- needed to activate protection against billion laughs attacks > > +- (default: 8 MiB). > > ++ needed to activate protection against amplification attacks > > ++ like billion laughs > > ++ (default: 8 MiB for the sum of direct and indirect output, > > ++ and 64 MiB for allocations of dynamic memory). > > + This can be thought of as an "activation threshold". > > + </para> > > + <para> > > +diff --git a/xmlwf/xmlwf.c b/xmlwf/xmlwf.c > > +index b9d0a7fc..14206d9e 100644 > > +--- a/xmlwf/xmlwf.c > > ++++ b/xmlwf/xmlwf.c > > +@@ -913,11 +913,11 @@ usage(const XML_Char *prog, int rc) { > > + T(" -t write no XML output for [t]iming of plain parsing\n") > > + T(" -N enable adding doctype and [n]otation declarations\n") > > + T("\n") > > +- T("billion laughs attack protection:\n") > > ++ T("amplification attack protection (e.g. billion laughs):\n") > > + T(" NOTE: If you ever need to increase these values for non-attack > payload, please file a bug report.\n") > > + T("\n") > > + T(" -a FACTOR set maximum tolerated [a]mplification factor (default: > 100.0)\n") > > +- T(" -b BYTES set number of output [b]ytes needed to activate > (default: 8 MiB)\n") > > ++ T(" -b BYTES set number of output [b]ytes needed to activate > (default: 8 MiB/64 MiB)\n") > > + T("\n") > > + T("reparse deferral:\n") > > + T(" -q disable reparse deferral, and allow [q]uadratic parse runtime > with large tokens\n") > > +@@ -1181,12 +1181,15 @@ tmain(int argc, XML_Char **argv) { > > + #if XML_GE == 1 > > + XML_SetBillionLaughsAttackProtectionMaximumAmplification( > > + parser, attackMaximumAmplification); > > ++ XML_SetAllocTrackerMaximumAmplification(parser, > > ++ attackMaximumAmplification); > > + #endif > > + } > > + if (attackThresholdGiven) { > > + #if XML_GE == 1 > > + XML_SetBillionLaughsAttackProtectionActivationThreshold( > > + parser, attackThresholdBytes); > > ++ XML_SetAllocTrackerActivationThreshold(parser, attackThresholdBytes); > > + #else > > + (void)attackThresholdBytes; // silence -Wunused-but-set-variable > > + #endif > > +diff --git a/xmlwf/xmlwf_helpgen.py b/xmlwf/xmlwf_helpgen.py > > +index 2360820d..e91c285c 100755 > > +--- a/xmlwf/xmlwf_helpgen.py > > ++++ b/xmlwf/xmlwf_helpgen.py > > +@@ -84,13 +84,13 @@ output_mode.add_argument('-m', action='store_true', > help='write [m]eta XML, not > > + output_mode.add_argument('-t', action='store_true', help='write no XML output > for [t]iming of plain parsing') > > + output_related.add_argument('-N', action='store_true', help='enable adding > doctype and [n]otation declarations') > > + > > +-billion_laughs = parser.add_argument_group('billion laughs attack protection', > > ++billion_laughs = parser.add_argument_group('amplification attack protection > (e.g. billion laughs)', > > + description='NOTE: ' > > + 'If you ever need to increase these values > ' > > + 'for non-attack payload, please file a bug > report.') > > + billion_laughs.add_argument('-a', metavar='FACTOR', > > + help='set maximum tolerated [a]mplification factor (default: > 100.0)') > > +-billion_laughs.add_argument('-b', metavar='BYTES', help='set number of > output [b]ytes needed to activate (default: 8 MiB)') > > ++billion_laughs.add_argument('-b', metavar='BYTES', help='set number of > output [b]ytes needed to activate (default: 8 MiB/64 MiB)') > > + > > + reparse_deferral = parser.add_argument_group('reparse deferral') > > + reparse_deferral.add_argument('-q', metavar='FACTOR', > > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-15.patch > b/meta/recipes-core/expat/expat/CVE-2025-59375-15.patch > > new file mode 100644 > > index 00000000000..8d068441928 > > --- /dev/null > > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-15.patch > > @@ -0,0 +1,70 @@ > > +From 5ae51be57ed0ca1e87582881d07ea9c29c4f7c05 Mon Sep 17 00:00:00 > 2001 > > +From: Sebastian Pipping <sebastian@pipping.org> > > +Date: Wed, 3 Sep 2025 17:06:41 +0200 > > +Subject: [PATCH] fuzz: Be robust towards NULL return from > > + XML_ExternalEntityParserCreate > > + > > +CVE: CVE-2025-59375 > > +Upstream-Status: Backport > [https://github.com/libexpat/libexpat/commit/5ae51be57ed0ca1e87582881d07ea9c > 29c4f7c05] > > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > > +--- > > + fuzz/xml_parse_fuzzer.c | 14 ++++++++------ > > + fuzz/xml_parsebuffer_fuzzer.c | 14 ++++++++------ > > + 2 files changed, 16 insertions(+), 12 deletions(-) > > + > > +diff --git a/fuzz/xml_parse_fuzzer.c b/fuzz/xml_parse_fuzzer.c > > +index 90c38549..29ab33ff 100644 > > +--- a/fuzz/xml_parse_fuzzer.c > > ++++ b/fuzz/xml_parse_fuzzer.c > > +@@ -89,15 +89,17 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t > size) { > > + > > + XML_Parser externalEntityParser > > + = XML_ExternalEntityParserCreate(parentParser, "e1", NULL); > > +- assert(externalEntityParser); > > +- ParseOneInput(externalEntityParser, data, size); > > +- XML_ParserFree(externalEntityParser); > > ++ if (externalEntityParser != NULL) { > > ++ ParseOneInput(externalEntityParser, data, size); > > ++ XML_ParserFree(externalEntityParser); > > ++ } > > + > > + XML_Parser externalDtdParser > > + = XML_ExternalEntityParserCreate(parentParser, NULL, NULL); > > +- assert(externalDtdParser); > > +- ParseOneInput(externalDtdParser, data, size); > > +- XML_ParserFree(externalDtdParser); > > ++ if (externalDtdParser != NULL) { > > ++ ParseOneInput(externalDtdParser, data, size); > > ++ XML_ParserFree(externalDtdParser); > > ++ } > > + > > + // finally frees this parser which served as parent > > + XML_ParserFree(parentParser); > > +diff --git a/fuzz/xml_parsebuffer_fuzzer.c b/fuzz/xml_parsebuffer_fuzzer.c > > +index 0db67dce..38b9981b 100644 > > +--- a/fuzz/xml_parsebuffer_fuzzer.c > > ++++ b/fuzz/xml_parsebuffer_fuzzer.c > > +@@ -101,15 +101,17 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t > size) { > > + > > + XML_Parser externalEntityParser > > + = XML_ExternalEntityParserCreate(parentParser, "e1", NULL); > > +- assert(externalEntityParser); > > +- ParseOneInput(externalEntityParser, data, size); > > +- XML_ParserFree(externalEntityParser); > > ++ if (externalEntityParser != NULL) { > > ++ ParseOneInput(externalEntityParser, data, size); > > ++ XML_ParserFree(externalEntityParser); > > ++ } > > + > > + XML_Parser externalDtdParser > > + = XML_ExternalEntityParserCreate(parentParser, NULL, NULL); > > +- assert(externalDtdParser); > > +- ParseOneInput(externalDtdParser, data, size); > > +- XML_ParserFree(externalDtdParser); > > ++ if (externalDtdParser != NULL) { > > ++ ParseOneInput(externalDtdParser, data, size); > > ++ XML_ParserFree(externalDtdParser); > > ++ } > > + > > + // finally frees this parser which served as parent > > + XML_ParserFree(parentParser); > > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-16.patch > b/meta/recipes-core/expat/expat/CVE-2025-59375-16.patch > > new file mode 100644 > > index 00000000000..a276347d83d > > --- /dev/null > > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-16.patch > > @@ -0,0 +1,146 @@ > > +From d6246c31a1238d065b4d9690d3bac740326f6485 Mon Sep 17 00:00:00 > 2001 > > +From: Sebastian Pipping <sebastian@pipping.org> > > +Date: Wed, 3 Sep 2025 01:28:03 +0200 > > +Subject: [PATCH] docs: Document the two allocation tracking API functions > > + > > +CVE: CVE-2025-59375 > > +Upstream-Status: Backport > [https://github.com/libexpat/libexpat/commit/d6246c31a1238d065b4d9690d3bac74 > 0326f6485] > > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > > +--- > > + doc/reference.html | 116 > +++++++++++++++++++++++++++++++++++++++++++++ > > + 1 file changed, 116 insertions(+) > > + > > +diff --git a/doc/reference.html b/doc/reference.html > > +index 89476710..81da4e6c 100644 > > +--- a/doc/reference.html > > ++++ b/doc/reference.html > > +@@ -157,6 +157,8 @@ interface.</p> > > + <ul> > > + <li><a > href="#XML_SetBillionLaughsAttackProtectionMaximumAmplification">XML_SetBil > lionLaughsAttackProtectionMaximumAmplification</a></li> > > + <li><a > href="#XML_SetBillionLaughsAttackProtectionActivationThreshold">XML_SetBillio > nLaughsAttackProtectionActivationThreshold</a></li> > > ++ <li><a > href="#XML_SetAllocTrackerMaximumAmplification">XML_SetAllocTrackerMaximu > mAmplification</a></li> > > ++ <li><a > href="#XML_SetAllocTrackerActivationThreshold">XML_SetAllocTrackerActivationT > hreshold</a></li> > > + <li><a > href="#XML_SetReparseDeferralEnabled">XML_SetReparseDeferralEnabled</a></ > li> > > + </ul> > > + </li> > > +@@ -2262,6 +2264,120 @@ > XML_SetBillionLaughsAttackProtectionActivationThreshold(XML_Parser p, > > + </p> > > + </div> > > + > > ++<h4 > id="XML_SetAllocTrackerMaximumAmplification">XML_SetAllocTrackerMaximumA > mplification</h4> > > ++<pre class="fcndec"> > > ++/* Added in Expat 2.7.2. */ > > ++XML_Bool > > ++XML_SetAllocTrackerMaximumAmplification(XML_Parser p, > > ++ float maximumAmplificationFactor); > > ++</pre> > > ++<div class="fcndef"> > > ++ <p> > > ++ Sets the maximum tolerated amplification factor > > ++ between direct input and bytes of dynamic memory allocated > > ++ (default: <code>100.0</code>) > > ++ of parser <code>p</code> to > <code>maximumAmplificationFactor</code>, and > > ++ returns <code>XML_TRUE</code> upon success and > <code>XML_FALSE</code> upon error. > > ++ </p> > > ++ > > ++ <p> > > ++ <strong>Note:</strong> > > ++ There are three types of allocations that intentionally bypass tracking and > limiting: > > ++ </p> > > ++ <ul> > > ++ <li> > > ++ application calls to functions > > ++ <code><a href="#XML_MemMalloc">XML_MemMalloc</a></code> > > ++ and > > ++ <code><a href="#XML_MemRealloc">XML_MemRealloc</a></code> > > ++ — > > ++ <em>healthy</em> use of these two functions continues to be a > responsibility > > ++ of the application using Expat > > ++ —, > > ++ </li> > > ++ <li> > > ++ the main character buffer used by functions > > ++ <code><a href="#XML_GetBuffer">XML_GetBuffer</a></code> > > ++ and > > ++ <code><a href="#XML_ParseBuffer">XML_ParseBuffer</a></code> > > ++ (and thus also by plain > > ++ <code><a href="#XML_Parse">XML_Parse</a></code>), and > > ++ </li> > > ++ <li> > > ++ the <a href="#XML_SetElementDeclHandler">content model memory</a> > > ++ (that is passed to the > > ++ <a href="#XML_SetElementDeclHandler">element declaration handler</a> > > ++ and freed by a call to > > ++ <code><a > href="#XML_FreeContentModel">XML_FreeContentModel</a></code>). > > ++ </li> > > ++ </ul> > > ++ > > ++ <p>The amplification factor is calculated as ..</p> > > ++ <pre>amplification := allocated / direct</pre> > > ++ <p> > > ++ .. while parsing, whereas > > ++ <code>direct</code> is the number of bytes read from the primary > document in parsing and > > ++ <code>allocated</code> is the number of bytes of dynamic memory > allocated in the parser hierarchy. > > ++ </p> > > ++ > > ++ <p>For a call to <code>XML_SetAllocTrackerMaximumAmplification</code> > to succeed:</p> > > ++ <ul> > > ++ <li>parser <code>p</code> must be a non-<code>NULL</code> root > parser (without any parent parsers) and</li> > > ++ <li><code>maximumAmplificationFactor</code> must be non- > <code>NaN</code> and greater than or equal to <code>1.0</code>.</li> > > ++ </ul> > > ++ > > ++ <p> > > ++ <strong>Note:</strong> > > ++ If you ever need to increase this value for non-attack payload, > > ++ please <a href="https://github.com/libexpat/libexpat/issues">file a bug > report</a>. > > ++ </p> > > ++ > > ++ <p> > > ++ <strong>Note:</strong> > > ++ Amplifications factors greater than 100 can been observed near the start of > parsing > > ++ even with benign files in practice. > > ++ > > ++ So if you do reduce the maximum allowed amplification, > > ++ please make sure that the activation threshold is still big enough > > ++ to not end up with undesired false positives (i.e. benign files being > rejected). > > ++ </p> > > ++</div> > > ++ > > ++<h4 > id="XML_SetAllocTrackerActivationThreshold">XML_SetAllocTrackerActivationThre > shold</h4> > > ++<pre class="fcndec"> > > ++/* Added in Expat 2.7.2. */ > > ++XML_Bool > > ++XML_SetAllocTrackerActivationThreshold(XML_Parser p, > > ++ unsigned long long activationThresholdBytes); > > ++</pre> > > ++<div class="fcndef"> > > ++ <p> > > ++ Sets number of allocated bytes of dynamic memory > > ++ needed to activate protection against disproportionate use of RAM > > ++ (default: <code>64 MiB</code>) > > ++ of parser <code>p</code> to <code>activationThresholdBytes</code>, > and > > ++ returns <code>XML_TRUE</code> upon success and > <code>XML_FALSE</code> upon error. > > ++ </p> > > ++ > > ++ <p> > > ++ <strong>Note:</strong> > > ++ For types of allocations that intentionally bypass tracking and limiting, > please see > > ++ <code><a > href="#XML_SetAllocTrackerMaximumAmplification">XML_SetAllocTrackerMaximu > mAmplification</a></code> > > ++ above. > > ++ </p> > > ++ > > ++ <p>For a call to <code>XML_SetAllocTrackerActivationThreshold</code> to > succeed:</p> > > ++ <ul> > > ++ <li>parser <code>p</code> must be a non-<code>NULL</code> root > parser (without any parent parsers).</li> > > ++ </ul> > > ++ > > ++ <p> > > ++ <strong>Note:</strong> > > ++ If you ever need to increase this value for non-attack payload, > > ++ please <a href="https://github.com/libexpat/libexpat/issues">file a bug > report</a>. > > ++ </p> > > ++</div> > > ++ > > + <h4 > id="XML_SetReparseDeferralEnabled">XML_SetReparseDeferralEnabled</h4> > > + <pre class="fcndec"> > > + /* Added in Expat 2.6.0. */ > > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-17.patch > b/meta/recipes-core/expat/expat/CVE-2025-59375-17.patch > > new file mode 100644 > > index 00000000000..ca0e3a34f73 > > --- /dev/null > > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-17.patch > > @@ -0,0 +1,28 @@ > > +From a6a2a49367f03f5d8a73c9027b45b59953ca27d8 Mon Sep 17 00:00:00 > 2001 > > +From: Sebastian Pipping <sebastian@pipping.org> > > +Date: Wed, 10 Sep 2025 19:52:39 +0200 > > +Subject: [PATCH] docs: Promote the contract to call XML_FreeContentModel > > + > > +.. when registering a custom element declaration handler > > +(via a call to function XML_SetElementDeclHandler) > > + > > +CVE: CVE-2025-59375 > > +Upstream-Status: Backport > [https://github.com/libexpat/libexpat/commit/a6a2a49367f03f5d8a73c9027b45b599 > 53ca27d8] > > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > > +--- > > + doc/reference.html | 2 +- > > + 1 file changed, 1 insertion(+), 1 deletion(-) > > + > > +diff --git a/doc/reference.html b/doc/reference.html > > +index 81da4e6c..564fc1b2 100644 > > +--- a/doc/reference.html > > ++++ b/doc/reference.html > > +@@ -1902,7 +1902,7 @@ struct XML_cp { > > + <p>Sets a handler for element declarations in a DTD. The handler gets > > + called with the name of the element in the declaration and a pointer > > + to a structure that contains the element model. It's the user code's > > +-responsibility to free model when finished with it. See <code> > > ++responsibility to free model when finished with via a call to <code> > > + <a href="#XML_FreeContentModel">XML_FreeContentModel</a></code>. > > + There is no need to free the model from the handler, it can be kept > > + around and freed at a later stage.</p> > > diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-18.patch > b/meta/recipes-core/expat/expat/CVE-2025-59375-18.patch > > new file mode 100644 > > index 00000000000..c29b3018254 > > --- /dev/null > > +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-18.patch > > @@ -0,0 +1,74 @@ > > +From a21a3a8299e1ee0b0ae5ae2886a0746d088cf135 Mon Sep 17 00:00:00 > 2001 > > +From: Sebastian Pipping <sebastian@pipping.org> > > +Date: Sun, 7 Sep 2025 16:00:35 +0200 > > +Subject: [PATCH] Changes: Document allocation tracking > > + > > +CVE: CVE-2025-59375 > > +Upstream-Status: Backport > [https://github.com/libexpat/libexpat/commit/a21a3a8299e1ee0b0ae5ae2886a0746d > 088cf135] > > +Signed-off-by: Peter Marko <peter.marko@siemens.com> > > +--- > > + Changes | 37 +++++++++++++++++++++++++++++++++++++ > > + 1 file changed, 37 insertions(+) > > + > > +diff --git a/Changes b/Changes > > +index cb752151..ceb5c5dc 100644 > > +--- a/Changes > > ++++ b/Changes > > +@@ -30,6 +30,36 @@ > > + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! > > + > > + Patches: > > ++ Security fixes: > > ++ #1018 #1034 CVE-2025-59375 -- Disallow use of disproportional amounts > of > > ++ dynamic memory from within an Expat parser (e.g. previously > > ++ a ~250 KiB sized document was able to cause allocation of > > ++ ~800 MiB from the heap, i.e. an "amplification" of factor > > ++ ~3,300); once a threshold (that defaults to 64 MiB) is > > ++ reached, a maximum amplification factor (that defaults to > > ++ 100.0) is enforced, and violating documents are rejected > > ++ with an out-of-memory error. > > ++ There are two new API functions to fine-tune this new > > ++ behavior: > > ++ - XML_SetAllocTrackerActivationThreshold > > ++ - XML_SetAllocTrackerMaximumAmplification . > > ++ If you ever need to increase these defaults for non-attack > > ++ XML payload, please file a bug report with libexpat. > > ++ There is also a new environment variable > > ++ EXPAT_MALLOC_DEBUG=(0|1|2) to control the verbosity > > ++ of allocations debugging at runtime, disabled by default. > > ++ Known impact is (reliable and easy) denial of service: > > ++ CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H/E:H/RL:O/RC:C > > ++ (Base Score: 7.5, Temporal Score: 7.2) > > ++ Please note that a layer of compression around XML can > > ++ significantly reduce the minimum attack payload size. > > ++ Distributors intending to backport (or cherry-pick) the > > ++ fix need to copy 99% of the related pull request, not just > > ++ the "lib: Implement tracking of dynamic memory allocations" > > ++ commit, to not end up with a state that literally does both > > ++ too much and too little at the same time. Appending ".diff" > > ++ to the pull request URL could be of help. > > ++ > > + Bug fixes: > > + #980 #989 Restore event pointer behavior from Expat 2.6.4 > > + (that the fix to CVE-2024-8176 changed in 2.7.0); > > +@@ -39,6 +69,10 @@ Patches: > > + - XML_GetCurrentColumnNumber > > + - XML_GetCurrentLineNumber > > + - XML_GetInputContext > > ++ #1034 docs: Promote the contract to call function > > ++ XML_FreeContentModel when registering a custom > > ++ element declaration handler (via a call to function > > ++ XML_SetElementDeclHandler) > > + > > + Special thanks to: > > + Berkay Eren Ürün > > +@@ -71,6 +105,9 @@ Patches: > > + Linutronix > > + Red Hat > > + Siemens > > ++ and > > ++ OSS-Fuzz / ClusterFuzz > > ++ Perl XML::Parser > > + > > + Release 2.6.4 Wed November 6 2024 > > + Security fixes: > > diff --git a/meta/recipes-core/expat/expat_2.6.4.bb b/meta/recipes- > core/expat/expat_2.6.4.bb > > index 816beaa8a3d..fdfe2b850a7 100644 > > --- a/meta/recipes-core/expat/expat_2.6.4.bb > > +++ b/meta/recipes-core/expat/expat_2.6.4.bb > > @@ -16,6 +16,25 @@ SRC_URI = > "${GITHUB_BASE_URI}/download/R_${VERSION_TAG}/expat-${PV}.tar.bz2 \ > > file://CVE-2024-8176-03.patch \ > > file://CVE-2024-8176-04.patch \ > > file://CVE-2024-8176-05.patch \ > > + file://CVE-2025-59375-00.patch \ > > + file://CVE-2025-59375-01.patch \ > > + file://CVE-2025-59375-02.patch \ > > + file://CVE-2025-59375-03.patch \ > > + file://CVE-2025-59375-04.patch \ > > + file://CVE-2025-59375-05.patch \ > > + file://CVE-2025-59375-06.patch \ > > + file://CVE-2025-59375-07.patch \ > > + file://CVE-2025-59375-08.patch \ > > + file://CVE-2025-59375-09.patch \ > > + file://CVE-2025-59375-10.patch \ > > + file://CVE-2025-59375-11.patch \ > > + file://CVE-2025-59375-12.patch \ > > + file://CVE-2025-59375-13.patch \ > > + file://CVE-2025-59375-14.patch \ > > + file://CVE-2025-59375-15.patch \ > > + file://CVE-2025-59375-16.patch \ > > + file://CVE-2025-59375-17.patch \ > > + file://CVE-2025-59375-18.patch \ > > " > > > > GITHUB_BASE_URI = "https://github.com/libexpat/libexpat/releases/" > > > > -=-=-=-=-=-=-=-=-=-=-=- > > Links: You receive all messages sent to this group. > > View/Reply Online (#225131): https://lists.openembedded.org/g/openembedded- > core/message/225131 > > Mute This Topic: https://lists.openembedded.org/mt/115861217/3619737 > > Group Owner: openembedded-core+owner@lists.openembedded.org > > Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub > [ankur.tyagi85@gmail.com] > > -=-=-=-=-=-=-=-=-=-=-=- > >
diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-00.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-00.patch new file mode 100644 index 00000000000..e3cbd0f6048 --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-00.patch @@ -0,0 +1,52 @@ +From 87321ac84a0d6cb42ee64a591adc79c1ec37fb5b Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping <sebastian@pipping.org> +Date: Tue, 2 Sep 2025 20:52:29 +0200 +Subject: [PATCH] xmlwf: Mention supported environment variables in --help + output + +CVE: CVE-2025-59375 +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/87321ac84a0d6cb42ee64a591adc79c1ec37fb5b] +Signed-off-by: Peter Marko <peter.marko@siemens.com> +--- + xmlwf/xmlwf.c | 8 ++++++++ + xmlwf/xmlwf_helpgen.py | 8 ++++++++ + 2 files changed, 16 insertions(+) + +diff --git a/xmlwf/xmlwf.c b/xmlwf/xmlwf.c +index ec7e51c9..8cfc73ca 100644 +--- a/xmlwf/xmlwf.c ++++ b/xmlwf/xmlwf.c +@@ -926,6 +926,14 @@ usage(const XML_Char *prog, int rc) { + T(" -h, --help show this [h]elp message and exit\n") + T(" -v, --version show program's [v]ersion number and exit\n") + T("\n") ++ T("environment variables:\n") ++ T(" EXPAT_ACCOUNTING_DEBUG=(0|1|2|3)\n") ++ T(" Control verbosity of accounting debugging (default: 0)\n") ++ T(" EXPAT_ENTITY_DEBUG=(0|1)\n") ++ T(" Control verbosity of entity debugging (default: 0)\n") ++ T(" EXPAT_ENTROPY_DEBUG=(0|1)\n") ++ T(" Control verbosity of entropy debugging (default: 0)\n") ++ T("\n") + T("exit status:\n") + T(" 0 the input files are well-formed and the output (if requested) was written successfully\n") + T(" 1 could not allocate data structures, signals a serious problem with execution environment\n") +diff --git a/xmlwf/xmlwf_helpgen.py b/xmlwf/xmlwf_helpgen.py +index c3257f0e..39a3dc13 100755 +--- a/xmlwf/xmlwf_helpgen.py ++++ b/xmlwf/xmlwf_helpgen.py +@@ -32,6 +32,14 @@ + import argparse + + epilog = """ ++environment variables: ++ EXPAT_ACCOUNTING_DEBUG=(0|1|2|3) ++ Control verbosity of accounting debugging (default: 0) ++ EXPAT_ENTITY_DEBUG=(0|1) ++ Control verbosity of entity debugging (default: 0) ++ EXPAT_ENTROPY_DEBUG=(0|1) ++ Control verbosity of entropy debugging (default: 0) ++ + exit status: + 0 the input files are well-formed and the output (if requested) was written successfully + 1 could not allocate data structures, signals a serious problem with execution environment diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-01.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-01.patch new file mode 100644 index 00000000000..6708bbef45c --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-01.patch @@ -0,0 +1,48 @@ +From 0872c189db6e457084fca335662a9cb49e8ec4c7 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping <sebastian@pipping.org> +Date: Mon, 1 Sep 2025 18:06:59 +0200 +Subject: [PATCH] lib: Make function dtdCreate use macro MALLOC + +.. and give its body access to the parser for upcoming changes + +CVE: CVE-2025-59375 +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/0872c189db6e457084fca335662a9cb49e8ec4c7] +Signed-off-by: Peter Marko <peter.marko@siemens.com> +--- + lib/xmlparse.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index 25f786ec..b9d6eed1 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -555,7 +555,7 @@ static XML_Bool setContext(XML_Parser parser, const XML_Char *context); + + static void FASTCALL normalizePublicId(XML_Char *s); + +-static DTD *dtdCreate(const XML_Memory_Handling_Suite *ms); ++static DTD *dtdCreate(XML_Parser parser); + /* do not call if m_parentParser != NULL */ + static void dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms); + static void dtdDestroy(DTD *p, XML_Bool isDocEntity, +@@ -1166,7 +1166,7 @@ parserCreate(const XML_Char *encodingName, + if (dtd) + parser->m_dtd = dtd; + else { +- parser->m_dtd = dtdCreate(&parser->m_mem); ++ parser->m_dtd = dtdCreate(parser); + if (parser->m_dtd == NULL) { + FREE(parser, parser->m_dataBuf); + FREE(parser, parser->m_atts); +@@ -7126,8 +7126,9 @@ normalizePublicId(XML_Char *publicId) { + } + + static DTD * +-dtdCreate(const XML_Memory_Handling_Suite *ms) { +- DTD *p = ms->malloc_fcn(sizeof(DTD)); ++dtdCreate(XML_Parser parser) { ++ const XML_Memory_Handling_Suite *const ms = &parser->m_mem; ++ DTD *p = MALLOC(parser, sizeof(DTD)); + if (p == NULL) + return p; + poolInit(&(p->pool), ms); diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-02.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-02.patch new file mode 100644 index 00000000000..b0543370ad8 --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-02.patch @@ -0,0 +1,109 @@ +From 8768dadae479d9f2e984b747fb2ba79bb78de94f Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping <sebastian@pipping.org> +Date: Mon, 1 Sep 2025 18:10:26 +0200 +Subject: [PATCH] lib: Make string pools use macros MALLOC, FREE, REALLOC + +CVE: CVE-2025-59375 +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/8768dadae479d9f2e984b747fb2ba79bb78de94f] +Signed-off-by: Peter Marko <peter.marko@siemens.com> +--- + lib/xmlparse.c | 27 +++++++++++++-------------- + 1 file changed, 13 insertions(+), 14 deletions(-) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index b9d6eed1..a56c71ea 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -357,7 +357,7 @@ typedef struct { + const XML_Char *end; + XML_Char *ptr; + XML_Char *start; +- const XML_Memory_Handling_Suite *mem; ++ XML_Parser parser; + } STRING_POOL; + + /* The XML_Char before the name is used to determine whether +@@ -574,8 +574,7 @@ static void FASTCALL hashTableIterInit(HASH_TABLE_ITER *iter, + const HASH_TABLE *table); + static NAMED *FASTCALL hashTableIterNext(HASH_TABLE_ITER *iter); + +-static void FASTCALL poolInit(STRING_POOL *pool, +- const XML_Memory_Handling_Suite *ms); ++static void FASTCALL poolInit(STRING_POOL *pool, XML_Parser parser); + static void FASTCALL poolClear(STRING_POOL *pool); + static void FASTCALL poolDestroy(STRING_POOL *pool); + static XML_Char *poolAppend(STRING_POOL *pool, const ENCODING *enc, +@@ -1200,8 +1199,8 @@ parserCreate(const XML_Char *encodingName, + + parser->m_protocolEncodingName = NULL; + +- poolInit(&parser->m_tempPool, &(parser->m_mem)); +- poolInit(&parser->m_temp2Pool, &(parser->m_mem)); ++ poolInit(&parser->m_tempPool, parser); ++ poolInit(&parser->m_temp2Pool, parser); + parserInit(parser, encodingName); + + if (encodingName && ! parser->m_protocolEncodingName) { +@@ -7131,8 +7130,8 @@ dtdCreate(XML_Parser parser) { + DTD *p = MALLOC(parser, sizeof(DTD)); + if (p == NULL) + return p; +- poolInit(&(p->pool), ms); +- poolInit(&(p->entityValuePool), ms); ++ poolInit(&(p->pool), parser); ++ poolInit(&(p->entityValuePool), parser); + hashTableInit(&(p->generalEntities), ms); + hashTableInit(&(p->elementTypes), ms); + hashTableInit(&(p->attributeIds), ms); +@@ -7596,13 +7595,13 @@ hashTableIterNext(HASH_TABLE_ITER *iter) { + } + + static void FASTCALL +-poolInit(STRING_POOL *pool, const XML_Memory_Handling_Suite *ms) { ++poolInit(STRING_POOL *pool, XML_Parser parser) { + pool->blocks = NULL; + pool->freeBlocks = NULL; + pool->start = NULL; + pool->ptr = NULL; + pool->end = NULL; +- pool->mem = ms; ++ pool->parser = parser; + } + + static void FASTCALL +@@ -7629,13 +7628,13 @@ poolDestroy(STRING_POOL *pool) { + BLOCK *p = pool->blocks; + while (p) { + BLOCK *tem = p->next; +- pool->mem->free_fcn(p); ++ FREE(pool->parser, p); + p = tem; + } + p = pool->freeBlocks; + while (p) { + BLOCK *tem = p->next; +- pool->mem->free_fcn(p); ++ FREE(pool->parser, p); + p = tem; + } + } +@@ -7790,8 +7789,8 @@ poolGrow(STRING_POOL *pool) { + if (bytesToAllocate == 0) + return XML_FALSE; + +- temp = (BLOCK *)pool->mem->realloc_fcn(pool->blocks, +- (unsigned)bytesToAllocate); ++ temp = (BLOCK *)REALLOC(pool->parser, pool->blocks, ++ (unsigned)bytesToAllocate); + if (temp == NULL) + return XML_FALSE; + pool->blocks = temp; +@@ -7831,7 +7830,7 @@ poolGrow(STRING_POOL *pool) { + if (bytesToAllocate == 0) + return XML_FALSE; + +- tem = pool->mem->malloc_fcn(bytesToAllocate); ++ tem = MALLOC(pool->parser, bytesToAllocate); + if (! tem) + return XML_FALSE; + tem->size = blockSize; diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-03.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-03.patch new file mode 100644 index 00000000000..b8c2c595e1a --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-03.patch @@ -0,0 +1,127 @@ +From 4fc6f1ee9f2b282cfe446bf645c992e37f8c3e15 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping <sebastian@pipping.org> +Date: Mon, 1 Sep 2025 18:14:09 +0200 +Subject: [PATCH] lib: Make function hash tables use macros MALLOC and FREE + +CVE: CVE-2025-59375 +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/4fc6f1ee9f2b282cfe446bf645c992e37f8c3e15] +Signed-off-by: Peter Marko <peter.marko@siemens.com> +--- + lib/xmlparse.c | 34 ++++++++++++++++------------------ + 1 file changed, 16 insertions(+), 18 deletions(-) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index a56c71ea..a65b0265 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -234,7 +234,7 @@ typedef struct { + unsigned char power; + size_t size; + size_t used; +- const XML_Memory_Handling_Suite *mem; ++ XML_Parser parser; + } HASH_TABLE; + + static size_t keylen(KEY s); +@@ -566,8 +566,7 @@ static int copyEntityTable(XML_Parser oldParser, HASH_TABLE *newTable, + STRING_POOL *newPool, const HASH_TABLE *oldTable); + static NAMED *lookup(XML_Parser parser, HASH_TABLE *table, KEY name, + size_t createSize); +-static void FASTCALL hashTableInit(HASH_TABLE *table, +- const XML_Memory_Handling_Suite *ms); ++static void FASTCALL hashTableInit(HASH_TABLE *table, XML_Parser parser); + static void FASTCALL hashTableClear(HASH_TABLE *table); + static void FASTCALL hashTableDestroy(HASH_TABLE *table); + static void FASTCALL hashTableIterInit(HASH_TABLE_ITER *iter, +@@ -7126,19 +7125,18 @@ normalizePublicId(XML_Char *publicId) { + + static DTD * + dtdCreate(XML_Parser parser) { +- const XML_Memory_Handling_Suite *const ms = &parser->m_mem; + DTD *p = MALLOC(parser, sizeof(DTD)); + if (p == NULL) + return p; + poolInit(&(p->pool), parser); + poolInit(&(p->entityValuePool), parser); +- hashTableInit(&(p->generalEntities), ms); +- hashTableInit(&(p->elementTypes), ms); +- hashTableInit(&(p->attributeIds), ms); +- hashTableInit(&(p->prefixes), ms); ++ hashTableInit(&(p->generalEntities), parser); ++ hashTableInit(&(p->elementTypes), parser); ++ hashTableInit(&(p->attributeIds), parser); ++ hashTableInit(&(p->prefixes), parser); + #ifdef XML_DTD + p->paramEntityRead = XML_FALSE; +- hashTableInit(&(p->paramEntities), ms); ++ hashTableInit(&(p->paramEntities), parser); + #endif /* XML_DTD */ + p->defaultPrefix.name = NULL; + p->defaultPrefix.binding = NULL; +@@ -7473,7 +7471,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) { + /* table->size is a power of 2 */ + table->size = (size_t)1 << INIT_POWER; + tsize = table->size * sizeof(NAMED *); +- table->v = table->mem->malloc_fcn(tsize); ++ table->v = MALLOC(table->parser, tsize); + if (! table->v) { + table->size = 0; + return NULL; +@@ -7513,7 +7511,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) { + } + + size_t tsize = newSize * sizeof(NAMED *); +- NAMED **newV = table->mem->malloc_fcn(tsize); ++ NAMED **newV = MALLOC(table->parser, tsize); + if (! newV) + return NULL; + memset(newV, 0, tsize); +@@ -7529,7 +7527,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) { + } + newV[j] = table->v[i]; + } +- table->mem->free_fcn(table->v); ++ FREE(table->parser, table->v); + table->v = newV; + table->power = newPower; + table->size = newSize; +@@ -7542,7 +7540,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) { + } + } + } +- table->v[i] = table->mem->malloc_fcn(createSize); ++ table->v[i] = MALLOC(table->parser, createSize); + if (! table->v[i]) + return NULL; + memset(table->v[i], 0, createSize); +@@ -7555,7 +7553,7 @@ static void FASTCALL + hashTableClear(HASH_TABLE *table) { + size_t i; + for (i = 0; i < table->size; i++) { +- table->mem->free_fcn(table->v[i]); ++ FREE(table->parser, table->v[i]); + table->v[i] = NULL; + } + table->used = 0; +@@ -7565,17 +7563,17 @@ static void FASTCALL + hashTableDestroy(HASH_TABLE *table) { + size_t i; + for (i = 0; i < table->size; i++) +- table->mem->free_fcn(table->v[i]); +- table->mem->free_fcn(table->v); ++ FREE(table->parser, table->v[i]); ++ FREE(table->parser, table->v); + } + + static void FASTCALL +-hashTableInit(HASH_TABLE *p, const XML_Memory_Handling_Suite *ms) { ++hashTableInit(HASH_TABLE *p, XML_Parser parser) { + p->power = 0; + p->size = 0; + p->used = 0; + p->v = NULL; +- p->mem = ms; ++ p->parser = parser; + } + + static void FASTCALL diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-04.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-04.patch new file mode 100644 index 00000000000..78d9e2fc91b --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-04.patch @@ -0,0 +1,62 @@ +From 51487ad9d760faa4809b0f8e189d2f666317e41a Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping <sebastian@pipping.org> +Date: Mon, 1 Sep 2025 17:45:50 +0200 +Subject: [PATCH] lib: Make function copyString use macro MALLOC + +CVE: CVE-2025-59375 +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/51487ad9d760faa4809b0f8e189d2f666317e41a] +Signed-off-by: Peter Marko <peter.marko@siemens.com> +--- + lib/xmlparse.c | 11 +++++------ + 1 file changed, 5 insertions(+), 6 deletions(-) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index a65b0265..c0576abd 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -593,8 +593,7 @@ static XML_Content *build_model(XML_Parser parser); + static ELEMENT_TYPE *getElementType(XML_Parser parser, const ENCODING *enc, + const char *ptr, const char *end); + +-static XML_Char *copyString(const XML_Char *s, +- const XML_Memory_Handling_Suite *memsuite); ++static XML_Char *copyString(const XML_Char *s, XML_Parser parser); + + static unsigned long generate_hash_secret_salt(XML_Parser parser); + static XML_Bool startParsing(XML_Parser parser); +@@ -1231,7 +1230,7 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) { + parser->m_processor = prologInitProcessor; + XmlPrologStateInit(&parser->m_prologState); + if (encodingName != NULL) { +- parser->m_protocolEncodingName = copyString(encodingName, &(parser->m_mem)); ++ parser->m_protocolEncodingName = copyString(encodingName, parser); + } + parser->m_curBase = NULL; + XmlInitEncoding(&parser->m_initEncoding, &parser->m_encoding, 0); +@@ -1419,7 +1418,7 @@ XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName) { + parser->m_protocolEncodingName = NULL; + else { + /* Copy the new encoding name into allocated memory */ +- parser->m_protocolEncodingName = copyString(encodingName, &(parser->m_mem)); ++ parser->m_protocolEncodingName = copyString(encodingName, parser); + if (! parser->m_protocolEncodingName) + return XML_STATUS_ERROR; + } +@@ -8064,7 +8063,7 @@ getElementType(XML_Parser parser, const ENCODING *enc, const char *ptr, + } + + static XML_Char * +-copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) { ++copyString(const XML_Char *s, XML_Parser parser) { + size_t charsRequired = 0; + XML_Char *result; + +@@ -8076,7 +8075,7 @@ copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) { + charsRequired++; + + /* Now allocate space for the copy */ +- result = memsuite->malloc_fcn(charsRequired * sizeof(XML_Char)); ++ result = MALLOC(parser, charsRequired * sizeof(XML_Char)); + if (result == NULL) + return NULL; + /* Copy the original into place */ diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-05.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-05.patch new file mode 100644 index 00000000000..37b882fbf49 --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-05.patch @@ -0,0 +1,64 @@ +From b3f0bda5f5e979781469532f7c304f7e223568d5 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping <sebastian@pipping.org> +Date: Mon, 1 Sep 2025 17:48:02 +0200 +Subject: [PATCH] lib: Make function dtdReset use macro FREE + +CVE: CVE-2025-59375 +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/b3f0bda5f5e979781469532f7c304f7e223568d5] +Signed-off-by: Peter Marko <peter.marko@siemens.com> +--- + lib/xmlparse.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index c0576abd..65fcce30 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -557,7 +557,7 @@ static void FASTCALL normalizePublicId(XML_Char *s); + + static DTD *dtdCreate(XML_Parser parser); + /* do not call if m_parentParser != NULL */ +-static void dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms); ++static void dtdReset(DTD *p, XML_Parser parser); + static void dtdDestroy(DTD *p, XML_Bool isDocEntity, + const XML_Memory_Handling_Suite *ms); + static int dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, +@@ -1382,7 +1382,7 @@ XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) { + FREE(parser, (void *)parser->m_protocolEncodingName); + parser->m_protocolEncodingName = NULL; + parserInit(parser, encodingName); +- dtdReset(parser->m_dtd, &parser->m_mem); ++ dtdReset(parser->m_dtd, parser); + return XML_TRUE; + } + +@@ -7155,7 +7155,7 @@ dtdCreate(XML_Parser parser) { + } + + static void +-dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) { ++dtdReset(DTD *p, XML_Parser parser) { + HASH_TABLE_ITER iter; + hashTableIterInit(&iter, &(p->elementTypes)); + for (;;) { +@@ -7163,7 +7163,7 @@ dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) { + if (! e) + break; + if (e->allocDefaultAtts != 0) +- ms->free_fcn(e->defaultAtts); ++ FREE(parser, e->defaultAtts); + } + hashTableClear(&(p->generalEntities)); + #ifdef XML_DTD +@@ -7180,9 +7180,9 @@ dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) { + + p->in_eldecl = XML_FALSE; + +- ms->free_fcn(p->scaffIndex); ++ FREE(parser, p->scaffIndex); + p->scaffIndex = NULL; +- ms->free_fcn(p->scaffold); ++ FREE(parser, p->scaffold); + p->scaffold = NULL; + + p->scaffLevel = 0; diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-06.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-06.patch new file mode 100644 index 00000000000..04f975a458f --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-06.patch @@ -0,0 +1,68 @@ +From 53a3eda0ae2e0317afd071b72b41976053d82732 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping <sebastian@pipping.org> +Date: Mon, 1 Sep 2025 17:50:59 +0200 +Subject: [PATCH] lib: Make function dtdDestroy use macro FREE + +CVE: CVE-2025-59375 +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/53a3eda0ae2e0317afd071b72b41976053d82732] +Signed-off-by: Peter Marko <peter.marko@siemens.com> +--- + lib/xmlparse.c | 16 +++++++--------- + 1 file changed, 7 insertions(+), 9 deletions(-) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index 65fcce30..e7df97da 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -558,8 +558,7 @@ static void FASTCALL normalizePublicId(XML_Char *s); + static DTD *dtdCreate(XML_Parser parser); + /* do not call if m_parentParser != NULL */ + static void dtdReset(DTD *p, XML_Parser parser); +-static void dtdDestroy(DTD *p, XML_Bool isDocEntity, +- const XML_Memory_Handling_Suite *ms); ++static void dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser); + static int dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, + const XML_Memory_Handling_Suite *ms); + static int copyEntityTable(XML_Parser oldParser, HASH_TABLE *newTable, +@@ -1685,8 +1684,7 @@ XML_ParserFree(XML_Parser parser) { + #else + if (parser->m_dtd) + #endif /* XML_DTD */ +- dtdDestroy(parser->m_dtd, (XML_Bool)! parser->m_parentParser, +- &parser->m_mem); ++ dtdDestroy(parser->m_dtd, (XML_Bool)! parser->m_parentParser, parser); + FREE(parser, (void *)parser->m_atts); + #ifdef XML_ATTR_INFO + FREE(parser, (void *)parser->m_attInfo); +@@ -7196,7 +7194,7 @@ dtdReset(DTD *p, XML_Parser parser) { + } + + static void +-dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) { ++dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser) { + HASH_TABLE_ITER iter; + hashTableIterInit(&iter, &(p->elementTypes)); + for (;;) { +@@ -7204,7 +7202,7 @@ dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) { + if (! e) + break; + if (e->allocDefaultAtts != 0) +- ms->free_fcn(e->defaultAtts); ++ FREE(parser, e->defaultAtts); + } + hashTableDestroy(&(p->generalEntities)); + #ifdef XML_DTD +@@ -7216,10 +7214,10 @@ dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) { + poolDestroy(&(p->pool)); + poolDestroy(&(p->entityValuePool)); + if (isDocEntity) { +- ms->free_fcn(p->scaffIndex); +- ms->free_fcn(p->scaffold); ++ FREE(parser, p->scaffIndex); ++ FREE(parser, p->scaffold); + } +- ms->free_fcn(p); ++ FREE(parser, p); + } + + /* Do a deep copy of the DTD. Return 0 for out of memory, non-zero otherwise. diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-07.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-07.patch new file mode 100644 index 00000000000..7eff0009d2c --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-07.patch @@ -0,0 +1,52 @@ +From 4e7a5d03daf672f20c73d40dc8970385c18b30d3 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping <sebastian@pipping.org> +Date: Mon, 1 Sep 2025 17:52:58 +0200 +Subject: [PATCH] lib: Make function dtdCopy use macro MALLOC + +CVE: CVE-2025-59375 +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/4e7a5d03daf672f20c73d40dc8970385c18b30d3] +Signed-off-by: Peter Marko <peter.marko@siemens.com> +--- + lib/xmlparse.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index e7df97da..9f0a8b3e 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -560,7 +560,7 @@ static DTD *dtdCreate(XML_Parser parser); + static void dtdReset(DTD *p, XML_Parser parser); + static void dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser); + static int dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, +- const XML_Memory_Handling_Suite *ms); ++ XML_Parser parser); + static int copyEntityTable(XML_Parser oldParser, HASH_TABLE *newTable, + STRING_POOL *newPool, const HASH_TABLE *oldTable); + static NAMED *lookup(XML_Parser parser, HASH_TABLE *table, KEY name, +@@ -1572,7 +1572,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context, + parser->m_prologState.inEntityValue = oldInEntityValue; + if (context) { + #endif /* XML_DTD */ +- if (! dtdCopy(oldParser, parser->m_dtd, oldDtd, &parser->m_mem) ++ if (! dtdCopy(oldParser, parser->m_dtd, oldDtd, parser) + || ! setContext(parser, context)) { + XML_ParserFree(parser); + return NULL; +@@ -7225,7 +7225,7 @@ dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser) { + */ + static int + dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, +- const XML_Memory_Handling_Suite *ms) { ++ XML_Parser parser) { + HASH_TABLE_ITER iter; + + /* Copy the prefix table. */ +@@ -7306,7 +7306,7 @@ dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, + } + #endif + newE->defaultAtts +- = ms->malloc_fcn(oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE)); ++ = MALLOC(parser, oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE)); + if (! newE->defaultAtts) { + return 0; + } diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-08.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-08.patch new file mode 100644 index 00000000000..deda31bebcd --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-08.patch @@ -0,0 +1,577 @@ +From cfce28e171676fe6f70d17b97ed8a59eaeb83f15 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping <sebastian@pipping.org> +Date: Mon, 1 Sep 2025 17:34:58 +0200 +Subject: [PATCH] lib: Implement tracking of dynamic memory allocations + +**PLEASE NOTE** that distributors intending to backport (or cherry-pick) +this fix need to copy 99% of the related pull request, not just this +commit, to not end up with a state that literally does both too much and +too little at the same time. Appending ".diff" to the pull request URL +could be of help. + +CVE: CVE-2025-59375 +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/cfce28e171676fe6f70d17b97ed8a59eaeb83f15] +Signed-off-by: Peter Marko <peter.marko@siemens.com> +--- + lib/expat.h | 15 +- + lib/internal.h | 5 + + lib/libexpat.def.cmake | 3 + + lib/xmlparse.c | 337 +++++++++++++++++++++++++++++++++++++++-- + tests/basic_tests.c | 4 + + tests/nsalloc_tests.c | 5 + + xmlwf/xmlwf.c | 2 + + xmlwf/xmlwf_helpgen.py | 2 + + 8 files changed, 361 insertions(+), 12 deletions(-) + +diff --git a/lib/expat.h b/lib/expat.h +index 610e1ddc..66a253c1 100644 +--- a/lib/expat.h ++++ b/lib/expat.h +@@ -1032,7 +1032,10 @@ enum XML_FeatureEnum { + XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT, + XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT, + /* Added in Expat 2.6.0. */ +- XML_FEATURE_GE ++ XML_FEATURE_GE, ++ /* Added in Expat 2.7.2. */ ++ XML_FEATURE_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT, ++ XML_FEATURE_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT, + /* Additional features must be added to the end of this enum. */ + }; + +@@ -1057,6 +1060,16 @@ XML_SetBillionLaughsAttackProtectionMaximumAmplification( + XMLPARSEAPI(XML_Bool) + XML_SetBillionLaughsAttackProtectionActivationThreshold( + XML_Parser parser, unsigned long long activationThresholdBytes); ++ ++/* Added in Expat 2.7.2. */ ++XMLPARSEAPI(XML_Bool) ++XML_SetAllocTrackerMaximumAmplification(XML_Parser parser, ++ float maximumAmplificationFactor); ++ ++/* Added in Expat 2.7.2. */ ++XMLPARSEAPI(XML_Bool) ++XML_SetAllocTrackerActivationThreshold( ++ XML_Parser parser, unsigned long long activationThresholdBytes); + #endif + + /* Added in Expat 2.6.0. */ +diff --git a/lib/internal.h b/lib/internal.h +index 6bde6ae6..eb67cf50 100644 +--- a/lib/internal.h ++++ b/lib/internal.h +@@ -145,6 +145,11 @@ + 100.0f + #define EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT \ + 8388608 // 8 MiB, 2^23 ++ ++#define EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT 100.0f ++#define EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT \ ++ 67108864 // 64 MiB, 2^26 ++ + /* NOTE END */ + + #include "expat.h" // so we can use type XML_Parser below +diff --git a/lib/libexpat.def.cmake b/lib/libexpat.def.cmake +index 10ee9cd6..7a3a7ec0 100644 +--- a/lib/libexpat.def.cmake ++++ b/lib/libexpat.def.cmake +@@ -79,3 +79,6 @@ EXPORTS + @_EXPAT_COMMENT_DTD_OR_GE@ XML_SetBillionLaughsAttackProtectionMaximumAmplification @70 + ; added with version 2.6.0 + XML_SetReparseDeferralEnabled @71 ++; added with version 2.7.2 ++@_EXPAT_COMMENT_DTD_OR_GE@ XML_SetAllocTrackerMaximumAmplification @72 ++@_EXPAT_COMMENT_DTD_OR_GE@ XML_SetAllocTrackerActivationThreshold @73 +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index 9f0a8b3e..fcf1cfdd 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -452,6 +452,14 @@ typedef struct accounting { + unsigned long long activationThresholdBytes; + } ACCOUNTING; + ++typedef struct MALLOC_TRACKER { ++ XmlBigCount bytesAllocated; ++ XmlBigCount peakBytesAllocated; // updated live only for debug level >=2 ++ unsigned long debugLevel; ++ float maximumAmplificationFactor; // >=1.0 ++ XmlBigCount activationThresholdBytes; ++} MALLOC_TRACKER; ++ + typedef struct entity_stats { + unsigned int countEverOpened; + unsigned int currentDepth; +@@ -599,7 +607,8 @@ static XML_Bool startParsing(XML_Parser parser); + + static XML_Parser parserCreate(const XML_Char *encodingName, + const XML_Memory_Handling_Suite *memsuite, +- const XML_Char *nameSep, DTD *dtd); ++ const XML_Char *nameSep, DTD *dtd, ++ XML_Parser parentParser); + + static void parserInit(XML_Parser parser, const XML_Char *encodingName); + +@@ -769,14 +778,220 @@ struct XML_ParserStruct { + unsigned long m_hash_secret_salt; + #if XML_GE == 1 + ACCOUNTING m_accounting; ++ MALLOC_TRACKER m_alloc_tracker; + ENTITY_STATS m_entity_stats; + #endif + XML_Bool m_reenter; + }; + +-#define MALLOC(parser, s) (parser->m_mem.malloc_fcn((s))) +-#define REALLOC(parser, p, s) (parser->m_mem.realloc_fcn((p), (s))) +-#define FREE(parser, p) (parser->m_mem.free_fcn((p))) ++#if XML_GE == 1 ++# define MALLOC(parser, s) (expat_malloc((parser), (s), __LINE__)) ++# define REALLOC(parser, p, s) (expat_realloc((parser), (p), (s), __LINE__)) ++# define FREE(parser, p) (expat_free((parser), (p), __LINE__)) ++#else ++# define MALLOC(parser, s) (parser->m_mem.malloc_fcn((s))) ++# define REALLOC(parser, p, s) (parser->m_mem.realloc_fcn((p), (s))) ++# define FREE(parser, p) (parser->m_mem.free_fcn((p))) ++#endif ++ ++#if XML_GE == 1 ++static void ++expat_heap_stat(XML_Parser rootParser, char operator, XmlBigCount absDiff, ++ XmlBigCount newTotal, XmlBigCount peakTotal, int sourceLine) { ++ // NOTE: This can be +infinity or -nan ++ const float amplification ++ = (float)newTotal / (float)rootParser->m_accounting.countBytesDirect; ++ fprintf( ++ stderr, ++ "expat: Allocations(%p): Direct " EXPAT_FMT_ULL("10") ", allocated %c" EXPAT_FMT_ULL( ++ "10") " to " EXPAT_FMT_ULL("10") " (" EXPAT_FMT_ULL("10") " peak), amplification %8.2f (xmlparse.c:%d)\n", ++ (void *)rootParser, rootParser->m_accounting.countBytesDirect, operator, ++ absDiff, newTotal, peakTotal, (double)amplification, sourceLine); ++} ++ ++static bool ++expat_heap_increase_tolerable(XML_Parser rootParser, XmlBigCount increase, ++ int sourceLine) { ++ assert(rootParser != NULL); ++ assert(increase > 0); ++ ++ XmlBigCount newTotal = 0; ++ bool tolerable = true; ++ ++ // Detect integer overflow ++ if ((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated < increase) { ++ tolerable = false; ++ } else { ++ newTotal = rootParser->m_alloc_tracker.bytesAllocated + increase; ++ ++ if (newTotal >= rootParser->m_alloc_tracker.activationThresholdBytes) { ++ assert(newTotal > 0); ++ // NOTE: This can be +infinity when dividing by zero but not -nan ++ const float amplification ++ = (float)newTotal / (float)rootParser->m_accounting.countBytesDirect; ++ if (amplification ++ > rootParser->m_alloc_tracker.maximumAmplificationFactor) { ++ tolerable = false; ++ } ++ } ++ } ++ ++ if (! tolerable && (rootParser->m_alloc_tracker.debugLevel >= 1)) { ++ expat_heap_stat(rootParser, '+', increase, newTotal, newTotal, sourceLine); ++ } ++ ++ return tolerable; ++} ++ ++static void * ++expat_malloc(XML_Parser parser, size_t size, int sourceLine) { ++ // Detect integer overflow ++ if (SIZE_MAX - size < sizeof(size_t)) { ++ return NULL; ++ } ++ ++ const XML_Parser rootParser = getRootParserOf(parser, NULL); ++ assert(rootParser->m_parentParser == NULL); ++ ++ const size_t bytesToAllocate = sizeof(size_t) + size; ++ ++ if ((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated ++ < bytesToAllocate) { ++ return NULL; // i.e. signal integer overflow as out-of-memory ++ } ++ ++ if (! expat_heap_increase_tolerable(rootParser, bytesToAllocate, ++ sourceLine)) { ++ return NULL; // i.e. signal violation as out-of-memory ++ } ++ ++ // Actually allocate ++ void *const mallocedPtr = parser->m_mem.malloc_fcn(bytesToAllocate); ++ ++ if (mallocedPtr == NULL) { ++ return NULL; ++ } ++ ++ // Update in-block recorded size ++ *(size_t *)mallocedPtr = size; ++ ++ // Update accounting ++ rootParser->m_alloc_tracker.bytesAllocated += bytesToAllocate; ++ ++ // Report as needed ++ if (rootParser->m_alloc_tracker.debugLevel >= 2) { ++ if (rootParser->m_alloc_tracker.bytesAllocated ++ > rootParser->m_alloc_tracker.peakBytesAllocated) { ++ rootParser->m_alloc_tracker.peakBytesAllocated ++ = rootParser->m_alloc_tracker.bytesAllocated; ++ } ++ expat_heap_stat(rootParser, '+', bytesToAllocate, ++ rootParser->m_alloc_tracker.bytesAllocated, ++ rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine); ++ } ++ ++ return (char *)mallocedPtr + sizeof(size_t); ++} ++ ++static void ++expat_free(XML_Parser parser, void *ptr, int sourceLine) { ++ assert(parser != NULL); ++ ++ if (ptr == NULL) { ++ return; ++ } ++ ++ const XML_Parser rootParser = getRootParserOf(parser, NULL); ++ assert(rootParser->m_parentParser == NULL); ++ ++ // Extract size (to the eyes of malloc_fcn/realloc_fcn) and ++ // the original pointer returned by malloc/realloc ++ void *const mallocedPtr = (char *)ptr - sizeof(size_t); ++ const size_t bytesAllocated = sizeof(size_t) + *(size_t *)mallocedPtr; ++ ++ // Update accounting ++ assert(rootParser->m_alloc_tracker.bytesAllocated >= bytesAllocated); ++ rootParser->m_alloc_tracker.bytesAllocated -= bytesAllocated; ++ ++ // Report as needed ++ if (rootParser->m_alloc_tracker.debugLevel >= 2) { ++ expat_heap_stat(rootParser, '-', bytesAllocated, ++ rootParser->m_alloc_tracker.bytesAllocated, ++ rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine); ++ } ++ ++ // NOTE: This may be freeing rootParser, so freeing has to come last ++ parser->m_mem.free_fcn(mallocedPtr); ++} ++ ++static void * ++expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine) { ++ assert(parser != NULL); ++ ++ if (ptr == NULL) { ++ return expat_malloc(parser, size, sourceLine); ++ } ++ ++ if (size == 0) { ++ expat_free(parser, ptr, sourceLine); ++ return NULL; ++ } ++ ++ const XML_Parser rootParser = getRootParserOf(parser, NULL); ++ assert(rootParser->m_parentParser == NULL); ++ ++ // Extract original size (to the eyes of the caller) and the original ++ // pointer returned by malloc/realloc ++ void *mallocedPtr = (char *)ptr - sizeof(size_t); ++ const size_t prevSize = *(size_t *)mallocedPtr; ++ ++ // Classify upcoming change ++ const bool isIncrease = (size > prevSize); ++ const size_t absDiff ++ = (size > prevSize) ? (size - prevSize) : (prevSize - size); ++ ++ // Ask for permission from accounting ++ if (isIncrease) { ++ if (! expat_heap_increase_tolerable(rootParser, absDiff, sourceLine)) { ++ return NULL; // i.e. signal violation as out-of-memory ++ } ++ } ++ ++ // Actually allocate ++ mallocedPtr = parser->m_mem.realloc_fcn(mallocedPtr, sizeof(size_t) + size); ++ ++ if (mallocedPtr == NULL) { ++ return NULL; ++ } ++ ++ // Update accounting ++ if (isIncrease) { ++ assert((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated ++ >= absDiff); ++ rootParser->m_alloc_tracker.bytesAllocated += absDiff; ++ } else { // i.e. decrease ++ assert(rootParser->m_alloc_tracker.bytesAllocated >= absDiff); ++ rootParser->m_alloc_tracker.bytesAllocated -= absDiff; ++ } ++ ++ // Report as needed ++ if (rootParser->m_alloc_tracker.debugLevel >= 2) { ++ if (rootParser->m_alloc_tracker.bytesAllocated ++ > rootParser->m_alloc_tracker.peakBytesAllocated) { ++ rootParser->m_alloc_tracker.peakBytesAllocated ++ = rootParser->m_alloc_tracker.bytesAllocated; ++ } ++ expat_heap_stat(rootParser, isIncrease ? '+' : '-', absDiff, ++ rootParser->m_alloc_tracker.bytesAllocated, ++ rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine); ++ } ++ ++ // Update in-block recorded size ++ *(size_t *)mallocedPtr = size; ++ ++ return (char *)mallocedPtr + sizeof(size_t); ++} ++#endif // XML_GE == 1 + + XML_Parser XMLCALL + XML_ParserCreate(const XML_Char *encodingName) { +@@ -1096,19 +1311,40 @@ XML_Parser XMLCALL + XML_ParserCreate_MM(const XML_Char *encodingName, + const XML_Memory_Handling_Suite *memsuite, + const XML_Char *nameSep) { +- return parserCreate(encodingName, memsuite, nameSep, NULL); ++ return parserCreate(encodingName, memsuite, nameSep, NULL, NULL); + } + + static XML_Parser + parserCreate(const XML_Char *encodingName, + const XML_Memory_Handling_Suite *memsuite, const XML_Char *nameSep, +- DTD *dtd) { +- XML_Parser parser; ++ DTD *dtd, XML_Parser parentParser) { ++ XML_Parser parser = NULL; ++ ++#if XML_GE == 1 ++ const size_t increase = sizeof(size_t) + sizeof(struct XML_ParserStruct); ++ ++ if (parentParser != NULL) { ++ const XML_Parser rootParser = getRootParserOf(parentParser, NULL); ++ if (! expat_heap_increase_tolerable(rootParser, increase, __LINE__)) { ++ return NULL; ++ } ++ } ++#else ++ UNUSED_P(parentParser); ++#endif + + if (memsuite) { + XML_Memory_Handling_Suite *mtemp; ++#if XML_GE == 1 ++ void *const sizeAndParser = memsuite->malloc_fcn( ++ sizeof(size_t) + sizeof(struct XML_ParserStruct)); ++ if (sizeAndParser != NULL) { ++ *(size_t *)sizeAndParser = sizeof(struct XML_ParserStruct); ++ parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t)); ++#else + parser = memsuite->malloc_fcn(sizeof(struct XML_ParserStruct)); + if (parser != NULL) { ++#endif + mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem); + mtemp->malloc_fcn = memsuite->malloc_fcn; + mtemp->realloc_fcn = memsuite->realloc_fcn; +@@ -1116,18 +1352,67 @@ parserCreate(const XML_Char *encodingName, + } + } else { + XML_Memory_Handling_Suite *mtemp; ++#if XML_GE == 1 ++ void *const sizeAndParser ++ = (XML_Parser)malloc(sizeof(size_t) + sizeof(struct XML_ParserStruct)); ++ if (sizeAndParser != NULL) { ++ *(size_t *)sizeAndParser = sizeof(struct XML_ParserStruct); ++ parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t)); ++#else + parser = (XML_Parser)malloc(sizeof(struct XML_ParserStruct)); + if (parser != NULL) { ++#endif + mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem); + mtemp->malloc_fcn = malloc; + mtemp->realloc_fcn = realloc; + mtemp->free_fcn = free; + } +- } ++ } // cppcheck-suppress[memleak symbolName=sizeAndParser] // Cppcheck >=2.18.0 + + if (! parser) + return parser; + ++#if XML_GE == 1 ++ // Initialize .m_alloc_tracker ++ memset(&parser->m_alloc_tracker, 0, sizeof(MALLOC_TRACKER)); ++ if (parentParser == NULL) { ++ parser->m_alloc_tracker.debugLevel ++ = getDebugLevel("EXPAT_MALLOC_DEBUG", 0u); ++ parser->m_alloc_tracker.maximumAmplificationFactor ++ = EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT; ++ parser->m_alloc_tracker.activationThresholdBytes ++ = EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT; ++ ++ // NOTE: This initialization needs to come this early because these fields ++ // are read by allocation tracking code ++ parser->m_parentParser = NULL; ++ parser->m_accounting.countBytesDirect = 0; ++ } else { ++ parser->m_parentParser = parentParser; ++ } ++ ++ // Record XML_ParserStruct allocation we did a few lines up before ++ const XML_Parser rootParser = getRootParserOf(parser, NULL); ++ assert(rootParser->m_parentParser == NULL); ++ assert(SIZE_MAX - rootParser->m_alloc_tracker.bytesAllocated >= increase); ++ rootParser->m_alloc_tracker.bytesAllocated += increase; ++ ++ // Report on allocation ++ if (rootParser->m_alloc_tracker.debugLevel >= 2) { ++ if (rootParser->m_alloc_tracker.bytesAllocated ++ > rootParser->m_alloc_tracker.peakBytesAllocated) { ++ rootParser->m_alloc_tracker.peakBytesAllocated ++ = rootParser->m_alloc_tracker.bytesAllocated; ++ } ++ ++ expat_heap_stat(rootParser, '+', increase, ++ rootParser->m_alloc_tracker.bytesAllocated, ++ rootParser->m_alloc_tracker.peakBytesAllocated, __LINE__); ++ } ++#else ++ parser->m_parentParser = NULL; ++#endif // XML_GE == 1 ++ + parser->m_buffer = NULL; + parser->m_bufferLim = NULL; + +@@ -1291,7 +1576,6 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) { + parser->m_unknownEncodingMem = NULL; + parser->m_unknownEncodingRelease = NULL; + parser->m_unknownEncodingData = NULL; +- parser->m_parentParser = NULL; + parser->m_parsingStatus.parsing = XML_INITIALIZED; + // Reentry can only be triggered inside m_processor calls + parser->m_reenter = XML_FALSE; +@@ -1526,9 +1810,10 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context, + */ + if (parser->m_ns) { + XML_Char tmp[2] = {parser->m_namespaceSeparator, 0}; +- parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd); ++ parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd, oldParser); + } else { +- parser = parserCreate(encodingName, &parser->m_mem, NULL, newDtd); ++ parser ++ = parserCreate(encodingName, &parser->m_mem, NULL, newDtd, oldParser); + } + + if (! parser) +@@ -2708,6 +2993,13 @@ XML_GetFeatureList(void) { + EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT}, + /* Added in Expat 2.6.0. */ + {XML_FEATURE_GE, XML_L("XML_GE"), 0}, ++ /* Added in Expat 2.7.2. */ ++ {XML_FEATURE_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT, ++ XML_L("XML_AT_MAX_AMP"), ++ (long int)EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT}, ++ {XML_FEATURE_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT, ++ XML_L("XML_AT_ACT_THRES"), ++ (long int)EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT}, + #endif + {XML_FEATURE_END, NULL, 0}}; + +@@ -2736,6 +3028,29 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold( + parser->m_accounting.activationThresholdBytes = activationThresholdBytes; + return XML_TRUE; + } ++ ++XML_Bool XMLCALL ++XML_SetAllocTrackerMaximumAmplification(XML_Parser parser, ++ float maximumAmplificationFactor) { ++ if ((parser == NULL) || (parser->m_parentParser != NULL) ++ || isnan(maximumAmplificationFactor) ++ || (maximumAmplificationFactor < 1.0f)) { ++ return XML_FALSE; ++ } ++ parser->m_alloc_tracker.maximumAmplificationFactor ++ = maximumAmplificationFactor; ++ return XML_TRUE; ++} ++ ++XML_Bool XMLCALL ++XML_SetAllocTrackerActivationThreshold( ++ XML_Parser parser, unsigned long long activationThresholdBytes) { ++ if ((parser == NULL) || (parser->m_parentParser != NULL)) { ++ return XML_FALSE; ++ } ++ parser->m_alloc_tracker.activationThresholdBytes = activationThresholdBytes; ++ return XML_TRUE; ++} + #endif /* XML_GE == 1 */ + + XML_Bool XMLCALL +diff --git a/tests/basic_tests.c b/tests/basic_tests.c +index 129db1d8..0231e094 100644 +--- a/tests/basic_tests.c ++++ b/tests/basic_tests.c +@@ -3089,6 +3089,10 @@ START_TEST(test_buffer_can_grow_to_max) { + for (int i = 0; i < num_prefixes; ++i) { + set_subtest("\"%s\"", prefixes[i]); + XML_Parser parser = XML_ParserCreate(NULL); ++#if XML_GE == 1 ++ assert_true(XML_SetAllocTrackerActivationThreshold(parser, (size_t)-1) ++ == XML_TRUE); // i.e. deactivate ++#endif + const int prefix_len = (int)strlen(prefixes[i]); + const enum XML_Status s + = _XML_Parse_SINGLE_BYTES(parser, prefixes[i], prefix_len, XML_FALSE); +diff --git a/tests/nsalloc_tests.c b/tests/nsalloc_tests.c +index 48520f42..0a594e14 100644 +--- a/tests/nsalloc_tests.c ++++ b/tests/nsalloc_tests.c +@@ -454,10 +454,15 @@ START_TEST(test_nsalloc_realloc_attributes) { + nsalloc_teardown(); + nsalloc_setup(); + } ++#if XML_GE == 1 ++ assert_true( ++ i == 0); // because expat_realloc relies on expat_malloc to some extent ++#else + if (i == 0) + fail("Parsing worked despite failing reallocations"); + else if (i == max_realloc_count) + fail("Parsing failed at max reallocation count"); ++#endif + } + END_TEST + +diff --git a/xmlwf/xmlwf.c b/xmlwf/xmlwf.c +index 8cfc73ca..b9d0a7fc 100644 +--- a/xmlwf/xmlwf.c ++++ b/xmlwf/xmlwf.c +@@ -933,6 +933,8 @@ usage(const XML_Char *prog, int rc) { + T(" Control verbosity of entity debugging (default: 0)\n") + T(" EXPAT_ENTROPY_DEBUG=(0|1)\n") + T(" Control verbosity of entropy debugging (default: 0)\n") ++ T(" EXPAT_MALLOC_DEBUG=(0|1|2)\n") ++ T(" Control verbosity of allocation tracker (default: 0)\n") + T("\n") + T("exit status:\n") + T(" 0 the input files are well-formed and the output (if requested) was written successfully\n") +diff --git a/xmlwf/xmlwf_helpgen.py b/xmlwf/xmlwf_helpgen.py +index 39a3dc13..2360820d 100755 +--- a/xmlwf/xmlwf_helpgen.py ++++ b/xmlwf/xmlwf_helpgen.py +@@ -39,6 +39,8 @@ environment variables: + Control verbosity of entity debugging (default: 0) + EXPAT_ENTROPY_DEBUG=(0|1) + Control verbosity of entropy debugging (default: 0) ++ EXPAT_MALLOC_DEBUG=(0|1|2) ++ Control verbosity of allocation tracker (default: 0) + + exit status: + 0 the input files are well-formed and the output (if requested) was written successfully diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-09.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-09.patch new file mode 100644 index 00000000000..364c28183ac --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-09.patch @@ -0,0 +1,43 @@ +From 1270e5bc0836d296ac4970fc9e1cf53d83972083 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping <sebastian@pipping.org> +Date: Sun, 7 Sep 2025 12:18:08 +0200 +Subject: [PATCH] lib: Make XML_MemFree and XML_FreeContentModel match their + siblings + +.. XML_MemMalloc and XML_MemRealloc in structure, prior to upcoming changes + +CVE: CVE-2025-59375 +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/1270e5bc0836d296ac4970fc9e1cf53d83972083] +Signed-off-by: Peter Marko <peter.marko@siemens.com> +--- + lib/xmlparse.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index fcf1cfdd..5d27cd45 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -2772,8 +2772,9 @@ XML_GetCurrentColumnNumber(XML_Parser parser) { + + void XMLCALL + XML_FreeContentModel(XML_Parser parser, XML_Content *model) { +- if (parser != NULL) +- FREE(parser, model); ++ if (parser == NULL) ++ return; ++ FREE(parser, model); + } + + void *XMLCALL +@@ -2792,8 +2793,9 @@ XML_MemRealloc(XML_Parser parser, void *ptr, size_t size) { + + void XMLCALL + XML_MemFree(XML_Parser parser, void *ptr) { +- if (parser != NULL) +- FREE(parser, ptr); ++ if (parser == NULL) ++ return; ++ FREE(parser, ptr); + } + + void XMLCALL diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-10.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-10.patch new file mode 100644 index 00000000000..fe5452000e1 --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-10.patch @@ -0,0 +1,54 @@ +From 96c7467281c72028aada525c1d3822512758b266 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping <sebastian@pipping.org> +Date: Sun, 7 Sep 2025 12:06:43 +0200 +Subject: [PATCH] lib: Exclude XML_Mem* functions from allocation tracking + +.. so that allocations by the user application +are not being limited. + +CVE: CVE-2025-59375 +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/96c7467281c72028aada525c1d3822512758b266] +Signed-off-by: Peter Marko <peter.marko@siemens.com> +--- + lib/xmlparse.c | 16 +++++++++++++--- + 1 file changed, 13 insertions(+), 3 deletions(-) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index 5d27cd45..8145a049 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -2781,21 +2781,31 @@ void *XMLCALL + XML_MemMalloc(XML_Parser parser, size_t size) { + if (parser == NULL) + return NULL; +- return MALLOC(parser, size); ++ ++ // NOTE: We are avoiding MALLOC(..) here to not include ++ // user allocations with allocation tracking and limiting. ++ return parser->m_mem.malloc_fcn(size); + } + + void *XMLCALL + XML_MemRealloc(XML_Parser parser, void *ptr, size_t size) { + if (parser == NULL) + return NULL; +- return REALLOC(parser, ptr, size); ++ ++ // NOTE: We are avoiding REALLOC(..) here to not include ++ // user allocations with allocation tracking and limiting. ++ return parser->m_mem.realloc_fcn(ptr, size); + } + + void XMLCALL + XML_MemFree(XML_Parser parser, void *ptr) { + if (parser == NULL) + return; +- FREE(parser, ptr); ++ ++ // NOTE: We are avoiding FREE(..) here because XML_MemMalloc and ++ // XML_MemRealloc are not using MALLOC(..) and REALLOC(..) ++ // but plain .malloc_fcn(..) and .realloc_fcn(..), internally. ++ parser->m_mem.free_fcn(ptr); + } + + void XMLCALL diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-11.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-11.patch new file mode 100644 index 00000000000..be892a78046 --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-11.patch @@ -0,0 +1,66 @@ +From ae4086198d710a62a0a1560007b81307dba72909 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping <sebastian@pipping.org> +Date: Tue, 9 Sep 2025 21:34:28 +0200 +Subject: [PATCH] lib: Exclude the main input buffer from allocation tracking + +.. so that control of the input buffer size remains with the +application using Expat + +CVE: CVE-2025-59375 +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/ae4086198d710a62a0a1560007b81307dba72909] +Signed-off-by: Peter Marko <peter.marko@siemens.com> +--- + lib/xmlparse.c | 19 +++++++++++++++---- + 1 file changed, 15 insertions(+), 4 deletions(-) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index 8145a049..00139b94 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -1975,7 +1975,10 @@ XML_ParserFree(XML_Parser parser) { + FREE(parser, (void *)parser->m_attInfo); + #endif + FREE(parser, parser->m_groupConnector); +- FREE(parser, parser->m_buffer); ++ // NOTE: We are avoiding FREE(..) here because parser->m_buffer ++ // is not being allocated with MALLOC(..) but with plain ++ // .malloc_fcn(..). ++ parser->m_mem.free_fcn(parser->m_buffer); + FREE(parser, parser->m_dataBuf); + FREE(parser, parser->m_nsAtts); + FREE(parser, parser->m_unknownEncodingMem); +@@ -2567,7 +2570,9 @@ XML_GetBuffer(XML_Parser parser, int len) { + parser->m_errorCode = XML_ERROR_NO_MEMORY; + return NULL; + } +- newBuf = (char *)MALLOC(parser, bufferSize); ++ // NOTE: We are avoiding MALLOC(..) here to leave limiting ++ // the input size to the application using Expat. ++ newBuf = (char *)parser->m_mem.malloc_fcn(bufferSize); + if (newBuf == 0) { + parser->m_errorCode = XML_ERROR_NO_MEMORY; + return NULL; +@@ -2578,7 +2583,10 @@ XML_GetBuffer(XML_Parser parser, int len) { + memcpy(newBuf, &parser->m_bufferPtr[-keep], + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr) + + keep); +- FREE(parser, parser->m_buffer); ++ // NOTE: We are avoiding FREE(..) here because parser->m_buffer ++ // is not being allocated with MALLOC(..) but with plain ++ // .malloc_fcn(..). ++ parser->m_mem.free_fcn(parser->m_buffer); + parser->m_buffer = newBuf; + parser->m_bufferEnd + = parser->m_buffer +@@ -2594,7 +2602,10 @@ XML_GetBuffer(XML_Parser parser, int len) { + if (parser->m_bufferPtr) { + memcpy(newBuf, parser->m_bufferPtr, + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr)); +- FREE(parser, parser->m_buffer); ++ // NOTE: We are avoiding FREE(..) here because parser->m_buffer ++ // is not being allocated with MALLOC(..) but with plain ++ // .malloc_fcn(..). ++ parser->m_mem.free_fcn(parser->m_buffer); + parser->m_bufferEnd + = newBuf + + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr); diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-12.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-12.patch new file mode 100644 index 00000000000..9e036a52849 --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-12.patch @@ -0,0 +1,58 @@ +From 7e35240dc97e9fd4f609e31f27c27b659535e436 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping <sebastian@pipping.org> +Date: Thu, 11 Sep 2025 00:27:05 +0200 +Subject: [PATCH] lib: Exclude the content model from allocation tracking + +.. so that applications that are not using XML_FreeContentModel +but plain free(..) or .free_fcn() to free the content model's +memory are safe + +CVE: CVE-2025-59375 +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/7e35240dc97e9fd4f609e31f27c27b659535e436] +Signed-off-by: Peter Marko <peter.marko@siemens.com> +--- + lib/xmlparse.c | 16 +++++++++++++--- + 1 file changed, 13 insertions(+), 3 deletions(-) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index 00139b94..d0b6e0cd 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -2785,7 +2785,10 @@ void XMLCALL + XML_FreeContentModel(XML_Parser parser, XML_Content *model) { + if (parser == NULL) + return; +- FREE(parser, model); ++ ++ // NOTE: We are avoiding FREE(..) here because the content model ++ // has been created using plain .malloc_fcn(..) rather than MALLOC(..). ++ parser->m_mem.free_fcn(model); + } + + void *XMLCALL +@@ -6063,8 +6066,12 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, + case XML_ROLE_CONTENT_EMPTY: + if (dtd->in_eldecl) { + if (parser->m_elementDeclHandler) { ++ // NOTE: We are avoiding MALLOC(..) here to so that ++ // applications that are not using XML_FreeContentModel but ++ // plain free(..) or .free_fcn() to free the content model's ++ // memory are safe. + XML_Content *content +- = (XML_Content *)MALLOC(parser, sizeof(XML_Content)); ++ = (XML_Content *)parser->m_mem.malloc_fcn(sizeof(XML_Content)); + if (! content) + return XML_ERROR_NO_MEMORY; + content->quant = XML_CQUANT_NONE; +@@ -8278,7 +8285,10 @@ build_model(XML_Parser parser) { + const size_t allocsize = (dtd->scaffCount * sizeof(XML_Content) + + (dtd->contentStringLen * sizeof(XML_Char))); + +- ret = (XML_Content *)MALLOC(parser, allocsize); ++ // NOTE: We are avoiding MALLOC(..) here to so that ++ // applications that are not using XML_FreeContentModel but plain ++ // free(..) or .free_fcn() to free the content model's memory are safe. ++ ret = (XML_Content *)parser->m_mem.malloc_fcn(allocsize); + if (! ret) + return NULL; + diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-13.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-13.patch new file mode 100644 index 00000000000..209dd83a4b6 --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-13.patch @@ -0,0 +1,309 @@ +From 31f9053c3c46741f4daf2ea2bdea75f40f720d42 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping <sebastian@pipping.org> +Date: Tue, 2 Sep 2025 22:36:49 +0200 +Subject: [PATCH] tests: Cover allocation tracking and limiting with tests + +CVE: CVE-2025-59375 +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/31f9053c3c46741f4daf2ea2bdea75f40f720d42] +Signed-off-by: Peter Marko <peter.marko@siemens.com> +--- + lib/internal.h | 3 + + lib/xmlparse.c | 12 +++ + tests/alloc_tests.c | 214 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 229 insertions(+) + +diff --git a/lib/internal.h b/lib/internal.h +index eb67cf50..6e087858 100644 +--- a/lib/internal.h ++++ b/lib/internal.h +@@ -173,6 +173,9 @@ extern + #endif + XML_Bool g_reparseDeferralEnabledDefault; // written ONLY in runtests.c + #if defined(XML_TESTING) ++void *expat_malloc(XML_Parser parser, size_t size, int sourceLine); ++void expat_free(XML_Parser parser, void *ptr, int sourceLine); ++void *expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine); + extern unsigned int g_bytesScanned; // used for testing only + #endif + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index d0b6e0cd..6e9c6fb2 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -843,7 +843,11 @@ expat_heap_increase_tolerable(XML_Parser rootParser, XmlBigCount increase, + return tolerable; + } + ++# if defined(XML_TESTING) ++void * ++# else + static void * ++# endif + expat_malloc(XML_Parser parser, size_t size, int sourceLine) { + // Detect integer overflow + if (SIZE_MAX - size < sizeof(size_t)) { +@@ -893,7 +897,11 @@ expat_malloc(XML_Parser parser, size_t size, int sourceLine) { + return (char *)mallocedPtr + sizeof(size_t); + } + ++# if defined(XML_TESTING) ++void ++# else + static void ++# endif + expat_free(XML_Parser parser, void *ptr, int sourceLine) { + assert(parser != NULL); + +@@ -924,7 +932,11 @@ expat_free(XML_Parser parser, void *ptr, int sourceLine) { + parser->m_mem.free_fcn(mallocedPtr); + } + ++# if defined(XML_TESTING) ++void * ++# else + static void * ++# endif + expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine) { + assert(parser != NULL); + +diff --git a/tests/alloc_tests.c b/tests/alloc_tests.c +index 4c3e2af4..275f92d5 100644 +--- a/tests/alloc_tests.c ++++ b/tests/alloc_tests.c +@@ -46,10 +46,16 @@ + # undef NDEBUG /* because test suite relies on assert(...) at the moment */ + #endif + ++#include <math.h> /* NAN, INFINITY */ ++#include <stdbool.h> ++#include <stdint.h> /* for SIZE_MAX */ + #include <string.h> + #include <assert.h> + ++#include "expat_config.h" ++ + #include "expat.h" ++#include "internal.h" + #include "common.h" + #include "minicheck.h" + #include "dummy.h" +@@ -2085,6 +2091,203 @@ START_TEST(test_alloc_reset_after_external_entity_parser_create_fail) { + } + END_TEST + ++START_TEST(test_alloc_tracker_size_recorded) { ++ XML_Memory_Handling_Suite memsuite = {malloc, realloc, free}; ++ ++ bool values[] = {true, false}; ++ for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++) { ++ const bool useMemSuite = values[i]; ++ set_subtest("useMemSuite=%d", (int)useMemSuite); ++ XML_Parser parser = useMemSuite ++ ? XML_ParserCreate_MM(NULL, &memsuite, XCS("|")) ++ : XML_ParserCreate(NULL); ++ ++#if XML_GE == 1 ++ void *ptr = expat_malloc(parser, 10, -1); ++ ++ assert_true(ptr != NULL); ++ assert_true(*((size_t *)ptr - 1) == 10); ++ ++ assert_true(expat_realloc(parser, ptr, SIZE_MAX / 2, -1) == NULL); ++ ++ assert_true(*((size_t *)ptr - 1) == 10); // i.e. unchanged ++ ++ ptr = expat_realloc(parser, ptr, 20, -1); ++ ++ assert_true(ptr != NULL); ++ assert_true(*((size_t *)ptr - 1) == 20); ++ ++ expat_free(parser, ptr, -1); ++#endif ++ ++ XML_ParserFree(parser); ++ } ++} ++END_TEST ++ ++START_TEST(test_alloc_tracker_maximum_amplification) { ++ if (g_reparseDeferralEnabledDefault == XML_TRUE) { ++ return; ++ } ++ ++ XML_Parser parser = XML_ParserCreate(NULL); ++ ++ // Get .m_accounting.countBytesDirect from 0 to 3 ++ const char *const chunk = "<e>"; ++ assert_true(_XML_Parse_SINGLE_BYTES(parser, chunk, (int)strlen(chunk), ++ /*isFinal=*/XML_FALSE) ++ == XML_STATUS_OK); ++ ++#if XML_GE == 1 ++ // Stop activation threshold from interfering ++ assert_true(XML_SetAllocTrackerActivationThreshold(parser, 0) == XML_TRUE); ++ ++ // Exceed maximum amplification: should be rejected. ++ assert_true(expat_malloc(parser, 1000, -1) == NULL); ++ ++ // Increase maximum amplification, and try the same amount once more: should ++ // work. ++ assert_true(XML_SetAllocTrackerMaximumAmplification(parser, 3000.0f) ++ == XML_TRUE); ++ ++ void *const ptr = expat_malloc(parser, 1000, -1); ++ assert_true(ptr != NULL); ++ expat_free(parser, ptr, -1); ++#endif ++ ++ XML_ParserFree(parser); ++} ++END_TEST ++ ++START_TEST(test_alloc_tracker_threshold) { ++ XML_Parser parser = XML_ParserCreate(NULL); ++ ++#if XML_GE == 1 ++ // Exceed maximum amplification *before* (default) threshold: should work. ++ void *const ptr = expat_malloc(parser, 1000, -1); ++ assert_true(ptr != NULL); ++ expat_free(parser, ptr, -1); ++ ++ // Exceed maximum amplification *after* threshold: should be rejected. ++ assert_true(XML_SetAllocTrackerActivationThreshold(parser, 999) == XML_TRUE); ++ assert_true(expat_malloc(parser, 1000, -1) == NULL); ++#endif ++ ++ XML_ParserFree(parser); ++} ++END_TEST ++ ++START_TEST(test_alloc_tracker_getbuffer_unlimited) { ++ XML_Parser parser = XML_ParserCreate(NULL); ++ ++#if XML_GE == 1 ++ // Artificially lower threshold ++ assert_true(XML_SetAllocTrackerActivationThreshold(parser, 0) == XML_TRUE); ++ ++ // Self-test: Prove that threshold is as rejecting as expected ++ assert_true(expat_malloc(parser, 1000, -1) == NULL); ++#endif ++ // XML_GetBuffer should be allowed to pass, though ++ assert_true(XML_GetBuffer(parser, 1000) != NULL); ++ ++ XML_ParserFree(parser); ++} ++END_TEST ++ ++START_TEST(test_alloc_tracker_api) { ++ XML_Parser parserWithoutParent = XML_ParserCreate(NULL); ++ XML_Parser parserWithParent = XML_ExternalEntityParserCreate( ++ parserWithoutParent, XCS("entity123"), NULL); ++ if (parserWithoutParent == NULL) ++ fail("parserWithoutParent is NULL"); ++ if (parserWithParent == NULL) ++ fail("parserWithParent is NULL"); ++ ++#if XML_GE == 1 ++ // XML_SetAllocTrackerMaximumAmplification, error cases ++ if (XML_SetAllocTrackerMaximumAmplification(NULL, 123.0f) == XML_TRUE) ++ fail("Call with NULL parser is NOT supposed to succeed"); ++ if (XML_SetAllocTrackerMaximumAmplification(parserWithParent, 123.0f) ++ == XML_TRUE) ++ fail("Call with non-root parser is NOT supposed to succeed"); ++ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, NAN) ++ == XML_TRUE) ++ fail("Call with NaN limit is NOT supposed to succeed"); ++ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, -1.0f) ++ == XML_TRUE) ++ fail("Call with negative limit is NOT supposed to succeed"); ++ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, 0.9f) ++ == XML_TRUE) ++ fail("Call with positive limit <1.0 is NOT supposed to succeed"); ++ ++ // XML_SetAllocTrackerMaximumAmplification, success cases ++ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, 1.0f) ++ == XML_FALSE) ++ fail("Call with positive limit >=1.0 is supposed to succeed"); ++ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, 123456.789f) ++ == XML_FALSE) ++ fail("Call with positive limit >=1.0 is supposed to succeed"); ++ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, INFINITY) ++ == XML_FALSE) ++ fail("Call with positive limit >=1.0 is supposed to succeed"); ++ ++ // XML_SetAllocTrackerActivationThreshold, error cases ++ if (XML_SetAllocTrackerActivationThreshold(NULL, 123) == XML_TRUE) ++ fail("Call with NULL parser is NOT supposed to succeed"); ++ if (XML_SetAllocTrackerActivationThreshold(parserWithParent, 123) == XML_TRUE) ++ fail("Call with non-root parser is NOT supposed to succeed"); ++ ++ // XML_SetAllocTrackerActivationThreshold, success cases ++ if (XML_SetAllocTrackerActivationThreshold(parserWithoutParent, 123) ++ == XML_FALSE) ++ fail("Call with non-NULL parentless parser is supposed to succeed"); ++#endif // XML_GE == 1 ++ ++ XML_ParserFree(parserWithParent); ++ XML_ParserFree(parserWithoutParent); ++} ++END_TEST ++ ++START_TEST(test_mem_api_cycle) { ++ XML_Parser parser = XML_ParserCreate(NULL); ++ ++ void *ptr = XML_MemMalloc(parser, 10); ++ ++ assert_true(ptr != NULL); ++ memset(ptr, 'x', 10); // assert writability, with ASan in mind ++ ++ ptr = XML_MemRealloc(parser, ptr, 20); ++ ++ assert_true(ptr != NULL); ++ memset(ptr, 'y', 20); // assert writability, with ASan in mind ++ ++ XML_MemFree(parser, ptr); ++ ++ XML_ParserFree(parser); ++} ++END_TEST ++ ++START_TEST(test_mem_api_unlimited) { ++ XML_Parser parser = XML_ParserCreate(NULL); ++ ++#if XML_GE == 1 ++ assert_true(XML_SetAllocTrackerActivationThreshold(parser, 0) == XML_TRUE); ++#endif ++ ++ void *ptr = XML_MemMalloc(parser, 1000); ++ ++ assert_true(ptr != NULL); ++ ++ ptr = XML_MemRealloc(parser, ptr, 2000); ++ ++ assert_true(ptr != NULL); ++ ++ XML_MemFree(parser, ptr); ++ ++ XML_ParserFree(parser); ++} ++END_TEST ++ + void + make_alloc_test_case(Suite *s) { + TCase *tc_alloc = tcase_create("allocation tests"); +@@ -2151,4 +2354,15 @@ make_alloc_test_case(Suite *s) { + + tcase_add_test__ifdef_xml_dtd( + tc_alloc, test_alloc_reset_after_external_entity_parser_create_fail); ++ ++ tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_tracker_size_recorded); ++ tcase_add_test__ifdef_xml_dtd(tc_alloc, ++ test_alloc_tracker_maximum_amplification); ++ tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_tracker_threshold); ++ tcase_add_test__ifdef_xml_dtd(tc_alloc, ++ test_alloc_tracker_getbuffer_unlimited); ++ tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_tracker_api); ++ ++ tcase_add_test(tc_alloc, test_mem_api_cycle); ++ tcase_add_test__ifdef_xml_dtd(tc_alloc, test_mem_api_unlimited); + } diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-14.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-14.patch new file mode 100644 index 00000000000..a339cc3f4b2 --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-14.patch @@ -0,0 +1,122 @@ +From 78366891a586f293aeff60a14a55e4afe1169586 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping <sebastian@pipping.org> +Date: Tue, 2 Sep 2025 16:44:00 +0200 +Subject: [PATCH] xmlwf: Wire allocation tracker config to existing arguments + -a and -b + +CVE: CVE-2025-59375 +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/78366891a586f293aeff60a14a55e4afe1169586] +Signed-off-by: Peter Marko <peter.marko@siemens.com> +--- + doc/xmlwf.xml | 26 ++++++++++++++++++++------ + xmlwf/xmlwf.c | 7 +++++-- + xmlwf/xmlwf_helpgen.py | 4 ++-- + 3 files changed, 27 insertions(+), 10 deletions(-) + +diff --git a/doc/xmlwf.xml b/doc/xmlwf.xml +index 17e9cf51..65d8ae9b 100644 +--- a/doc/xmlwf.xml ++++ b/doc/xmlwf.xml +@@ -158,19 +158,31 @@ supports both. + <listitem> + <para> + Sets the maximum tolerated amplification factor +- for protection against billion laughs attacks (default: 100.0). ++ for protection against amplification attacks ++ like the billion laughs attack ++ (default: 100.0 ++ for the sum of direct and indirect output and also ++ for allocations of dynamic memory). + The amplification factor is calculated as .. + </para> + <literallayout> + amplification := (direct + indirect) / direct + </literallayout> + <para> +- .. while parsing, whereas ++ .. with regard to use of entities and .. ++ </para> ++ <literallayout> ++ amplification := allocated / direct ++ </literallayout> ++ <para> ++ .. with regard to dynamic memory while parsing. + <direct> is the number of bytes read +- from the primary document in parsing and ++ from the primary document in parsing, + <indirect> is the number of bytes + added by expanding entities and reading of external DTD files, +- combined. ++ combined, and ++ <allocated> is the total number of bytes of dynamic memory ++ allocated (and not freed) per hierarchy of parsers. + </para> + <para> + <emphasis>NOTE</emphasis>: +@@ -185,8 +197,10 @@ supports both. + <listitem> + <para> + Sets the number of output bytes (including amplification) +- needed to activate protection against billion laughs attacks +- (default: 8 MiB). ++ needed to activate protection against amplification attacks ++ like billion laughs ++ (default: 8 MiB for the sum of direct and indirect output, ++ and 64 MiB for allocations of dynamic memory). + This can be thought of as an "activation threshold". + </para> + <para> +diff --git a/xmlwf/xmlwf.c b/xmlwf/xmlwf.c +index b9d0a7fc..14206d9e 100644 +--- a/xmlwf/xmlwf.c ++++ b/xmlwf/xmlwf.c +@@ -913,11 +913,11 @@ usage(const XML_Char *prog, int rc) { + T(" -t write no XML output for [t]iming of plain parsing\n") + T(" -N enable adding doctype and [n]otation declarations\n") + T("\n") +- T("billion laughs attack protection:\n") ++ T("amplification attack protection (e.g. billion laughs):\n") + T(" NOTE: If you ever need to increase these values for non-attack payload, please file a bug report.\n") + T("\n") + T(" -a FACTOR set maximum tolerated [a]mplification factor (default: 100.0)\n") +- T(" -b BYTES set number of output [b]ytes needed to activate (default: 8 MiB)\n") ++ T(" -b BYTES set number of output [b]ytes needed to activate (default: 8 MiB/64 MiB)\n") + T("\n") + T("reparse deferral:\n") + T(" -q disable reparse deferral, and allow [q]uadratic parse runtime with large tokens\n") +@@ -1181,12 +1181,15 @@ tmain(int argc, XML_Char **argv) { + #if XML_GE == 1 + XML_SetBillionLaughsAttackProtectionMaximumAmplification( + parser, attackMaximumAmplification); ++ XML_SetAllocTrackerMaximumAmplification(parser, ++ attackMaximumAmplification); + #endif + } + if (attackThresholdGiven) { + #if XML_GE == 1 + XML_SetBillionLaughsAttackProtectionActivationThreshold( + parser, attackThresholdBytes); ++ XML_SetAllocTrackerActivationThreshold(parser, attackThresholdBytes); + #else + (void)attackThresholdBytes; // silence -Wunused-but-set-variable + #endif +diff --git a/xmlwf/xmlwf_helpgen.py b/xmlwf/xmlwf_helpgen.py +index 2360820d..e91c285c 100755 +--- a/xmlwf/xmlwf_helpgen.py ++++ b/xmlwf/xmlwf_helpgen.py +@@ -84,13 +84,13 @@ output_mode.add_argument('-m', action='store_true', help='write [m]eta XML, not + output_mode.add_argument('-t', action='store_true', help='write no XML output for [t]iming of plain parsing') + output_related.add_argument('-N', action='store_true', help='enable adding doctype and [n]otation declarations') + +-billion_laughs = parser.add_argument_group('billion laughs attack protection', ++billion_laughs = parser.add_argument_group('amplification attack protection (e.g. billion laughs)', + description='NOTE: ' + 'If you ever need to increase these values ' + 'for non-attack payload, please file a bug report.') + billion_laughs.add_argument('-a', metavar='FACTOR', + help='set maximum tolerated [a]mplification factor (default: 100.0)') +-billion_laughs.add_argument('-b', metavar='BYTES', help='set number of output [b]ytes needed to activate (default: 8 MiB)') ++billion_laughs.add_argument('-b', metavar='BYTES', help='set number of output [b]ytes needed to activate (default: 8 MiB/64 MiB)') + + reparse_deferral = parser.add_argument_group('reparse deferral') + reparse_deferral.add_argument('-q', metavar='FACTOR', diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-15.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-15.patch new file mode 100644 index 00000000000..8d068441928 --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-15.patch @@ -0,0 +1,70 @@ +From 5ae51be57ed0ca1e87582881d07ea9c29c4f7c05 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping <sebastian@pipping.org> +Date: Wed, 3 Sep 2025 17:06:41 +0200 +Subject: [PATCH] fuzz: Be robust towards NULL return from + XML_ExternalEntityParserCreate + +CVE: CVE-2025-59375 +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/5ae51be57ed0ca1e87582881d07ea9c29c4f7c05] +Signed-off-by: Peter Marko <peter.marko@siemens.com> +--- + fuzz/xml_parse_fuzzer.c | 14 ++++++++------ + fuzz/xml_parsebuffer_fuzzer.c | 14 ++++++++------ + 2 files changed, 16 insertions(+), 12 deletions(-) + +diff --git a/fuzz/xml_parse_fuzzer.c b/fuzz/xml_parse_fuzzer.c +index 90c38549..29ab33ff 100644 +--- a/fuzz/xml_parse_fuzzer.c ++++ b/fuzz/xml_parse_fuzzer.c +@@ -89,15 +89,17 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + + XML_Parser externalEntityParser + = XML_ExternalEntityParserCreate(parentParser, "e1", NULL); +- assert(externalEntityParser); +- ParseOneInput(externalEntityParser, data, size); +- XML_ParserFree(externalEntityParser); ++ if (externalEntityParser != NULL) { ++ ParseOneInput(externalEntityParser, data, size); ++ XML_ParserFree(externalEntityParser); ++ } + + XML_Parser externalDtdParser + = XML_ExternalEntityParserCreate(parentParser, NULL, NULL); +- assert(externalDtdParser); +- ParseOneInput(externalDtdParser, data, size); +- XML_ParserFree(externalDtdParser); ++ if (externalDtdParser != NULL) { ++ ParseOneInput(externalDtdParser, data, size); ++ XML_ParserFree(externalDtdParser); ++ } + + // finally frees this parser which served as parent + XML_ParserFree(parentParser); +diff --git a/fuzz/xml_parsebuffer_fuzzer.c b/fuzz/xml_parsebuffer_fuzzer.c +index 0db67dce..38b9981b 100644 +--- a/fuzz/xml_parsebuffer_fuzzer.c ++++ b/fuzz/xml_parsebuffer_fuzzer.c +@@ -101,15 +101,17 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + + XML_Parser externalEntityParser + = XML_ExternalEntityParserCreate(parentParser, "e1", NULL); +- assert(externalEntityParser); +- ParseOneInput(externalEntityParser, data, size); +- XML_ParserFree(externalEntityParser); ++ if (externalEntityParser != NULL) { ++ ParseOneInput(externalEntityParser, data, size); ++ XML_ParserFree(externalEntityParser); ++ } + + XML_Parser externalDtdParser + = XML_ExternalEntityParserCreate(parentParser, NULL, NULL); +- assert(externalDtdParser); +- ParseOneInput(externalDtdParser, data, size); +- XML_ParserFree(externalDtdParser); ++ if (externalDtdParser != NULL) { ++ ParseOneInput(externalDtdParser, data, size); ++ XML_ParserFree(externalDtdParser); ++ } + + // finally frees this parser which served as parent + XML_ParserFree(parentParser); diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-16.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-16.patch new file mode 100644 index 00000000000..a276347d83d --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-16.patch @@ -0,0 +1,146 @@ +From d6246c31a1238d065b4d9690d3bac740326f6485 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping <sebastian@pipping.org> +Date: Wed, 3 Sep 2025 01:28:03 +0200 +Subject: [PATCH] docs: Document the two allocation tracking API functions + +CVE: CVE-2025-59375 +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/d6246c31a1238d065b4d9690d3bac740326f6485] +Signed-off-by: Peter Marko <peter.marko@siemens.com> +--- + doc/reference.html | 116 +++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 116 insertions(+) + +diff --git a/doc/reference.html b/doc/reference.html +index 89476710..81da4e6c 100644 +--- a/doc/reference.html ++++ b/doc/reference.html +@@ -157,6 +157,8 @@ interface.</p> + <ul> + <li><a href="#XML_SetBillionLaughsAttackProtectionMaximumAmplification">XML_SetBillionLaughsAttackProtectionMaximumAmplification</a></li> + <li><a href="#XML_SetBillionLaughsAttackProtectionActivationThreshold">XML_SetBillionLaughsAttackProtectionActivationThreshold</a></li> ++ <li><a href="#XML_SetAllocTrackerMaximumAmplification">XML_SetAllocTrackerMaximumAmplification</a></li> ++ <li><a href="#XML_SetAllocTrackerActivationThreshold">XML_SetAllocTrackerActivationThreshold</a></li> + <li><a href="#XML_SetReparseDeferralEnabled">XML_SetReparseDeferralEnabled</a></li> + </ul> + </li> +@@ -2262,6 +2264,120 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold(XML_Parser p, + </p> + </div> + ++<h4 id="XML_SetAllocTrackerMaximumAmplification">XML_SetAllocTrackerMaximumAmplification</h4> ++<pre class="fcndec"> ++/* Added in Expat 2.7.2. */ ++XML_Bool ++XML_SetAllocTrackerMaximumAmplification(XML_Parser p, ++ float maximumAmplificationFactor); ++</pre> ++<div class="fcndef"> ++ <p> ++ Sets the maximum tolerated amplification factor ++ between direct input and bytes of dynamic memory allocated ++ (default: <code>100.0</code>) ++ of parser <code>p</code> to <code>maximumAmplificationFactor</code>, and ++ returns <code>XML_TRUE</code> upon success and <code>XML_FALSE</code> upon error. ++ </p> ++ ++ <p> ++ <strong>Note:</strong> ++ There are three types of allocations that intentionally bypass tracking and limiting: ++ </p> ++ <ul> ++ <li> ++ application calls to functions ++ <code><a href="#XML_MemMalloc">XML_MemMalloc</a></code> ++ and ++ <code><a href="#XML_MemRealloc">XML_MemRealloc</a></code> ++ — ++ <em>healthy</em> use of these two functions continues to be a responsibility ++ of the application using Expat ++ —, ++ </li> ++ <li> ++ the main character buffer used by functions ++ <code><a href="#XML_GetBuffer">XML_GetBuffer</a></code> ++ and ++ <code><a href="#XML_ParseBuffer">XML_ParseBuffer</a></code> ++ (and thus also by plain ++ <code><a href="#XML_Parse">XML_Parse</a></code>), and ++ </li> ++ <li> ++ the <a href="#XML_SetElementDeclHandler">content model memory</a> ++ (that is passed to the ++ <a href="#XML_SetElementDeclHandler">element declaration handler</a> ++ and freed by a call to ++ <code><a href="#XML_FreeContentModel">XML_FreeContentModel</a></code>). ++ </li> ++ </ul> ++ ++ <p>The amplification factor is calculated as ..</p> ++ <pre>amplification := allocated / direct</pre> ++ <p> ++ .. while parsing, whereas ++ <code>direct</code> is the number of bytes read from the primary document in parsing and ++ <code>allocated</code> is the number of bytes of dynamic memory allocated in the parser hierarchy. ++ </p> ++ ++ <p>For a call to <code>XML_SetAllocTrackerMaximumAmplification</code> to succeed:</p> ++ <ul> ++ <li>parser <code>p</code> must be a non-<code>NULL</code> root parser (without any parent parsers) and</li> ++ <li><code>maximumAmplificationFactor</code> must be non-<code>NaN</code> and greater than or equal to <code>1.0</code>.</li> ++ </ul> ++ ++ <p> ++ <strong>Note:</strong> ++ If you ever need to increase this value for non-attack payload, ++ please <a href="https://github.com/libexpat/libexpat/issues">file a bug report</a>. ++ </p> ++ ++ <p> ++ <strong>Note:</strong> ++ Amplifications factors greater than 100 can been observed near the start of parsing ++ even with benign files in practice. ++ ++ So if you do reduce the maximum allowed amplification, ++ please make sure that the activation threshold is still big enough ++ to not end up with undesired false positives (i.e. benign files being rejected). ++ </p> ++</div> ++ ++<h4 id="XML_SetAllocTrackerActivationThreshold">XML_SetAllocTrackerActivationThreshold</h4> ++<pre class="fcndec"> ++/* Added in Expat 2.7.2. */ ++XML_Bool ++XML_SetAllocTrackerActivationThreshold(XML_Parser p, ++ unsigned long long activationThresholdBytes); ++</pre> ++<div class="fcndef"> ++ <p> ++ Sets number of allocated bytes of dynamic memory ++ needed to activate protection against disproportionate use of RAM ++ (default: <code>64 MiB</code>) ++ of parser <code>p</code> to <code>activationThresholdBytes</code>, and ++ returns <code>XML_TRUE</code> upon success and <code>XML_FALSE</code> upon error. ++ </p> ++ ++ <p> ++ <strong>Note:</strong> ++ For types of allocations that intentionally bypass tracking and limiting, please see ++ <code><a href="#XML_SetAllocTrackerMaximumAmplification">XML_SetAllocTrackerMaximumAmplification</a></code> ++ above. ++ </p> ++ ++ <p>For a call to <code>XML_SetAllocTrackerActivationThreshold</code> to succeed:</p> ++ <ul> ++ <li>parser <code>p</code> must be a non-<code>NULL</code> root parser (without any parent parsers).</li> ++ </ul> ++ ++ <p> ++ <strong>Note:</strong> ++ If you ever need to increase this value for non-attack payload, ++ please <a href="https://github.com/libexpat/libexpat/issues">file a bug report</a>. ++ </p> ++</div> ++ + <h4 id="XML_SetReparseDeferralEnabled">XML_SetReparseDeferralEnabled</h4> + <pre class="fcndec"> + /* Added in Expat 2.6.0. */ diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-17.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-17.patch new file mode 100644 index 00000000000..ca0e3a34f73 --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-17.patch @@ -0,0 +1,28 @@ +From a6a2a49367f03f5d8a73c9027b45b59953ca27d8 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping <sebastian@pipping.org> +Date: Wed, 10 Sep 2025 19:52:39 +0200 +Subject: [PATCH] docs: Promote the contract to call XML_FreeContentModel + +.. when registering a custom element declaration handler +(via a call to function XML_SetElementDeclHandler) + +CVE: CVE-2025-59375 +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/a6a2a49367f03f5d8a73c9027b45b59953ca27d8] +Signed-off-by: Peter Marko <peter.marko@siemens.com> +--- + doc/reference.html | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/doc/reference.html b/doc/reference.html +index 81da4e6c..564fc1b2 100644 +--- a/doc/reference.html ++++ b/doc/reference.html +@@ -1902,7 +1902,7 @@ struct XML_cp { + <p>Sets a handler for element declarations in a DTD. The handler gets + called with the name of the element in the declaration and a pointer + to a structure that contains the element model. It's the user code's +-responsibility to free model when finished with it. See <code> ++responsibility to free model when finished with via a call to <code> + <a href="#XML_FreeContentModel">XML_FreeContentModel</a></code>. + There is no need to free the model from the handler, it can be kept + around and freed at a later stage.</p> diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-18.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-18.patch new file mode 100644 index 00000000000..c29b3018254 --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2025-59375-18.patch @@ -0,0 +1,74 @@ +From a21a3a8299e1ee0b0ae5ae2886a0746d088cf135 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping <sebastian@pipping.org> +Date: Sun, 7 Sep 2025 16:00:35 +0200 +Subject: [PATCH] Changes: Document allocation tracking + +CVE: CVE-2025-59375 +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/a21a3a8299e1ee0b0ae5ae2886a0746d088cf135] +Signed-off-by: Peter Marko <peter.marko@siemens.com> +--- + Changes | 37 +++++++++++++++++++++++++++++++++++++ + 1 file changed, 37 insertions(+) + +diff --git a/Changes b/Changes +index cb752151..ceb5c5dc 100644 +--- a/Changes ++++ b/Changes +@@ -30,6 +30,36 @@ + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + Patches: ++ Security fixes: ++ #1018 #1034 CVE-2025-59375 -- Disallow use of disproportional amounts of ++ dynamic memory from within an Expat parser (e.g. previously ++ a ~250 KiB sized document was able to cause allocation of ++ ~800 MiB from the heap, i.e. an "amplification" of factor ++ ~3,300); once a threshold (that defaults to 64 MiB) is ++ reached, a maximum amplification factor (that defaults to ++ 100.0) is enforced, and violating documents are rejected ++ with an out-of-memory error. ++ There are two new API functions to fine-tune this new ++ behavior: ++ - XML_SetAllocTrackerActivationThreshold ++ - XML_SetAllocTrackerMaximumAmplification . ++ If you ever need to increase these defaults for non-attack ++ XML payload, please file a bug report with libexpat. ++ There is also a new environment variable ++ EXPAT_MALLOC_DEBUG=(0|1|2) to control the verbosity ++ of allocations debugging at runtime, disabled by default. ++ Known impact is (reliable and easy) denial of service: ++ CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H/E:H/RL:O/RC:C ++ (Base Score: 7.5, Temporal Score: 7.2) ++ Please note that a layer of compression around XML can ++ significantly reduce the minimum attack payload size. ++ Distributors intending to backport (or cherry-pick) the ++ fix need to copy 99% of the related pull request, not just ++ the "lib: Implement tracking of dynamic memory allocations" ++ commit, to not end up with a state that literally does both ++ too much and too little at the same time. Appending ".diff" ++ to the pull request URL could be of help. ++ + Bug fixes: + #980 #989 Restore event pointer behavior from Expat 2.6.4 + (that the fix to CVE-2024-8176 changed in 2.7.0); +@@ -39,6 +69,10 @@ Patches: + - XML_GetCurrentColumnNumber + - XML_GetCurrentLineNumber + - XML_GetInputContext ++ #1034 docs: Promote the contract to call function ++ XML_FreeContentModel when registering a custom ++ element declaration handler (via a call to function ++ XML_SetElementDeclHandler) + + Special thanks to: + Berkay Eren Ürün +@@ -71,6 +105,9 @@ Patches: + Linutronix + Red Hat + Siemens ++ and ++ OSS-Fuzz / ClusterFuzz ++ Perl XML::Parser + + Release 2.6.4 Wed November 6 2024 + Security fixes: diff --git a/meta/recipes-core/expat/expat_2.6.4.bb b/meta/recipes-core/expat/expat_2.6.4.bb index 816beaa8a3d..fdfe2b850a7 100644 --- a/meta/recipes-core/expat/expat_2.6.4.bb +++ b/meta/recipes-core/expat/expat_2.6.4.bb @@ -16,6 +16,25 @@ SRC_URI = "${GITHUB_BASE_URI}/download/R_${VERSION_TAG}/expat-${PV}.tar.bz2 \ file://CVE-2024-8176-03.patch \ file://CVE-2024-8176-04.patch \ file://CVE-2024-8176-05.patch \ + file://CVE-2025-59375-00.patch \ + file://CVE-2025-59375-01.patch \ + file://CVE-2025-59375-02.patch \ + file://CVE-2025-59375-03.patch \ + file://CVE-2025-59375-04.patch \ + file://CVE-2025-59375-05.patch \ + file://CVE-2025-59375-06.patch \ + file://CVE-2025-59375-07.patch \ + file://CVE-2025-59375-08.patch \ + file://CVE-2025-59375-09.patch \ + file://CVE-2025-59375-10.patch \ + file://CVE-2025-59375-11.patch \ + file://CVE-2025-59375-12.patch \ + file://CVE-2025-59375-13.patch \ + file://CVE-2025-59375-14.patch \ + file://CVE-2025-59375-15.patch \ + file://CVE-2025-59375-16.patch \ + file://CVE-2025-59375-17.patch \ + file://CVE-2025-59375-18.patch \ " GITHUB_BASE_URI = "https://github.com/libexpat/libexpat/releases/"