summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Marko <peter.marko@siemens.com>2025-10-21 21:19:57 +0200
committerSteve Sakoman <steve@sakoman.com>2025-11-03 07:17:01 -0800
commit2421e79018781d1b85a577cf6f6cbd6294a712ca (patch)
tree3abf7b4b3c90f532413ab44f0749aa6efae1f9f7
parent251d8b676e98fee2b2a9dc0dda2caede9f5acb19 (diff)
downloadpoky-2421e79018781d1b85a577cf6f6cbd6294a712ca.tar.gz
expat: patch CVE-2025-59375
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. 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 Finally picked PR fixing regression [2] together with two minor commits to have a clean cherry-picks. Also here the Changes commit needed conflict resolution. [1] https://github.com/libexpat/libexpat/pull/1034 [2] https://github.com/libexpat/libexpat/pull/1048 (From OE-Core rev: 684d3cdbc08ce41dc1f92e1f228eee34bc2bc1fe) Signed-off-by: Peter Marko <peter.marko@siemens.com> Signed-off-by: Steve Sakoman <steve@sakoman.com>
-rw-r--r--meta/recipes-core/expat/expat/CVE-2025-59375-00.patch52
-rw-r--r--meta/recipes-core/expat/expat/CVE-2025-59375-01.patch48
-rw-r--r--meta/recipes-core/expat/expat/CVE-2025-59375-02.patch109
-rw-r--r--meta/recipes-core/expat/expat/CVE-2025-59375-03.patch127
-rw-r--r--meta/recipes-core/expat/expat/CVE-2025-59375-04.patch62
-rw-r--r--meta/recipes-core/expat/expat/CVE-2025-59375-05.patch64
-rw-r--r--meta/recipes-core/expat/expat/CVE-2025-59375-06.patch68
-rw-r--r--meta/recipes-core/expat/expat/CVE-2025-59375-07.patch52
-rw-r--r--meta/recipes-core/expat/expat/CVE-2025-59375-08.patch577
-rw-r--r--meta/recipes-core/expat/expat/CVE-2025-59375-09.patch43
-rw-r--r--meta/recipes-core/expat/expat/CVE-2025-59375-10.patch54
-rw-r--r--meta/recipes-core/expat/expat/CVE-2025-59375-11.patch66
-rw-r--r--meta/recipes-core/expat/expat/CVE-2025-59375-12.patch58
-rw-r--r--meta/recipes-core/expat/expat/CVE-2025-59375-13.patch309
-rw-r--r--meta/recipes-core/expat/expat/CVE-2025-59375-14.patch122
-rw-r--r--meta/recipes-core/expat/expat/CVE-2025-59375-15.patch70
-rw-r--r--meta/recipes-core/expat/expat/CVE-2025-59375-16.patch146
-rw-r--r--meta/recipes-core/expat/expat/CVE-2025-59375-17.patch28
-rw-r--r--meta/recipes-core/expat/expat/CVE-2025-59375-18.patch74
-rw-r--r--meta/recipes-core/expat/expat/CVE-2025-59375-19.patch103
-rw-r--r--meta/recipes-core/expat/expat/CVE-2025-59375-20.patch285
-rw-r--r--meta/recipes-core/expat/expat/CVE-2025-59375-21.patch196
-rw-r--r--meta/recipes-core/expat/expat/CVE-2025-59375-22.patch37
-rw-r--r--meta/recipes-core/expat/expat/CVE-2025-59375-23.patch47
-rw-r--r--meta/recipes-core/expat/expat/CVE-2025-59375-24.patch36
-rw-r--r--meta/recipes-core/expat/expat_2.6.4.bb25
26 files changed, 2858 insertions, 0 deletions
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 0000000000..e3cbd0f604
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2025-59375-00.patch
@@ -0,0 +1,52 @@
1From 87321ac84a0d6cb42ee64a591adc79c1ec37fb5b Mon Sep 17 00:00:00 2001
2From: Sebastian Pipping <sebastian@pipping.org>
3Date: Tue, 2 Sep 2025 20:52:29 +0200
4Subject: [PATCH] xmlwf: Mention supported environment variables in --help
5 output
6
7CVE: CVE-2025-59375
8Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/87321ac84a0d6cb42ee64a591adc79c1ec37fb5b]
9Signed-off-by: Peter Marko <peter.marko@siemens.com>
10---
11 xmlwf/xmlwf.c | 8 ++++++++
12 xmlwf/xmlwf_helpgen.py | 8 ++++++++
13 2 files changed, 16 insertions(+)
14
15diff --git a/xmlwf/xmlwf.c b/xmlwf/xmlwf.c
16index ec7e51c9..8cfc73ca 100644
17--- a/xmlwf/xmlwf.c
18+++ b/xmlwf/xmlwf.c
19@@ -926,6 +926,14 @@ usage(const XML_Char *prog, int rc) {
20 T(" -h, --help show this [h]elp message and exit\n")
21 T(" -v, --version show program's [v]ersion number and exit\n")
22 T("\n")
23+ T("environment variables:\n")
24+ T(" EXPAT_ACCOUNTING_DEBUG=(0|1|2|3)\n")
25+ T(" Control verbosity of accounting debugging (default: 0)\n")
26+ T(" EXPAT_ENTITY_DEBUG=(0|1)\n")
27+ T(" Control verbosity of entity debugging (default: 0)\n")
28+ T(" EXPAT_ENTROPY_DEBUG=(0|1)\n")
29+ T(" Control verbosity of entropy debugging (default: 0)\n")
30+ T("\n")
31 T("exit status:\n")
32 T(" 0 the input files are well-formed and the output (if requested) was written successfully\n")
33 T(" 1 could not allocate data structures, signals a serious problem with execution environment\n")
34diff --git a/xmlwf/xmlwf_helpgen.py b/xmlwf/xmlwf_helpgen.py
35index c3257f0e..39a3dc13 100755
36--- a/xmlwf/xmlwf_helpgen.py
37+++ b/xmlwf/xmlwf_helpgen.py
38@@ -32,6 +32,14 @@
39 import argparse
40
41 epilog = """
42+environment variables:
43+ EXPAT_ACCOUNTING_DEBUG=(0|1|2|3)
44+ Control verbosity of accounting debugging (default: 0)
45+ EXPAT_ENTITY_DEBUG=(0|1)
46+ Control verbosity of entity debugging (default: 0)
47+ EXPAT_ENTROPY_DEBUG=(0|1)
48+ Control verbosity of entropy debugging (default: 0)
49+
50 exit status:
51 0 the input files are well-formed and the output (if requested) was written successfully
52 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 0000000000..6708bbef45
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2025-59375-01.patch
@@ -0,0 +1,48 @@
1From 0872c189db6e457084fca335662a9cb49e8ec4c7 Mon Sep 17 00:00:00 2001
2From: Sebastian Pipping <sebastian@pipping.org>
3Date: Mon, 1 Sep 2025 18:06:59 +0200
4Subject: [PATCH] lib: Make function dtdCreate use macro MALLOC
5
6.. and give its body access to the parser for upcoming changes
7
8CVE: CVE-2025-59375
9Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/0872c189db6e457084fca335662a9cb49e8ec4c7]
10Signed-off-by: Peter Marko <peter.marko@siemens.com>
11---
12 lib/xmlparse.c | 9 +++++----
13 1 file changed, 5 insertions(+), 4 deletions(-)
14
15diff --git a/lib/xmlparse.c b/lib/xmlparse.c
16index 25f786ec..b9d6eed1 100644
17--- a/lib/xmlparse.c
18+++ b/lib/xmlparse.c
19@@ -555,7 +555,7 @@ static XML_Bool setContext(XML_Parser parser, const XML_Char *context);
20
21 static void FASTCALL normalizePublicId(XML_Char *s);
22
23-static DTD *dtdCreate(const XML_Memory_Handling_Suite *ms);
24+static DTD *dtdCreate(XML_Parser parser);
25 /* do not call if m_parentParser != NULL */
26 static void dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms);
27 static void dtdDestroy(DTD *p, XML_Bool isDocEntity,
28@@ -1166,7 +1166,7 @@ parserCreate(const XML_Char *encodingName,
29 if (dtd)
30 parser->m_dtd = dtd;
31 else {
32- parser->m_dtd = dtdCreate(&parser->m_mem);
33+ parser->m_dtd = dtdCreate(parser);
34 if (parser->m_dtd == NULL) {
35 FREE(parser, parser->m_dataBuf);
36 FREE(parser, parser->m_atts);
37@@ -7126,8 +7126,9 @@ normalizePublicId(XML_Char *publicId) {
38 }
39
40 static DTD *
41-dtdCreate(const XML_Memory_Handling_Suite *ms) {
42- DTD *p = ms->malloc_fcn(sizeof(DTD));
43+dtdCreate(XML_Parser parser) {
44+ const XML_Memory_Handling_Suite *const ms = &parser->m_mem;
45+ DTD *p = MALLOC(parser, sizeof(DTD));
46 if (p == NULL)
47 return p;
48 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 0000000000..b0543370ad
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2025-59375-02.patch
@@ -0,0 +1,109 @@
1From 8768dadae479d9f2e984b747fb2ba79bb78de94f Mon Sep 17 00:00:00 2001
2From: Sebastian Pipping <sebastian@pipping.org>
3Date: Mon, 1 Sep 2025 18:10:26 +0200
4Subject: [PATCH] lib: Make string pools use macros MALLOC, FREE, REALLOC
5
6CVE: CVE-2025-59375
7Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/8768dadae479d9f2e984b747fb2ba79bb78de94f]
8Signed-off-by: Peter Marko <peter.marko@siemens.com>
9---
10 lib/xmlparse.c | 27 +++++++++++++--------------
11 1 file changed, 13 insertions(+), 14 deletions(-)
12
13diff --git a/lib/xmlparse.c b/lib/xmlparse.c
14index b9d6eed1..a56c71ea 100644
15--- a/lib/xmlparse.c
16+++ b/lib/xmlparse.c
17@@ -357,7 +357,7 @@ typedef struct {
18 const XML_Char *end;
19 XML_Char *ptr;
20 XML_Char *start;
21- const XML_Memory_Handling_Suite *mem;
22+ XML_Parser parser;
23 } STRING_POOL;
24
25 /* The XML_Char before the name is used to determine whether
26@@ -574,8 +574,7 @@ static void FASTCALL hashTableIterInit(HASH_TABLE_ITER *iter,
27 const HASH_TABLE *table);
28 static NAMED *FASTCALL hashTableIterNext(HASH_TABLE_ITER *iter);
29
30-static void FASTCALL poolInit(STRING_POOL *pool,
31- const XML_Memory_Handling_Suite *ms);
32+static void FASTCALL poolInit(STRING_POOL *pool, XML_Parser parser);
33 static void FASTCALL poolClear(STRING_POOL *pool);
34 static void FASTCALL poolDestroy(STRING_POOL *pool);
35 static XML_Char *poolAppend(STRING_POOL *pool, const ENCODING *enc,
36@@ -1200,8 +1199,8 @@ parserCreate(const XML_Char *encodingName,
37
38 parser->m_protocolEncodingName = NULL;
39
40- poolInit(&parser->m_tempPool, &(parser->m_mem));
41- poolInit(&parser->m_temp2Pool, &(parser->m_mem));
42+ poolInit(&parser->m_tempPool, parser);
43+ poolInit(&parser->m_temp2Pool, parser);
44 parserInit(parser, encodingName);
45
46 if (encodingName && ! parser->m_protocolEncodingName) {
47@@ -7131,8 +7130,8 @@ dtdCreate(XML_Parser parser) {
48 DTD *p = MALLOC(parser, sizeof(DTD));
49 if (p == NULL)
50 return p;
51- poolInit(&(p->pool), ms);
52- poolInit(&(p->entityValuePool), ms);
53+ poolInit(&(p->pool), parser);
54+ poolInit(&(p->entityValuePool), parser);
55 hashTableInit(&(p->generalEntities), ms);
56 hashTableInit(&(p->elementTypes), ms);
57 hashTableInit(&(p->attributeIds), ms);
58@@ -7596,13 +7595,13 @@ hashTableIterNext(HASH_TABLE_ITER *iter) {
59 }
60
61 static void FASTCALL
62-poolInit(STRING_POOL *pool, const XML_Memory_Handling_Suite *ms) {
63+poolInit(STRING_POOL *pool, XML_Parser parser) {
64 pool->blocks = NULL;
65 pool->freeBlocks = NULL;
66 pool->start = NULL;
67 pool->ptr = NULL;
68 pool->end = NULL;
69- pool->mem = ms;
70+ pool->parser = parser;
71 }
72
73 static void FASTCALL
74@@ -7629,13 +7628,13 @@ poolDestroy(STRING_POOL *pool) {
75 BLOCK *p = pool->blocks;
76 while (p) {
77 BLOCK *tem = p->next;
78- pool->mem->free_fcn(p);
79+ FREE(pool->parser, p);
80 p = tem;
81 }
82 p = pool->freeBlocks;
83 while (p) {
84 BLOCK *tem = p->next;
85- pool->mem->free_fcn(p);
86+ FREE(pool->parser, p);
87 p = tem;
88 }
89 }
90@@ -7790,8 +7789,8 @@ poolGrow(STRING_POOL *pool) {
91 if (bytesToAllocate == 0)
92 return XML_FALSE;
93
94- temp = (BLOCK *)pool->mem->realloc_fcn(pool->blocks,
95- (unsigned)bytesToAllocate);
96+ temp = (BLOCK *)REALLOC(pool->parser, pool->blocks,
97+ (unsigned)bytesToAllocate);
98 if (temp == NULL)
99 return XML_FALSE;
100 pool->blocks = temp;
101@@ -7831,7 +7830,7 @@ poolGrow(STRING_POOL *pool) {
102 if (bytesToAllocate == 0)
103 return XML_FALSE;
104
105- tem = pool->mem->malloc_fcn(bytesToAllocate);
106+ tem = MALLOC(pool->parser, bytesToAllocate);
107 if (! tem)
108 return XML_FALSE;
109 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 0000000000..b8c2c595e1
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2025-59375-03.patch
@@ -0,0 +1,127 @@
1From 4fc6f1ee9f2b282cfe446bf645c992e37f8c3e15 Mon Sep 17 00:00:00 2001
2From: Sebastian Pipping <sebastian@pipping.org>
3Date: Mon, 1 Sep 2025 18:14:09 +0200
4Subject: [PATCH] lib: Make function hash tables use macros MALLOC and FREE
5
6CVE: CVE-2025-59375
7Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/4fc6f1ee9f2b282cfe446bf645c992e37f8c3e15]
8Signed-off-by: Peter Marko <peter.marko@siemens.com>
9---
10 lib/xmlparse.c | 34 ++++++++++++++++------------------
11 1 file changed, 16 insertions(+), 18 deletions(-)
12
13diff --git a/lib/xmlparse.c b/lib/xmlparse.c
14index a56c71ea..a65b0265 100644
15--- a/lib/xmlparse.c
16+++ b/lib/xmlparse.c
17@@ -234,7 +234,7 @@ typedef struct {
18 unsigned char power;
19 size_t size;
20 size_t used;
21- const XML_Memory_Handling_Suite *mem;
22+ XML_Parser parser;
23 } HASH_TABLE;
24
25 static size_t keylen(KEY s);
26@@ -566,8 +566,7 @@ static int copyEntityTable(XML_Parser oldParser, HASH_TABLE *newTable,
27 STRING_POOL *newPool, const HASH_TABLE *oldTable);
28 static NAMED *lookup(XML_Parser parser, HASH_TABLE *table, KEY name,
29 size_t createSize);
30-static void FASTCALL hashTableInit(HASH_TABLE *table,
31- const XML_Memory_Handling_Suite *ms);
32+static void FASTCALL hashTableInit(HASH_TABLE *table, XML_Parser parser);
33 static void FASTCALL hashTableClear(HASH_TABLE *table);
34 static void FASTCALL hashTableDestroy(HASH_TABLE *table);
35 static void FASTCALL hashTableIterInit(HASH_TABLE_ITER *iter,
36@@ -7126,19 +7125,18 @@ normalizePublicId(XML_Char *publicId) {
37
38 static DTD *
39 dtdCreate(XML_Parser parser) {
40- const XML_Memory_Handling_Suite *const ms = &parser->m_mem;
41 DTD *p = MALLOC(parser, sizeof(DTD));
42 if (p == NULL)
43 return p;
44 poolInit(&(p->pool), parser);
45 poolInit(&(p->entityValuePool), parser);
46- hashTableInit(&(p->generalEntities), ms);
47- hashTableInit(&(p->elementTypes), ms);
48- hashTableInit(&(p->attributeIds), ms);
49- hashTableInit(&(p->prefixes), ms);
50+ hashTableInit(&(p->generalEntities), parser);
51+ hashTableInit(&(p->elementTypes), parser);
52+ hashTableInit(&(p->attributeIds), parser);
53+ hashTableInit(&(p->prefixes), parser);
54 #ifdef XML_DTD
55 p->paramEntityRead = XML_FALSE;
56- hashTableInit(&(p->paramEntities), ms);
57+ hashTableInit(&(p->paramEntities), parser);
58 #endif /* XML_DTD */
59 p->defaultPrefix.name = NULL;
60 p->defaultPrefix.binding = NULL;
61@@ -7473,7 +7471,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) {
62 /* table->size is a power of 2 */
63 table->size = (size_t)1 << INIT_POWER;
64 tsize = table->size * sizeof(NAMED *);
65- table->v = table->mem->malloc_fcn(tsize);
66+ table->v = MALLOC(table->parser, tsize);
67 if (! table->v) {
68 table->size = 0;
69 return NULL;
70@@ -7513,7 +7511,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) {
71 }
72
73 size_t tsize = newSize * sizeof(NAMED *);
74- NAMED **newV = table->mem->malloc_fcn(tsize);
75+ NAMED **newV = MALLOC(table->parser, tsize);
76 if (! newV)
77 return NULL;
78 memset(newV, 0, tsize);
79@@ -7529,7 +7527,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) {
80 }
81 newV[j] = table->v[i];
82 }
83- table->mem->free_fcn(table->v);
84+ FREE(table->parser, table->v);
85 table->v = newV;
86 table->power = newPower;
87 table->size = newSize;
88@@ -7542,7 +7540,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) {
89 }
90 }
91 }
92- table->v[i] = table->mem->malloc_fcn(createSize);
93+ table->v[i] = MALLOC(table->parser, createSize);
94 if (! table->v[i])
95 return NULL;
96 memset(table->v[i], 0, createSize);
97@@ -7555,7 +7553,7 @@ static void FASTCALL
98 hashTableClear(HASH_TABLE *table) {
99 size_t i;
100 for (i = 0; i < table->size; i++) {
101- table->mem->free_fcn(table->v[i]);
102+ FREE(table->parser, table->v[i]);
103 table->v[i] = NULL;
104 }
105 table->used = 0;
106@@ -7565,17 +7563,17 @@ static void FASTCALL
107 hashTableDestroy(HASH_TABLE *table) {
108 size_t i;
109 for (i = 0; i < table->size; i++)
110- table->mem->free_fcn(table->v[i]);
111- table->mem->free_fcn(table->v);
112+ FREE(table->parser, table->v[i]);
113+ FREE(table->parser, table->v);
114 }
115
116 static void FASTCALL
117-hashTableInit(HASH_TABLE *p, const XML_Memory_Handling_Suite *ms) {
118+hashTableInit(HASH_TABLE *p, XML_Parser parser) {
119 p->power = 0;
120 p->size = 0;
121 p->used = 0;
122 p->v = NULL;
123- p->mem = ms;
124+ p->parser = parser;
125 }
126
127 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 0000000000..78d9e2fc91
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2025-59375-04.patch
@@ -0,0 +1,62 @@
1From 51487ad9d760faa4809b0f8e189d2f666317e41a Mon Sep 17 00:00:00 2001
2From: Sebastian Pipping <sebastian@pipping.org>
3Date: Mon, 1 Sep 2025 17:45:50 +0200
4Subject: [PATCH] lib: Make function copyString use macro MALLOC
5
6CVE: CVE-2025-59375
7Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/51487ad9d760faa4809b0f8e189d2f666317e41a]
8Signed-off-by: Peter Marko <peter.marko@siemens.com>
9---
10 lib/xmlparse.c | 11 +++++------
11 1 file changed, 5 insertions(+), 6 deletions(-)
12
13diff --git a/lib/xmlparse.c b/lib/xmlparse.c
14index a65b0265..c0576abd 100644
15--- a/lib/xmlparse.c
16+++ b/lib/xmlparse.c
17@@ -593,8 +593,7 @@ static XML_Content *build_model(XML_Parser parser);
18 static ELEMENT_TYPE *getElementType(XML_Parser parser, const ENCODING *enc,
19 const char *ptr, const char *end);
20
21-static XML_Char *copyString(const XML_Char *s,
22- const XML_Memory_Handling_Suite *memsuite);
23+static XML_Char *copyString(const XML_Char *s, XML_Parser parser);
24
25 static unsigned long generate_hash_secret_salt(XML_Parser parser);
26 static XML_Bool startParsing(XML_Parser parser);
27@@ -1231,7 +1230,7 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) {
28 parser->m_processor = prologInitProcessor;
29 XmlPrologStateInit(&parser->m_prologState);
30 if (encodingName != NULL) {
31- parser->m_protocolEncodingName = copyString(encodingName, &(parser->m_mem));
32+ parser->m_protocolEncodingName = copyString(encodingName, parser);
33 }
34 parser->m_curBase = NULL;
35 XmlInitEncoding(&parser->m_initEncoding, &parser->m_encoding, 0);
36@@ -1419,7 +1418,7 @@ XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName) {
37 parser->m_protocolEncodingName = NULL;
38 else {
39 /* Copy the new encoding name into allocated memory */
40- parser->m_protocolEncodingName = copyString(encodingName, &(parser->m_mem));
41+ parser->m_protocolEncodingName = copyString(encodingName, parser);
42 if (! parser->m_protocolEncodingName)
43 return XML_STATUS_ERROR;
44 }
45@@ -8064,7 +8063,7 @@ getElementType(XML_Parser parser, const ENCODING *enc, const char *ptr,
46 }
47
48 static XML_Char *
49-copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) {
50+copyString(const XML_Char *s, XML_Parser parser) {
51 size_t charsRequired = 0;
52 XML_Char *result;
53
54@@ -8076,7 +8075,7 @@ copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) {
55 charsRequired++;
56
57 /* Now allocate space for the copy */
58- result = memsuite->malloc_fcn(charsRequired * sizeof(XML_Char));
59+ result = MALLOC(parser, charsRequired * sizeof(XML_Char));
60 if (result == NULL)
61 return NULL;
62 /* 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 0000000000..37b882fbf4
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2025-59375-05.patch
@@ -0,0 +1,64 @@
1From b3f0bda5f5e979781469532f7c304f7e223568d5 Mon Sep 17 00:00:00 2001
2From: Sebastian Pipping <sebastian@pipping.org>
3Date: Mon, 1 Sep 2025 17:48:02 +0200
4Subject: [PATCH] lib: Make function dtdReset use macro FREE
5
6CVE: CVE-2025-59375
7Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/b3f0bda5f5e979781469532f7c304f7e223568d5]
8Signed-off-by: Peter Marko <peter.marko@siemens.com>
9---
10 lib/xmlparse.c | 12 ++++++------
11 1 file changed, 6 insertions(+), 6 deletions(-)
12
13diff --git a/lib/xmlparse.c b/lib/xmlparse.c
14index c0576abd..65fcce30 100644
15--- a/lib/xmlparse.c
16+++ b/lib/xmlparse.c
17@@ -557,7 +557,7 @@ static void FASTCALL normalizePublicId(XML_Char *s);
18
19 static DTD *dtdCreate(XML_Parser parser);
20 /* do not call if m_parentParser != NULL */
21-static void dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms);
22+static void dtdReset(DTD *p, XML_Parser parser);
23 static void dtdDestroy(DTD *p, XML_Bool isDocEntity,
24 const XML_Memory_Handling_Suite *ms);
25 static int dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd,
26@@ -1382,7 +1382,7 @@ XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) {
27 FREE(parser, (void *)parser->m_protocolEncodingName);
28 parser->m_protocolEncodingName = NULL;
29 parserInit(parser, encodingName);
30- dtdReset(parser->m_dtd, &parser->m_mem);
31+ dtdReset(parser->m_dtd, parser);
32 return XML_TRUE;
33 }
34
35@@ -7155,7 +7155,7 @@ dtdCreate(XML_Parser parser) {
36 }
37
38 static void
39-dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) {
40+dtdReset(DTD *p, XML_Parser parser) {
41 HASH_TABLE_ITER iter;
42 hashTableIterInit(&iter, &(p->elementTypes));
43 for (;;) {
44@@ -7163,7 +7163,7 @@ dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) {
45 if (! e)
46 break;
47 if (e->allocDefaultAtts != 0)
48- ms->free_fcn(e->defaultAtts);
49+ FREE(parser, e->defaultAtts);
50 }
51 hashTableClear(&(p->generalEntities));
52 #ifdef XML_DTD
53@@ -7180,9 +7180,9 @@ dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) {
54
55 p->in_eldecl = XML_FALSE;
56
57- ms->free_fcn(p->scaffIndex);
58+ FREE(parser, p->scaffIndex);
59 p->scaffIndex = NULL;
60- ms->free_fcn(p->scaffold);
61+ FREE(parser, p->scaffold);
62 p->scaffold = NULL;
63
64 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 0000000000..04f975a458
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2025-59375-06.patch
@@ -0,0 +1,68 @@
1From 53a3eda0ae2e0317afd071b72b41976053d82732 Mon Sep 17 00:00:00 2001
2From: Sebastian Pipping <sebastian@pipping.org>
3Date: Mon, 1 Sep 2025 17:50:59 +0200
4Subject: [PATCH] lib: Make function dtdDestroy use macro FREE
5
6CVE: CVE-2025-59375
7Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/53a3eda0ae2e0317afd071b72b41976053d82732]
8Signed-off-by: Peter Marko <peter.marko@siemens.com>
9---
10 lib/xmlparse.c | 16 +++++++---------
11 1 file changed, 7 insertions(+), 9 deletions(-)
12
13diff --git a/lib/xmlparse.c b/lib/xmlparse.c
14index 65fcce30..e7df97da 100644
15--- a/lib/xmlparse.c
16+++ b/lib/xmlparse.c
17@@ -558,8 +558,7 @@ static void FASTCALL normalizePublicId(XML_Char *s);
18 static DTD *dtdCreate(XML_Parser parser);
19 /* do not call if m_parentParser != NULL */
20 static void dtdReset(DTD *p, XML_Parser parser);
21-static void dtdDestroy(DTD *p, XML_Bool isDocEntity,
22- const XML_Memory_Handling_Suite *ms);
23+static void dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser);
24 static int dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd,
25 const XML_Memory_Handling_Suite *ms);
26 static int copyEntityTable(XML_Parser oldParser, HASH_TABLE *newTable,
27@@ -1685,8 +1684,7 @@ XML_ParserFree(XML_Parser parser) {
28 #else
29 if (parser->m_dtd)
30 #endif /* XML_DTD */
31- dtdDestroy(parser->m_dtd, (XML_Bool)! parser->m_parentParser,
32- &parser->m_mem);
33+ dtdDestroy(parser->m_dtd, (XML_Bool)! parser->m_parentParser, parser);
34 FREE(parser, (void *)parser->m_atts);
35 #ifdef XML_ATTR_INFO
36 FREE(parser, (void *)parser->m_attInfo);
37@@ -7196,7 +7194,7 @@ dtdReset(DTD *p, XML_Parser parser) {
38 }
39
40 static void
41-dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) {
42+dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser) {
43 HASH_TABLE_ITER iter;
44 hashTableIterInit(&iter, &(p->elementTypes));
45 for (;;) {
46@@ -7204,7 +7202,7 @@ dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) {
47 if (! e)
48 break;
49 if (e->allocDefaultAtts != 0)
50- ms->free_fcn(e->defaultAtts);
51+ FREE(parser, e->defaultAtts);
52 }
53 hashTableDestroy(&(p->generalEntities));
54 #ifdef XML_DTD
55@@ -7216,10 +7214,10 @@ dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) {
56 poolDestroy(&(p->pool));
57 poolDestroy(&(p->entityValuePool));
58 if (isDocEntity) {
59- ms->free_fcn(p->scaffIndex);
60- ms->free_fcn(p->scaffold);
61+ FREE(parser, p->scaffIndex);
62+ FREE(parser, p->scaffold);
63 }
64- ms->free_fcn(p);
65+ FREE(parser, p);
66 }
67
68 /* 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 0000000000..7eff0009d2
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2025-59375-07.patch
@@ -0,0 +1,52 @@
1From 4e7a5d03daf672f20c73d40dc8970385c18b30d3 Mon Sep 17 00:00:00 2001
2From: Sebastian Pipping <sebastian@pipping.org>
3Date: Mon, 1 Sep 2025 17:52:58 +0200
4Subject: [PATCH] lib: Make function dtdCopy use macro MALLOC
5
6CVE: CVE-2025-59375
7Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/4e7a5d03daf672f20c73d40dc8970385c18b30d3]
8Signed-off-by: Peter Marko <peter.marko@siemens.com>
9---
10 lib/xmlparse.c | 8 ++++----
11 1 file changed, 4 insertions(+), 4 deletions(-)
12
13diff --git a/lib/xmlparse.c b/lib/xmlparse.c
14index e7df97da..9f0a8b3e 100644
15--- a/lib/xmlparse.c
16+++ b/lib/xmlparse.c
17@@ -560,7 +560,7 @@ static DTD *dtdCreate(XML_Parser parser);
18 static void dtdReset(DTD *p, XML_Parser parser);
19 static void dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser);
20 static int dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd,
21- const XML_Memory_Handling_Suite *ms);
22+ XML_Parser parser);
23 static int copyEntityTable(XML_Parser oldParser, HASH_TABLE *newTable,
24 STRING_POOL *newPool, const HASH_TABLE *oldTable);
25 static NAMED *lookup(XML_Parser parser, HASH_TABLE *table, KEY name,
26@@ -1572,7 +1572,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context,
27 parser->m_prologState.inEntityValue = oldInEntityValue;
28 if (context) {
29 #endif /* XML_DTD */
30- if (! dtdCopy(oldParser, parser->m_dtd, oldDtd, &parser->m_mem)
31+ if (! dtdCopy(oldParser, parser->m_dtd, oldDtd, parser)
32 || ! setContext(parser, context)) {
33 XML_ParserFree(parser);
34 return NULL;
35@@ -7225,7 +7225,7 @@ dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser) {
36 */
37 static int
38 dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd,
39- const XML_Memory_Handling_Suite *ms) {
40+ XML_Parser parser) {
41 HASH_TABLE_ITER iter;
42
43 /* Copy the prefix table. */
44@@ -7306,7 +7306,7 @@ dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd,
45 }
46 #endif
47 newE->defaultAtts
48- = ms->malloc_fcn(oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE));
49+ = MALLOC(parser, oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE));
50 if (! newE->defaultAtts) {
51 return 0;
52 }
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 0000000000..deda31bebc
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2025-59375-08.patch
@@ -0,0 +1,577 @@
1From cfce28e171676fe6f70d17b97ed8a59eaeb83f15 Mon Sep 17 00:00:00 2001
2From: Sebastian Pipping <sebastian@pipping.org>
3Date: Mon, 1 Sep 2025 17:34:58 +0200
4Subject: [PATCH] lib: Implement tracking of dynamic memory allocations
5
6**PLEASE NOTE** that distributors intending to backport (or cherry-pick)
7this fix need to copy 99% of the related pull request, not just this
8commit, to not end up with a state that literally does both too much and
9too little at the same time. Appending ".diff" to the pull request URL
10could be of help.
11
12CVE: CVE-2025-59375
13Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/cfce28e171676fe6f70d17b97ed8a59eaeb83f15]
14Signed-off-by: Peter Marko <peter.marko@siemens.com>
15---
16 lib/expat.h | 15 +-
17 lib/internal.h | 5 +
18 lib/libexpat.def.cmake | 3 +
19 lib/xmlparse.c | 337 +++++++++++++++++++++++++++++++++++++++--
20 tests/basic_tests.c | 4 +
21 tests/nsalloc_tests.c | 5 +
22 xmlwf/xmlwf.c | 2 +
23 xmlwf/xmlwf_helpgen.py | 2 +
24 8 files changed, 361 insertions(+), 12 deletions(-)
25
26diff --git a/lib/expat.h b/lib/expat.h
27index 610e1ddc..66a253c1 100644
28--- a/lib/expat.h
29+++ b/lib/expat.h
30@@ -1032,7 +1032,10 @@ enum XML_FeatureEnum {
31 XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT,
32 XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT,
33 /* Added in Expat 2.6.0. */
34- XML_FEATURE_GE
35+ XML_FEATURE_GE,
36+ /* Added in Expat 2.7.2. */
37+ XML_FEATURE_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT,
38+ XML_FEATURE_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT,
39 /* Additional features must be added to the end of this enum. */
40 };
41
42@@ -1057,6 +1060,16 @@ XML_SetBillionLaughsAttackProtectionMaximumAmplification(
43 XMLPARSEAPI(XML_Bool)
44 XML_SetBillionLaughsAttackProtectionActivationThreshold(
45 XML_Parser parser, unsigned long long activationThresholdBytes);
46+
47+/* Added in Expat 2.7.2. */
48+XMLPARSEAPI(XML_Bool)
49+XML_SetAllocTrackerMaximumAmplification(XML_Parser parser,
50+ float maximumAmplificationFactor);
51+
52+/* Added in Expat 2.7.2. */
53+XMLPARSEAPI(XML_Bool)
54+XML_SetAllocTrackerActivationThreshold(
55+ XML_Parser parser, unsigned long long activationThresholdBytes);
56 #endif
57
58 /* Added in Expat 2.6.0. */
59diff --git a/lib/internal.h b/lib/internal.h
60index 6bde6ae6..eb67cf50 100644
61--- a/lib/internal.h
62+++ b/lib/internal.h
63@@ -145,6 +145,11 @@
64 100.0f
65 #define EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT \
66 8388608 // 8 MiB, 2^23
67+
68+#define EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT 100.0f
69+#define EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT \
70+ 67108864 // 64 MiB, 2^26
71+
72 /* NOTE END */
73
74 #include "expat.h" // so we can use type XML_Parser below
75diff --git a/lib/libexpat.def.cmake b/lib/libexpat.def.cmake
76index 10ee9cd6..7a3a7ec0 100644
77--- a/lib/libexpat.def.cmake
78+++ b/lib/libexpat.def.cmake
79@@ -79,3 +79,6 @@ EXPORTS
80 @_EXPAT_COMMENT_DTD_OR_GE@ XML_SetBillionLaughsAttackProtectionMaximumAmplification @70
81 ; added with version 2.6.0
82 XML_SetReparseDeferralEnabled @71
83+; added with version 2.7.2
84+@_EXPAT_COMMENT_DTD_OR_GE@ XML_SetAllocTrackerMaximumAmplification @72
85+@_EXPAT_COMMENT_DTD_OR_GE@ XML_SetAllocTrackerActivationThreshold @73
86diff --git a/lib/xmlparse.c b/lib/xmlparse.c
87index 9f0a8b3e..fcf1cfdd 100644
88--- a/lib/xmlparse.c
89+++ b/lib/xmlparse.c
90@@ -452,6 +452,14 @@ typedef struct accounting {
91 unsigned long long activationThresholdBytes;
92 } ACCOUNTING;
93
94+typedef struct MALLOC_TRACKER {
95+ XmlBigCount bytesAllocated;
96+ XmlBigCount peakBytesAllocated; // updated live only for debug level >=2
97+ unsigned long debugLevel;
98+ float maximumAmplificationFactor; // >=1.0
99+ XmlBigCount activationThresholdBytes;
100+} MALLOC_TRACKER;
101+
102 typedef struct entity_stats {
103 unsigned int countEverOpened;
104 unsigned int currentDepth;
105@@ -599,7 +607,8 @@ static XML_Bool startParsing(XML_Parser parser);
106
107 static XML_Parser parserCreate(const XML_Char *encodingName,
108 const XML_Memory_Handling_Suite *memsuite,
109- const XML_Char *nameSep, DTD *dtd);
110+ const XML_Char *nameSep, DTD *dtd,
111+ XML_Parser parentParser);
112
113 static void parserInit(XML_Parser parser, const XML_Char *encodingName);
114
115@@ -769,14 +778,220 @@ struct XML_ParserStruct {
116 unsigned long m_hash_secret_salt;
117 #if XML_GE == 1
118 ACCOUNTING m_accounting;
119+ MALLOC_TRACKER m_alloc_tracker;
120 ENTITY_STATS m_entity_stats;
121 #endif
122 XML_Bool m_reenter;
123 };
124
125-#define MALLOC(parser, s) (parser->m_mem.malloc_fcn((s)))
126-#define REALLOC(parser, p, s) (parser->m_mem.realloc_fcn((p), (s)))
127-#define FREE(parser, p) (parser->m_mem.free_fcn((p)))
128+#if XML_GE == 1
129+# define MALLOC(parser, s) (expat_malloc((parser), (s), __LINE__))
130+# define REALLOC(parser, p, s) (expat_realloc((parser), (p), (s), __LINE__))
131+# define FREE(parser, p) (expat_free((parser), (p), __LINE__))
132+#else
133+# define MALLOC(parser, s) (parser->m_mem.malloc_fcn((s)))
134+# define REALLOC(parser, p, s) (parser->m_mem.realloc_fcn((p), (s)))
135+# define FREE(parser, p) (parser->m_mem.free_fcn((p)))
136+#endif
137+
138+#if XML_GE == 1
139+static void
140+expat_heap_stat(XML_Parser rootParser, char operator, XmlBigCount absDiff,
141+ XmlBigCount newTotal, XmlBigCount peakTotal, int sourceLine) {
142+ // NOTE: This can be +infinity or -nan
143+ const float amplification
144+ = (float)newTotal / (float)rootParser->m_accounting.countBytesDirect;
145+ fprintf(
146+ stderr,
147+ "expat: Allocations(%p): Direct " EXPAT_FMT_ULL("10") ", allocated %c" EXPAT_FMT_ULL(
148+ "10") " to " EXPAT_FMT_ULL("10") " (" EXPAT_FMT_ULL("10") " peak), amplification %8.2f (xmlparse.c:%d)\n",
149+ (void *)rootParser, rootParser->m_accounting.countBytesDirect, operator,
150+ absDiff, newTotal, peakTotal, (double)amplification, sourceLine);
151+}
152+
153+static bool
154+expat_heap_increase_tolerable(XML_Parser rootParser, XmlBigCount increase,
155+ int sourceLine) {
156+ assert(rootParser != NULL);
157+ assert(increase > 0);
158+
159+ XmlBigCount newTotal = 0;
160+ bool tolerable = true;
161+
162+ // Detect integer overflow
163+ if ((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated < increase) {
164+ tolerable = false;
165+ } else {
166+ newTotal = rootParser->m_alloc_tracker.bytesAllocated + increase;
167+
168+ if (newTotal >= rootParser->m_alloc_tracker.activationThresholdBytes) {
169+ assert(newTotal > 0);
170+ // NOTE: This can be +infinity when dividing by zero but not -nan
171+ const float amplification
172+ = (float)newTotal / (float)rootParser->m_accounting.countBytesDirect;
173+ if (amplification
174+ > rootParser->m_alloc_tracker.maximumAmplificationFactor) {
175+ tolerable = false;
176+ }
177+ }
178+ }
179+
180+ if (! tolerable && (rootParser->m_alloc_tracker.debugLevel >= 1)) {
181+ expat_heap_stat(rootParser, '+', increase, newTotal, newTotal, sourceLine);
182+ }
183+
184+ return tolerable;
185+}
186+
187+static void *
188+expat_malloc(XML_Parser parser, size_t size, int sourceLine) {
189+ // Detect integer overflow
190+ if (SIZE_MAX - size < sizeof(size_t)) {
191+ return NULL;
192+ }
193+
194+ const XML_Parser rootParser = getRootParserOf(parser, NULL);
195+ assert(rootParser->m_parentParser == NULL);
196+
197+ const size_t bytesToAllocate = sizeof(size_t) + size;
198+
199+ if ((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated
200+ < bytesToAllocate) {
201+ return NULL; // i.e. signal integer overflow as out-of-memory
202+ }
203+
204+ if (! expat_heap_increase_tolerable(rootParser, bytesToAllocate,
205+ sourceLine)) {
206+ return NULL; // i.e. signal violation as out-of-memory
207+ }
208+
209+ // Actually allocate
210+ void *const mallocedPtr = parser->m_mem.malloc_fcn(bytesToAllocate);
211+
212+ if (mallocedPtr == NULL) {
213+ return NULL;
214+ }
215+
216+ // Update in-block recorded size
217+ *(size_t *)mallocedPtr = size;
218+
219+ // Update accounting
220+ rootParser->m_alloc_tracker.bytesAllocated += bytesToAllocate;
221+
222+ // Report as needed
223+ if (rootParser->m_alloc_tracker.debugLevel >= 2) {
224+ if (rootParser->m_alloc_tracker.bytesAllocated
225+ > rootParser->m_alloc_tracker.peakBytesAllocated) {
226+ rootParser->m_alloc_tracker.peakBytesAllocated
227+ = rootParser->m_alloc_tracker.bytesAllocated;
228+ }
229+ expat_heap_stat(rootParser, '+', bytesToAllocate,
230+ rootParser->m_alloc_tracker.bytesAllocated,
231+ rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine);
232+ }
233+
234+ return (char *)mallocedPtr + sizeof(size_t);
235+}
236+
237+static void
238+expat_free(XML_Parser parser, void *ptr, int sourceLine) {
239+ assert(parser != NULL);
240+
241+ if (ptr == NULL) {
242+ return;
243+ }
244+
245+ const XML_Parser rootParser = getRootParserOf(parser, NULL);
246+ assert(rootParser->m_parentParser == NULL);
247+
248+ // Extract size (to the eyes of malloc_fcn/realloc_fcn) and
249+ // the original pointer returned by malloc/realloc
250+ void *const mallocedPtr = (char *)ptr - sizeof(size_t);
251+ const size_t bytesAllocated = sizeof(size_t) + *(size_t *)mallocedPtr;
252+
253+ // Update accounting
254+ assert(rootParser->m_alloc_tracker.bytesAllocated >= bytesAllocated);
255+ rootParser->m_alloc_tracker.bytesAllocated -= bytesAllocated;
256+
257+ // Report as needed
258+ if (rootParser->m_alloc_tracker.debugLevel >= 2) {
259+ expat_heap_stat(rootParser, '-', bytesAllocated,
260+ rootParser->m_alloc_tracker.bytesAllocated,
261+ rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine);
262+ }
263+
264+ // NOTE: This may be freeing rootParser, so freeing has to come last
265+ parser->m_mem.free_fcn(mallocedPtr);
266+}
267+
268+static void *
269+expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine) {
270+ assert(parser != NULL);
271+
272+ if (ptr == NULL) {
273+ return expat_malloc(parser, size, sourceLine);
274+ }
275+
276+ if (size == 0) {
277+ expat_free(parser, ptr, sourceLine);
278+ return NULL;
279+ }
280+
281+ const XML_Parser rootParser = getRootParserOf(parser, NULL);
282+ assert(rootParser->m_parentParser == NULL);
283+
284+ // Extract original size (to the eyes of the caller) and the original
285+ // pointer returned by malloc/realloc
286+ void *mallocedPtr = (char *)ptr - sizeof(size_t);
287+ const size_t prevSize = *(size_t *)mallocedPtr;
288+
289+ // Classify upcoming change
290+ const bool isIncrease = (size > prevSize);
291+ const size_t absDiff
292+ = (size > prevSize) ? (size - prevSize) : (prevSize - size);
293+
294+ // Ask for permission from accounting
295+ if (isIncrease) {
296+ if (! expat_heap_increase_tolerable(rootParser, absDiff, sourceLine)) {
297+ return NULL; // i.e. signal violation as out-of-memory
298+ }
299+ }
300+
301+ // Actually allocate
302+ mallocedPtr = parser->m_mem.realloc_fcn(mallocedPtr, sizeof(size_t) + size);
303+
304+ if (mallocedPtr == NULL) {
305+ return NULL;
306+ }
307+
308+ // Update accounting
309+ if (isIncrease) {
310+ assert((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated
311+ >= absDiff);
312+ rootParser->m_alloc_tracker.bytesAllocated += absDiff;
313+ } else { // i.e. decrease
314+ assert(rootParser->m_alloc_tracker.bytesAllocated >= absDiff);
315+ rootParser->m_alloc_tracker.bytesAllocated -= absDiff;
316+ }
317+
318+ // Report as needed
319+ if (rootParser->m_alloc_tracker.debugLevel >= 2) {
320+ if (rootParser->m_alloc_tracker.bytesAllocated
321+ > rootParser->m_alloc_tracker.peakBytesAllocated) {
322+ rootParser->m_alloc_tracker.peakBytesAllocated
323+ = rootParser->m_alloc_tracker.bytesAllocated;
324+ }
325+ expat_heap_stat(rootParser, isIncrease ? '+' : '-', absDiff,
326+ rootParser->m_alloc_tracker.bytesAllocated,
327+ rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine);
328+ }
329+
330+ // Update in-block recorded size
331+ *(size_t *)mallocedPtr = size;
332+
333+ return (char *)mallocedPtr + sizeof(size_t);
334+}
335+#endif // XML_GE == 1
336
337 XML_Parser XMLCALL
338 XML_ParserCreate(const XML_Char *encodingName) {
339@@ -1096,19 +1311,40 @@ XML_Parser XMLCALL
340 XML_ParserCreate_MM(const XML_Char *encodingName,
341 const XML_Memory_Handling_Suite *memsuite,
342 const XML_Char *nameSep) {
343- return parserCreate(encodingName, memsuite, nameSep, NULL);
344+ return parserCreate(encodingName, memsuite, nameSep, NULL, NULL);
345 }
346
347 static XML_Parser
348 parserCreate(const XML_Char *encodingName,
349 const XML_Memory_Handling_Suite *memsuite, const XML_Char *nameSep,
350- DTD *dtd) {
351- XML_Parser parser;
352+ DTD *dtd, XML_Parser parentParser) {
353+ XML_Parser parser = NULL;
354+
355+#if XML_GE == 1
356+ const size_t increase = sizeof(size_t) + sizeof(struct XML_ParserStruct);
357+
358+ if (parentParser != NULL) {
359+ const XML_Parser rootParser = getRootParserOf(parentParser, NULL);
360+ if (! expat_heap_increase_tolerable(rootParser, increase, __LINE__)) {
361+ return NULL;
362+ }
363+ }
364+#else
365+ UNUSED_P(parentParser);
366+#endif
367
368 if (memsuite) {
369 XML_Memory_Handling_Suite *mtemp;
370+#if XML_GE == 1
371+ void *const sizeAndParser = memsuite->malloc_fcn(
372+ sizeof(size_t) + sizeof(struct XML_ParserStruct));
373+ if (sizeAndParser != NULL) {
374+ *(size_t *)sizeAndParser = sizeof(struct XML_ParserStruct);
375+ parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t));
376+#else
377 parser = memsuite->malloc_fcn(sizeof(struct XML_ParserStruct));
378 if (parser != NULL) {
379+#endif
380 mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem);
381 mtemp->malloc_fcn = memsuite->malloc_fcn;
382 mtemp->realloc_fcn = memsuite->realloc_fcn;
383@@ -1116,18 +1352,67 @@ parserCreate(const XML_Char *encodingName,
384 }
385 } else {
386 XML_Memory_Handling_Suite *mtemp;
387+#if XML_GE == 1
388+ void *const sizeAndParser
389+ = (XML_Parser)malloc(sizeof(size_t) + sizeof(struct XML_ParserStruct));
390+ if (sizeAndParser != NULL) {
391+ *(size_t *)sizeAndParser = sizeof(struct XML_ParserStruct);
392+ parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t));
393+#else
394 parser = (XML_Parser)malloc(sizeof(struct XML_ParserStruct));
395 if (parser != NULL) {
396+#endif
397 mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem);
398 mtemp->malloc_fcn = malloc;
399 mtemp->realloc_fcn = realloc;
400 mtemp->free_fcn = free;
401 }
402- }
403+ } // cppcheck-suppress[memleak symbolName=sizeAndParser] // Cppcheck >=2.18.0
404
405 if (! parser)
406 return parser;
407
408+#if XML_GE == 1
409+ // Initialize .m_alloc_tracker
410+ memset(&parser->m_alloc_tracker, 0, sizeof(MALLOC_TRACKER));
411+ if (parentParser == NULL) {
412+ parser->m_alloc_tracker.debugLevel
413+ = getDebugLevel("EXPAT_MALLOC_DEBUG", 0u);
414+ parser->m_alloc_tracker.maximumAmplificationFactor
415+ = EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT;
416+ parser->m_alloc_tracker.activationThresholdBytes
417+ = EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT;
418+
419+ // NOTE: This initialization needs to come this early because these fields
420+ // are read by allocation tracking code
421+ parser->m_parentParser = NULL;
422+ parser->m_accounting.countBytesDirect = 0;
423+ } else {
424+ parser->m_parentParser = parentParser;
425+ }
426+
427+ // Record XML_ParserStruct allocation we did a few lines up before
428+ const XML_Parser rootParser = getRootParserOf(parser, NULL);
429+ assert(rootParser->m_parentParser == NULL);
430+ assert(SIZE_MAX - rootParser->m_alloc_tracker.bytesAllocated >= increase);
431+ rootParser->m_alloc_tracker.bytesAllocated += increase;
432+
433+ // Report on allocation
434+ if (rootParser->m_alloc_tracker.debugLevel >= 2) {
435+ if (rootParser->m_alloc_tracker.bytesAllocated
436+ > rootParser->m_alloc_tracker.peakBytesAllocated) {
437+ rootParser->m_alloc_tracker.peakBytesAllocated
438+ = rootParser->m_alloc_tracker.bytesAllocated;
439+ }
440+
441+ expat_heap_stat(rootParser, '+', increase,
442+ rootParser->m_alloc_tracker.bytesAllocated,
443+ rootParser->m_alloc_tracker.peakBytesAllocated, __LINE__);
444+ }
445+#else
446+ parser->m_parentParser = NULL;
447+#endif // XML_GE == 1
448+
449 parser->m_buffer = NULL;
450 parser->m_bufferLim = NULL;
451
452@@ -1291,7 +1576,6 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) {
453 parser->m_unknownEncodingMem = NULL;
454 parser->m_unknownEncodingRelease = NULL;
455 parser->m_unknownEncodingData = NULL;
456- parser->m_parentParser = NULL;
457 parser->m_parsingStatus.parsing = XML_INITIALIZED;
458 // Reentry can only be triggered inside m_processor calls
459 parser->m_reenter = XML_FALSE;
460@@ -1526,9 +1810,10 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context,
461 */
462 if (parser->m_ns) {
463 XML_Char tmp[2] = {parser->m_namespaceSeparator, 0};
464- parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd);
465+ parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd, oldParser);
466 } else {
467- parser = parserCreate(encodingName, &parser->m_mem, NULL, newDtd);
468+ parser
469+ = parserCreate(encodingName, &parser->m_mem, NULL, newDtd, oldParser);
470 }
471
472 if (! parser)
473@@ -2708,6 +2993,13 @@ XML_GetFeatureList(void) {
474 EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT},
475 /* Added in Expat 2.6.0. */
476 {XML_FEATURE_GE, XML_L("XML_GE"), 0},
477+ /* Added in Expat 2.7.2. */
478+ {XML_FEATURE_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT,
479+ XML_L("XML_AT_MAX_AMP"),
480+ (long int)EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT},
481+ {XML_FEATURE_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT,
482+ XML_L("XML_AT_ACT_THRES"),
483+ (long int)EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT},
484 #endif
485 {XML_FEATURE_END, NULL, 0}};
486
487@@ -2736,6 +3028,29 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold(
488 parser->m_accounting.activationThresholdBytes = activationThresholdBytes;
489 return XML_TRUE;
490 }
491+
492+XML_Bool XMLCALL
493+XML_SetAllocTrackerMaximumAmplification(XML_Parser parser,
494+ float maximumAmplificationFactor) {
495+ if ((parser == NULL) || (parser->m_parentParser != NULL)
496+ || isnan(maximumAmplificationFactor)
497+ || (maximumAmplificationFactor < 1.0f)) {
498+ return XML_FALSE;
499+ }
500+ parser->m_alloc_tracker.maximumAmplificationFactor
501+ = maximumAmplificationFactor;
502+ return XML_TRUE;
503+}
504+
505+XML_Bool XMLCALL
506+XML_SetAllocTrackerActivationThreshold(
507+ XML_Parser parser, unsigned long long activationThresholdBytes) {
508+ if ((parser == NULL) || (parser->m_parentParser != NULL)) {
509+ return XML_FALSE;
510+ }
511+ parser->m_alloc_tracker.activationThresholdBytes = activationThresholdBytes;
512+ return XML_TRUE;
513+}
514 #endif /* XML_GE == 1 */
515
516 XML_Bool XMLCALL
517diff --git a/tests/basic_tests.c b/tests/basic_tests.c
518index 129db1d8..0231e094 100644
519--- a/tests/basic_tests.c
520+++ b/tests/basic_tests.c
521@@ -3089,6 +3089,10 @@ START_TEST(test_buffer_can_grow_to_max) {
522 for (int i = 0; i < num_prefixes; ++i) {
523 set_subtest("\"%s\"", prefixes[i]);
524 XML_Parser parser = XML_ParserCreate(NULL);
525+#if XML_GE == 1
526+ assert_true(XML_SetAllocTrackerActivationThreshold(parser, (size_t)-1)
527+ == XML_TRUE); // i.e. deactivate
528+#endif
529 const int prefix_len = (int)strlen(prefixes[i]);
530 const enum XML_Status s
531 = _XML_Parse_SINGLE_BYTES(parser, prefixes[i], prefix_len, XML_FALSE);
532diff --git a/tests/nsalloc_tests.c b/tests/nsalloc_tests.c
533index 48520f42..0a594e14 100644
534--- a/tests/nsalloc_tests.c
535+++ b/tests/nsalloc_tests.c
536@@ -454,10 +454,15 @@ START_TEST(test_nsalloc_realloc_attributes) {
537 nsalloc_teardown();
538 nsalloc_setup();
539 }
540+#if XML_GE == 1
541+ assert_true(
542+ i == 0); // because expat_realloc relies on expat_malloc to some extent
543+#else
544 if (i == 0)
545 fail("Parsing worked despite failing reallocations");
546 else if (i == max_realloc_count)
547 fail("Parsing failed at max reallocation count");
548+#endif
549 }
550 END_TEST
551
552diff --git a/xmlwf/xmlwf.c b/xmlwf/xmlwf.c
553index 8cfc73ca..b9d0a7fc 100644
554--- a/xmlwf/xmlwf.c
555+++ b/xmlwf/xmlwf.c
556@@ -933,6 +933,8 @@ usage(const XML_Char *prog, int rc) {
557 T(" Control verbosity of entity debugging (default: 0)\n")
558 T(" EXPAT_ENTROPY_DEBUG=(0|1)\n")
559 T(" Control verbosity of entropy debugging (default: 0)\n")
560+ T(" EXPAT_MALLOC_DEBUG=(0|1|2)\n")
561+ T(" Control verbosity of allocation tracker (default: 0)\n")
562 T("\n")
563 T("exit status:\n")
564 T(" 0 the input files are well-formed and the output (if requested) was written successfully\n")
565diff --git a/xmlwf/xmlwf_helpgen.py b/xmlwf/xmlwf_helpgen.py
566index 39a3dc13..2360820d 100755
567--- a/xmlwf/xmlwf_helpgen.py
568+++ b/xmlwf/xmlwf_helpgen.py
569@@ -39,6 +39,8 @@ environment variables:
570 Control verbosity of entity debugging (default: 0)
571 EXPAT_ENTROPY_DEBUG=(0|1)
572 Control verbosity of entropy debugging (default: 0)
573+ EXPAT_MALLOC_DEBUG=(0|1|2)
574+ Control verbosity of allocation tracker (default: 0)
575
576 exit status:
577 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 0000000000..364c28183a
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2025-59375-09.patch
@@ -0,0 +1,43 @@
1From 1270e5bc0836d296ac4970fc9e1cf53d83972083 Mon Sep 17 00:00:00 2001
2From: Sebastian Pipping <sebastian@pipping.org>
3Date: Sun, 7 Sep 2025 12:18:08 +0200
4Subject: [PATCH] lib: Make XML_MemFree and XML_FreeContentModel match their
5 siblings
6
7.. XML_MemMalloc and XML_MemRealloc in structure, prior to upcoming changes
8
9CVE: CVE-2025-59375
10Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/1270e5bc0836d296ac4970fc9e1cf53d83972083]
11Signed-off-by: Peter Marko <peter.marko@siemens.com>
12---
13 lib/xmlparse.c | 10 ++++++----
14 1 file changed, 6 insertions(+), 4 deletions(-)
15
16diff --git a/lib/xmlparse.c b/lib/xmlparse.c
17index fcf1cfdd..5d27cd45 100644
18--- a/lib/xmlparse.c
19+++ b/lib/xmlparse.c
20@@ -2772,8 +2772,9 @@ XML_GetCurrentColumnNumber(XML_Parser parser) {
21
22 void XMLCALL
23 XML_FreeContentModel(XML_Parser parser, XML_Content *model) {
24- if (parser != NULL)
25- FREE(parser, model);
26+ if (parser == NULL)
27+ return;
28+ FREE(parser, model);
29 }
30
31 void *XMLCALL
32@@ -2792,8 +2793,9 @@ XML_MemRealloc(XML_Parser parser, void *ptr, size_t size) {
33
34 void XMLCALL
35 XML_MemFree(XML_Parser parser, void *ptr) {
36- if (parser != NULL)
37- FREE(parser, ptr);
38+ if (parser == NULL)
39+ return;
40+ FREE(parser, ptr);
41 }
42
43 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 0000000000..fe5452000e
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2025-59375-10.patch
@@ -0,0 +1,54 @@
1From 96c7467281c72028aada525c1d3822512758b266 Mon Sep 17 00:00:00 2001
2From: Sebastian Pipping <sebastian@pipping.org>
3Date: Sun, 7 Sep 2025 12:06:43 +0200
4Subject: [PATCH] lib: Exclude XML_Mem* functions from allocation tracking
5
6.. so that allocations by the user application
7are not being limited.
8
9CVE: CVE-2025-59375
10Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/96c7467281c72028aada525c1d3822512758b266]
11Signed-off-by: Peter Marko <peter.marko@siemens.com>
12---
13 lib/xmlparse.c | 16 +++++++++++++---
14 1 file changed, 13 insertions(+), 3 deletions(-)
15
16diff --git a/lib/xmlparse.c b/lib/xmlparse.c
17index 5d27cd45..8145a049 100644
18--- a/lib/xmlparse.c
19+++ b/lib/xmlparse.c
20@@ -2781,21 +2781,31 @@ void *XMLCALL
21 XML_MemMalloc(XML_Parser parser, size_t size) {
22 if (parser == NULL)
23 return NULL;
24- return MALLOC(parser, size);
25+
26+ // NOTE: We are avoiding MALLOC(..) here to not include
27+ // user allocations with allocation tracking and limiting.
28+ return parser->m_mem.malloc_fcn(size);
29 }
30
31 void *XMLCALL
32 XML_MemRealloc(XML_Parser parser, void *ptr, size_t size) {
33 if (parser == NULL)
34 return NULL;
35- return REALLOC(parser, ptr, size);
36+
37+ // NOTE: We are avoiding REALLOC(..) here to not include
38+ // user allocations with allocation tracking and limiting.
39+ return parser->m_mem.realloc_fcn(ptr, size);
40 }
41
42 void XMLCALL
43 XML_MemFree(XML_Parser parser, void *ptr) {
44 if (parser == NULL)
45 return;
46- FREE(parser, ptr);
47+
48+ // NOTE: We are avoiding FREE(..) here because XML_MemMalloc and
49+ // XML_MemRealloc are not using MALLOC(..) and REALLOC(..)
50+ // but plain .malloc_fcn(..) and .realloc_fcn(..), internally.
51+ parser->m_mem.free_fcn(ptr);
52 }
53
54 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 0000000000..be892a7804
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2025-59375-11.patch
@@ -0,0 +1,66 @@
1From ae4086198d710a62a0a1560007b81307dba72909 Mon Sep 17 00:00:00 2001
2From: Sebastian Pipping <sebastian@pipping.org>
3Date: Tue, 9 Sep 2025 21:34:28 +0200
4Subject: [PATCH] lib: Exclude the main input buffer from allocation tracking
5
6.. so that control of the input buffer size remains with the
7application using Expat
8
9CVE: CVE-2025-59375
10Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/ae4086198d710a62a0a1560007b81307dba72909]
11Signed-off-by: Peter Marko <peter.marko@siemens.com>
12---
13 lib/xmlparse.c | 19 +++++++++++++++----
14 1 file changed, 15 insertions(+), 4 deletions(-)
15
16diff --git a/lib/xmlparse.c b/lib/xmlparse.c
17index 8145a049..00139b94 100644
18--- a/lib/xmlparse.c
19+++ b/lib/xmlparse.c
20@@ -1975,7 +1975,10 @@ XML_ParserFree(XML_Parser parser) {
21 FREE(parser, (void *)parser->m_attInfo);
22 #endif
23 FREE(parser, parser->m_groupConnector);
24- FREE(parser, parser->m_buffer);
25+ // NOTE: We are avoiding FREE(..) here because parser->m_buffer
26+ // is not being allocated with MALLOC(..) but with plain
27+ // .malloc_fcn(..).
28+ parser->m_mem.free_fcn(parser->m_buffer);
29 FREE(parser, parser->m_dataBuf);
30 FREE(parser, parser->m_nsAtts);
31 FREE(parser, parser->m_unknownEncodingMem);
32@@ -2567,7 +2570,9 @@ XML_GetBuffer(XML_Parser parser, int len) {
33 parser->m_errorCode = XML_ERROR_NO_MEMORY;
34 return NULL;
35 }
36- newBuf = (char *)MALLOC(parser, bufferSize);
37+ // NOTE: We are avoiding MALLOC(..) here to leave limiting
38+ // the input size to the application using Expat.
39+ newBuf = (char *)parser->m_mem.malloc_fcn(bufferSize);
40 if (newBuf == 0) {
41 parser->m_errorCode = XML_ERROR_NO_MEMORY;
42 return NULL;
43@@ -2578,7 +2583,10 @@ XML_GetBuffer(XML_Parser parser, int len) {
44 memcpy(newBuf, &parser->m_bufferPtr[-keep],
45 EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr)
46 + keep);
47- FREE(parser, parser->m_buffer);
48+ // NOTE: We are avoiding FREE(..) here because parser->m_buffer
49+ // is not being allocated with MALLOC(..) but with plain
50+ // .malloc_fcn(..).
51+ parser->m_mem.free_fcn(parser->m_buffer);
52 parser->m_buffer = newBuf;
53 parser->m_bufferEnd
54 = parser->m_buffer
55@@ -2594,7 +2602,10 @@ XML_GetBuffer(XML_Parser parser, int len) {
56 if (parser->m_bufferPtr) {
57 memcpy(newBuf, parser->m_bufferPtr,
58 EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr));
59- FREE(parser, parser->m_buffer);
60+ // NOTE: We are avoiding FREE(..) here because parser->m_buffer
61+ // is not being allocated with MALLOC(..) but with plain
62+ // .malloc_fcn(..).
63+ parser->m_mem.free_fcn(parser->m_buffer);
64 parser->m_bufferEnd
65 = newBuf
66 + 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 0000000000..9e036a5284
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2025-59375-12.patch
@@ -0,0 +1,58 @@
1From 7e35240dc97e9fd4f609e31f27c27b659535e436 Mon Sep 17 00:00:00 2001
2From: Sebastian Pipping <sebastian@pipping.org>
3Date: Thu, 11 Sep 2025 00:27:05 +0200
4Subject: [PATCH] lib: Exclude the content model from allocation tracking
5
6.. so that applications that are not using XML_FreeContentModel
7but plain free(..) or .free_fcn() to free the content model's
8memory are safe
9
10CVE: CVE-2025-59375
11Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/7e35240dc97e9fd4f609e31f27c27b659535e436]
12Signed-off-by: Peter Marko <peter.marko@siemens.com>
13---
14 lib/xmlparse.c | 16 +++++++++++++---
15 1 file changed, 13 insertions(+), 3 deletions(-)
16
17diff --git a/lib/xmlparse.c b/lib/xmlparse.c
18index 00139b94..d0b6e0cd 100644
19--- a/lib/xmlparse.c
20+++ b/lib/xmlparse.c
21@@ -2785,7 +2785,10 @@ void XMLCALL
22 XML_FreeContentModel(XML_Parser parser, XML_Content *model) {
23 if (parser == NULL)
24 return;
25- FREE(parser, model);
26+
27+ // NOTE: We are avoiding FREE(..) here because the content model
28+ // has been created using plain .malloc_fcn(..) rather than MALLOC(..).
29+ parser->m_mem.free_fcn(model);
30 }
31
32 void *XMLCALL
33@@ -6063,8 +6066,12 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
34 case XML_ROLE_CONTENT_EMPTY:
35 if (dtd->in_eldecl) {
36 if (parser->m_elementDeclHandler) {
37+ // NOTE: We are avoiding MALLOC(..) here to so that
38+ // applications that are not using XML_FreeContentModel but
39+ // plain free(..) or .free_fcn() to free the content model's
40+ // memory are safe.
41 XML_Content *content
42- = (XML_Content *)MALLOC(parser, sizeof(XML_Content));
43+ = (XML_Content *)parser->m_mem.malloc_fcn(sizeof(XML_Content));
44 if (! content)
45 return XML_ERROR_NO_MEMORY;
46 content->quant = XML_CQUANT_NONE;
47@@ -8278,7 +8285,10 @@ build_model(XML_Parser parser) {
48 const size_t allocsize = (dtd->scaffCount * sizeof(XML_Content)
49 + (dtd->contentStringLen * sizeof(XML_Char)));
50
51- ret = (XML_Content *)MALLOC(parser, allocsize);
52+ // NOTE: We are avoiding MALLOC(..) here to so that
53+ // applications that are not using XML_FreeContentModel but plain
54+ // free(..) or .free_fcn() to free the content model's memory are safe.
55+ ret = (XML_Content *)parser->m_mem.malloc_fcn(allocsize);
56 if (! ret)
57 return NULL;
58
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 0000000000..209dd83a4b
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2025-59375-13.patch
@@ -0,0 +1,309 @@
1From 31f9053c3c46741f4daf2ea2bdea75f40f720d42 Mon Sep 17 00:00:00 2001
2From: Sebastian Pipping <sebastian@pipping.org>
3Date: Tue, 2 Sep 2025 22:36:49 +0200
4Subject: [PATCH] tests: Cover allocation tracking and limiting with tests
5
6CVE: CVE-2025-59375
7Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/31f9053c3c46741f4daf2ea2bdea75f40f720d42]
8Signed-off-by: Peter Marko <peter.marko@siemens.com>
9---
10 lib/internal.h | 3 +
11 lib/xmlparse.c | 12 +++
12 tests/alloc_tests.c | 214 ++++++++++++++++++++++++++++++++++++++++++++
13 3 files changed, 229 insertions(+)
14
15diff --git a/lib/internal.h b/lib/internal.h
16index eb67cf50..6e087858 100644
17--- a/lib/internal.h
18+++ b/lib/internal.h
19@@ -173,6 +173,9 @@ extern
20 #endif
21 XML_Bool g_reparseDeferralEnabledDefault; // written ONLY in runtests.c
22 #if defined(XML_TESTING)
23+void *expat_malloc(XML_Parser parser, size_t size, int sourceLine);
24+void expat_free(XML_Parser parser, void *ptr, int sourceLine);
25+void *expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine);
26 extern unsigned int g_bytesScanned; // used for testing only
27 #endif
28
29diff --git a/lib/xmlparse.c b/lib/xmlparse.c
30index d0b6e0cd..6e9c6fb2 100644
31--- a/lib/xmlparse.c
32+++ b/lib/xmlparse.c
33@@ -843,7 +843,11 @@ expat_heap_increase_tolerable(XML_Parser rootParser, XmlBigCount increase,
34 return tolerable;
35 }
36
37+# if defined(XML_TESTING)
38+void *
39+# else
40 static void *
41+# endif
42 expat_malloc(XML_Parser parser, size_t size, int sourceLine) {
43 // Detect integer overflow
44 if (SIZE_MAX - size < sizeof(size_t)) {
45@@ -893,7 +897,11 @@ expat_malloc(XML_Parser parser, size_t size, int sourceLine) {
46 return (char *)mallocedPtr + sizeof(size_t);
47 }
48
49+# if defined(XML_TESTING)
50+void
51+# else
52 static void
53+# endif
54 expat_free(XML_Parser parser, void *ptr, int sourceLine) {
55 assert(parser != NULL);
56
57@@ -924,7 +932,11 @@ expat_free(XML_Parser parser, void *ptr, int sourceLine) {
58 parser->m_mem.free_fcn(mallocedPtr);
59 }
60
61+# if defined(XML_TESTING)
62+void *
63+# else
64 static void *
65+# endif
66 expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine) {
67 assert(parser != NULL);
68
69diff --git a/tests/alloc_tests.c b/tests/alloc_tests.c
70index 4c3e2af4..275f92d5 100644
71--- a/tests/alloc_tests.c
72+++ b/tests/alloc_tests.c
73@@ -46,10 +46,16 @@
74 # undef NDEBUG /* because test suite relies on assert(...) at the moment */
75 #endif
76
77+#include <math.h> /* NAN, INFINITY */
78+#include <stdbool.h>
79+#include <stdint.h> /* for SIZE_MAX */
80 #include <string.h>
81 #include <assert.h>
82
83+#include "expat_config.h"
84+
85 #include "expat.h"
86+#include "internal.h"
87 #include "common.h"
88 #include "minicheck.h"
89 #include "dummy.h"
90@@ -2085,6 +2091,203 @@ START_TEST(test_alloc_reset_after_external_entity_parser_create_fail) {
91 }
92 END_TEST
93
94+START_TEST(test_alloc_tracker_size_recorded) {
95+ XML_Memory_Handling_Suite memsuite = {malloc, realloc, free};
96+
97+ bool values[] = {true, false};
98+ for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++) {
99+ const bool useMemSuite = values[i];
100+ set_subtest("useMemSuite=%d", (int)useMemSuite);
101+ XML_Parser parser = useMemSuite
102+ ? XML_ParserCreate_MM(NULL, &memsuite, XCS("|"))
103+ : XML_ParserCreate(NULL);
104+
105+#if XML_GE == 1
106+ void *ptr = expat_malloc(parser, 10, -1);
107+
108+ assert_true(ptr != NULL);
109+ assert_true(*((size_t *)ptr - 1) == 10);
110+
111+ assert_true(expat_realloc(parser, ptr, SIZE_MAX / 2, -1) == NULL);
112+
113+ assert_true(*((size_t *)ptr - 1) == 10); // i.e. unchanged
114+
115+ ptr = expat_realloc(parser, ptr, 20, -1);
116+
117+ assert_true(ptr != NULL);
118+ assert_true(*((size_t *)ptr - 1) == 20);
119+
120+ expat_free(parser, ptr, -1);
121+#endif
122+
123+ XML_ParserFree(parser);
124+ }
125+}
126+END_TEST
127+
128+START_TEST(test_alloc_tracker_maximum_amplification) {
129+ if (g_reparseDeferralEnabledDefault == XML_TRUE) {
130+ return;
131+ }
132+
133+ XML_Parser parser = XML_ParserCreate(NULL);
134+
135+ // Get .m_accounting.countBytesDirect from 0 to 3
136+ const char *const chunk = "<e>";
137+ assert_true(_XML_Parse_SINGLE_BYTES(parser, chunk, (int)strlen(chunk),
138+ /*isFinal=*/XML_FALSE)
139+ == XML_STATUS_OK);
140+
141+#if XML_GE == 1
142+ // Stop activation threshold from interfering
143+ assert_true(XML_SetAllocTrackerActivationThreshold(parser, 0) == XML_TRUE);
144+
145+ // Exceed maximum amplification: should be rejected.
146+ assert_true(expat_malloc(parser, 1000, -1) == NULL);
147+
148+ // Increase maximum amplification, and try the same amount once more: should
149+ // work.
150+ assert_true(XML_SetAllocTrackerMaximumAmplification(parser, 3000.0f)
151+ == XML_TRUE);
152+
153+ void *const ptr = expat_malloc(parser, 1000, -1);
154+ assert_true(ptr != NULL);
155+ expat_free(parser, ptr, -1);
156+#endif
157+
158+ XML_ParserFree(parser);
159+}
160+END_TEST
161+
162+START_TEST(test_alloc_tracker_threshold) {
163+ XML_Parser parser = XML_ParserCreate(NULL);
164+
165+#if XML_GE == 1
166+ // Exceed maximum amplification *before* (default) threshold: should work.
167+ void *const ptr = expat_malloc(parser, 1000, -1);
168+ assert_true(ptr != NULL);
169+ expat_free(parser, ptr, -1);
170+
171+ // Exceed maximum amplification *after* threshold: should be rejected.
172+ assert_true(XML_SetAllocTrackerActivationThreshold(parser, 999) == XML_TRUE);
173+ assert_true(expat_malloc(parser, 1000, -1) == NULL);
174+#endif
175+
176+ XML_ParserFree(parser);
177+}
178+END_TEST
179+
180+START_TEST(test_alloc_tracker_getbuffer_unlimited) {
181+ XML_Parser parser = XML_ParserCreate(NULL);
182+
183+#if XML_GE == 1
184+ // Artificially lower threshold
185+ assert_true(XML_SetAllocTrackerActivationThreshold(parser, 0) == XML_TRUE);
186+
187+ // Self-test: Prove that threshold is as rejecting as expected
188+ assert_true(expat_malloc(parser, 1000, -1) == NULL);
189+#endif
190+ // XML_GetBuffer should be allowed to pass, though
191+ assert_true(XML_GetBuffer(parser, 1000) != NULL);
192+
193+ XML_ParserFree(parser);
194+}
195+END_TEST
196+
197+START_TEST(test_alloc_tracker_api) {
198+ XML_Parser parserWithoutParent = XML_ParserCreate(NULL);
199+ XML_Parser parserWithParent = XML_ExternalEntityParserCreate(
200+ parserWithoutParent, XCS("entity123"), NULL);
201+ if (parserWithoutParent == NULL)
202+ fail("parserWithoutParent is NULL");
203+ if (parserWithParent == NULL)
204+ fail("parserWithParent is NULL");
205+
206+#if XML_GE == 1
207+ // XML_SetAllocTrackerMaximumAmplification, error cases
208+ if (XML_SetAllocTrackerMaximumAmplification(NULL, 123.0f) == XML_TRUE)
209+ fail("Call with NULL parser is NOT supposed to succeed");
210+ if (XML_SetAllocTrackerMaximumAmplification(parserWithParent, 123.0f)
211+ == XML_TRUE)
212+ fail("Call with non-root parser is NOT supposed to succeed");
213+ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, NAN)
214+ == XML_TRUE)
215+ fail("Call with NaN limit is NOT supposed to succeed");
216+ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, -1.0f)
217+ == XML_TRUE)
218+ fail("Call with negative limit is NOT supposed to succeed");
219+ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, 0.9f)
220+ == XML_TRUE)
221+ fail("Call with positive limit <1.0 is NOT supposed to succeed");
222+
223+ // XML_SetAllocTrackerMaximumAmplification, success cases
224+ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, 1.0f)
225+ == XML_FALSE)
226+ fail("Call with positive limit >=1.0 is supposed to succeed");
227+ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, 123456.789f)
228+ == XML_FALSE)
229+ fail("Call with positive limit >=1.0 is supposed to succeed");
230+ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, INFINITY)
231+ == XML_FALSE)
232+ fail("Call with positive limit >=1.0 is supposed to succeed");
233+
234+ // XML_SetAllocTrackerActivationThreshold, error cases
235+ if (XML_SetAllocTrackerActivationThreshold(NULL, 123) == XML_TRUE)
236+ fail("Call with NULL parser is NOT supposed to succeed");
237+ if (XML_SetAllocTrackerActivationThreshold(parserWithParent, 123) == XML_TRUE)
238+ fail("Call with non-root parser is NOT supposed to succeed");
239+
240+ // XML_SetAllocTrackerActivationThreshold, success cases
241+ if (XML_SetAllocTrackerActivationThreshold(parserWithoutParent, 123)
242+ == XML_FALSE)
243+ fail("Call with non-NULL parentless parser is supposed to succeed");
244+#endif // XML_GE == 1
245+
246+ XML_ParserFree(parserWithParent);
247+ XML_ParserFree(parserWithoutParent);
248+}
249+END_TEST
250+
251+START_TEST(test_mem_api_cycle) {
252+ XML_Parser parser = XML_ParserCreate(NULL);
253+
254+ void *ptr = XML_MemMalloc(parser, 10);
255+
256+ assert_true(ptr != NULL);
257+ memset(ptr, 'x', 10); // assert writability, with ASan in mind
258+
259+ ptr = XML_MemRealloc(parser, ptr, 20);
260+
261+ assert_true(ptr != NULL);
262+ memset(ptr, 'y', 20); // assert writability, with ASan in mind
263+
264+ XML_MemFree(parser, ptr);
265+
266+ XML_ParserFree(parser);
267+}
268+END_TEST
269+
270+START_TEST(test_mem_api_unlimited) {
271+ XML_Parser parser = XML_ParserCreate(NULL);
272+
273+#if XML_GE == 1
274+ assert_true(XML_SetAllocTrackerActivationThreshold(parser, 0) == XML_TRUE);
275+#endif
276+
277+ void *ptr = XML_MemMalloc(parser, 1000);
278+
279+ assert_true(ptr != NULL);
280+
281+ ptr = XML_MemRealloc(parser, ptr, 2000);
282+
283+ assert_true(ptr != NULL);
284+
285+ XML_MemFree(parser, ptr);
286+
287+ XML_ParserFree(parser);
288+}
289+END_TEST
290+
291 void
292 make_alloc_test_case(Suite *s) {
293 TCase *tc_alloc = tcase_create("allocation tests");
294@@ -2151,4 +2354,15 @@ make_alloc_test_case(Suite *s) {
295
296 tcase_add_test__ifdef_xml_dtd(
297 tc_alloc, test_alloc_reset_after_external_entity_parser_create_fail);
298+
299+ tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_tracker_size_recorded);
300+ tcase_add_test__ifdef_xml_dtd(tc_alloc,
301+ test_alloc_tracker_maximum_amplification);
302+ tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_tracker_threshold);
303+ tcase_add_test__ifdef_xml_dtd(tc_alloc,
304+ test_alloc_tracker_getbuffer_unlimited);
305+ tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_tracker_api);
306+
307+ tcase_add_test(tc_alloc, test_mem_api_cycle);
308+ tcase_add_test__ifdef_xml_dtd(tc_alloc, test_mem_api_unlimited);
309 }
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 0000000000..a339cc3f4b
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2025-59375-14.patch
@@ -0,0 +1,122 @@
1From 78366891a586f293aeff60a14a55e4afe1169586 Mon Sep 17 00:00:00 2001
2From: Sebastian Pipping <sebastian@pipping.org>
3Date: Tue, 2 Sep 2025 16:44:00 +0200
4Subject: [PATCH] xmlwf: Wire allocation tracker config to existing arguments
5 -a and -b
6
7CVE: CVE-2025-59375
8Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/78366891a586f293aeff60a14a55e4afe1169586]
9Signed-off-by: Peter Marko <peter.marko@siemens.com>
10---
11 doc/xmlwf.xml | 26 ++++++++++++++++++++------
12 xmlwf/xmlwf.c | 7 +++++--
13 xmlwf/xmlwf_helpgen.py | 4 ++--
14 3 files changed, 27 insertions(+), 10 deletions(-)
15
16diff --git a/doc/xmlwf.xml b/doc/xmlwf.xml
17index 17e9cf51..65d8ae9b 100644
18--- a/doc/xmlwf.xml
19+++ b/doc/xmlwf.xml
20@@ -158,19 +158,31 @@ supports both.
21 <listitem>
22 <para>
23 Sets the maximum tolerated amplification factor
24- for protection against billion laughs attacks (default: 100.0).
25+ for protection against amplification attacks
26+ like the billion laughs attack
27+ (default: 100.0
28+ for the sum of direct and indirect output and also
29+ for allocations of dynamic memory).
30 The amplification factor is calculated as ..
31 </para>
32 <literallayout>
33 amplification := (direct + indirect) / direct
34 </literallayout>
35 <para>
36- .. while parsing, whereas
37+ .. with regard to use of entities and ..
38+ </para>
39+ <literallayout>
40+ amplification := allocated / direct
41+ </literallayout>
42+ <para>
43+ .. with regard to dynamic memory while parsing.
44 &lt;direct&gt; is the number of bytes read
45- from the primary document in parsing and
46+ from the primary document in parsing,
47 &lt;indirect&gt; is the number of bytes
48 added by expanding entities and reading of external DTD files,
49- combined.
50+ combined, and
51+ &lt;allocated&gt; is the total number of bytes of dynamic memory
52+ allocated (and not freed) per hierarchy of parsers.
53 </para>
54 <para>
55 <emphasis>NOTE</emphasis>:
56@@ -185,8 +197,10 @@ supports both.
57 <listitem>
58 <para>
59 Sets the number of output bytes (including amplification)
60- needed to activate protection against billion laughs attacks
61- (default: 8 MiB).
62+ needed to activate protection against amplification attacks
63+ like billion laughs
64+ (default: 8 MiB for the sum of direct and indirect output,
65+ and 64 MiB for allocations of dynamic memory).
66 This can be thought of as an &quot;activation threshold&quot;.
67 </para>
68 <para>
69diff --git a/xmlwf/xmlwf.c b/xmlwf/xmlwf.c
70index b9d0a7fc..14206d9e 100644
71--- a/xmlwf/xmlwf.c
72+++ b/xmlwf/xmlwf.c
73@@ -913,11 +913,11 @@ usage(const XML_Char *prog, int rc) {
74 T(" -t write no XML output for [t]iming of plain parsing\n")
75 T(" -N enable adding doctype and [n]otation declarations\n")
76 T("\n")
77- T("billion laughs attack protection:\n")
78+ T("amplification attack protection (e.g. billion laughs):\n")
79 T(" NOTE: If you ever need to increase these values for non-attack payload, please file a bug report.\n")
80 T("\n")
81 T(" -a FACTOR set maximum tolerated [a]mplification factor (default: 100.0)\n")
82- T(" -b BYTES set number of output [b]ytes needed to activate (default: 8 MiB)\n")
83+ T(" -b BYTES set number of output [b]ytes needed to activate (default: 8 MiB/64 MiB)\n")
84 T("\n")
85 T("reparse deferral:\n")
86 T(" -q disable reparse deferral, and allow [q]uadratic parse runtime with large tokens\n")
87@@ -1181,12 +1181,15 @@ tmain(int argc, XML_Char **argv) {
88 #if XML_GE == 1
89 XML_SetBillionLaughsAttackProtectionMaximumAmplification(
90 parser, attackMaximumAmplification);
91+ XML_SetAllocTrackerMaximumAmplification(parser,
92+ attackMaximumAmplification);
93 #endif
94 }
95 if (attackThresholdGiven) {
96 #if XML_GE == 1
97 XML_SetBillionLaughsAttackProtectionActivationThreshold(
98 parser, attackThresholdBytes);
99+ XML_SetAllocTrackerActivationThreshold(parser, attackThresholdBytes);
100 #else
101 (void)attackThresholdBytes; // silence -Wunused-but-set-variable
102 #endif
103diff --git a/xmlwf/xmlwf_helpgen.py b/xmlwf/xmlwf_helpgen.py
104index 2360820d..e91c285c 100755
105--- a/xmlwf/xmlwf_helpgen.py
106+++ b/xmlwf/xmlwf_helpgen.py
107@@ -84,13 +84,13 @@ output_mode.add_argument('-m', action='store_true', help='write [m]eta XML, not
108 output_mode.add_argument('-t', action='store_true', help='write no XML output for [t]iming of plain parsing')
109 output_related.add_argument('-N', action='store_true', help='enable adding doctype and [n]otation declarations')
110
111-billion_laughs = parser.add_argument_group('billion laughs attack protection',
112+billion_laughs = parser.add_argument_group('amplification attack protection (e.g. billion laughs)',
113 description='NOTE: '
114 'If you ever need to increase these values '
115 'for non-attack payload, please file a bug report.')
116 billion_laughs.add_argument('-a', metavar='FACTOR',
117 help='set maximum tolerated [a]mplification factor (default: 100.0)')
118-billion_laughs.add_argument('-b', metavar='BYTES', help='set number of output [b]ytes needed to activate (default: 8 MiB)')
119+billion_laughs.add_argument('-b', metavar='BYTES', help='set number of output [b]ytes needed to activate (default: 8 MiB/64 MiB)')
120
121 reparse_deferral = parser.add_argument_group('reparse deferral')
122 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 0000000000..8d06844192
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2025-59375-15.patch
@@ -0,0 +1,70 @@
1From 5ae51be57ed0ca1e87582881d07ea9c29c4f7c05 Mon Sep 17 00:00:00 2001
2From: Sebastian Pipping <sebastian@pipping.org>
3Date: Wed, 3 Sep 2025 17:06:41 +0200
4Subject: [PATCH] fuzz: Be robust towards NULL return from
5 XML_ExternalEntityParserCreate
6
7CVE: CVE-2025-59375
8Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/5ae51be57ed0ca1e87582881d07ea9c29c4f7c05]
9Signed-off-by: Peter Marko <peter.marko@siemens.com>
10---
11 fuzz/xml_parse_fuzzer.c | 14 ++++++++------
12 fuzz/xml_parsebuffer_fuzzer.c | 14 ++++++++------
13 2 files changed, 16 insertions(+), 12 deletions(-)
14
15diff --git a/fuzz/xml_parse_fuzzer.c b/fuzz/xml_parse_fuzzer.c
16index 90c38549..29ab33ff 100644
17--- a/fuzz/xml_parse_fuzzer.c
18+++ b/fuzz/xml_parse_fuzzer.c
19@@ -89,15 +89,17 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
20
21 XML_Parser externalEntityParser
22 = XML_ExternalEntityParserCreate(parentParser, "e1", NULL);
23- assert(externalEntityParser);
24- ParseOneInput(externalEntityParser, data, size);
25- XML_ParserFree(externalEntityParser);
26+ if (externalEntityParser != NULL) {
27+ ParseOneInput(externalEntityParser, data, size);
28+ XML_ParserFree(externalEntityParser);
29+ }
30
31 XML_Parser externalDtdParser
32 = XML_ExternalEntityParserCreate(parentParser, NULL, NULL);
33- assert(externalDtdParser);
34- ParseOneInput(externalDtdParser, data, size);
35- XML_ParserFree(externalDtdParser);
36+ if (externalDtdParser != NULL) {
37+ ParseOneInput(externalDtdParser, data, size);
38+ XML_ParserFree(externalDtdParser);
39+ }
40
41 // finally frees this parser which served as parent
42 XML_ParserFree(parentParser);
43diff --git a/fuzz/xml_parsebuffer_fuzzer.c b/fuzz/xml_parsebuffer_fuzzer.c
44index 0db67dce..38b9981b 100644
45--- a/fuzz/xml_parsebuffer_fuzzer.c
46+++ b/fuzz/xml_parsebuffer_fuzzer.c
47@@ -101,15 +101,17 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
48
49 XML_Parser externalEntityParser
50 = XML_ExternalEntityParserCreate(parentParser, "e1", NULL);
51- assert(externalEntityParser);
52- ParseOneInput(externalEntityParser, data, size);
53- XML_ParserFree(externalEntityParser);
54+ if (externalEntityParser != NULL) {
55+ ParseOneInput(externalEntityParser, data, size);
56+ XML_ParserFree(externalEntityParser);
57+ }
58
59 XML_Parser externalDtdParser
60 = XML_ExternalEntityParserCreate(parentParser, NULL, NULL);
61- assert(externalDtdParser);
62- ParseOneInput(externalDtdParser, data, size);
63- XML_ParserFree(externalDtdParser);
64+ if (externalDtdParser != NULL) {
65+ ParseOneInput(externalDtdParser, data, size);
66+ XML_ParserFree(externalDtdParser);
67+ }
68
69 // finally frees this parser which served as parent
70 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 0000000000..a276347d83
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2025-59375-16.patch
@@ -0,0 +1,146 @@
1From d6246c31a1238d065b4d9690d3bac740326f6485 Mon Sep 17 00:00:00 2001
2From: Sebastian Pipping <sebastian@pipping.org>
3Date: Wed, 3 Sep 2025 01:28:03 +0200
4Subject: [PATCH] docs: Document the two allocation tracking API functions
5
6CVE: CVE-2025-59375
7Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/d6246c31a1238d065b4d9690d3bac740326f6485]
8Signed-off-by: Peter Marko <peter.marko@siemens.com>
9---
10 doc/reference.html | 116 +++++++++++++++++++++++++++++++++++++++++++++
11 1 file changed, 116 insertions(+)
12
13diff --git a/doc/reference.html b/doc/reference.html
14index 89476710..81da4e6c 100644
15--- a/doc/reference.html
16+++ b/doc/reference.html
17@@ -157,6 +157,8 @@ interface.</p>
18 <ul>
19 <li><a href="#XML_SetBillionLaughsAttackProtectionMaximumAmplification">XML_SetBillionLaughsAttackProtectionMaximumAmplification</a></li>
20 <li><a href="#XML_SetBillionLaughsAttackProtectionActivationThreshold">XML_SetBillionLaughsAttackProtectionActivationThreshold</a></li>
21+ <li><a href="#XML_SetAllocTrackerMaximumAmplification">XML_SetAllocTrackerMaximumAmplification</a></li>
22+ <li><a href="#XML_SetAllocTrackerActivationThreshold">XML_SetAllocTrackerActivationThreshold</a></li>
23 <li><a href="#XML_SetReparseDeferralEnabled">XML_SetReparseDeferralEnabled</a></li>
24 </ul>
25 </li>
26@@ -2262,6 +2264,120 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold(XML_Parser p,
27 </p>
28 </div>
29
30+<h4 id="XML_SetAllocTrackerMaximumAmplification">XML_SetAllocTrackerMaximumAmplification</h4>
31+<pre class="fcndec">
32+/* Added in Expat 2.7.2. */
33+XML_Bool
34+XML_SetAllocTrackerMaximumAmplification(XML_Parser p,
35+ float maximumAmplificationFactor);
36+</pre>
37+<div class="fcndef">
38+ <p>
39+ Sets the maximum tolerated amplification factor
40+ between direct input and bytes of dynamic memory allocated
41+ (default: <code>100.0</code>)
42+ of parser <code>p</code> to <code>maximumAmplificationFactor</code>, and
43+ returns <code>XML_TRUE</code> upon success and <code>XML_FALSE</code> upon error.
44+ </p>
45+
46+ <p>
47+ <strong>Note:</strong>
48+ There are three types of allocations that intentionally bypass tracking and limiting:
49+ </p>
50+ <ul>
51+ <li>
52+ application calls to functions
53+ <code><a href="#XML_MemMalloc">XML_MemMalloc</a></code>
54+ and
55+ <code><a href="#XML_MemRealloc">XML_MemRealloc</a></code>
56+ &mdash;
57+ <em>healthy</em> use of these two functions continues to be a responsibility
58+ of the application using Expat
59+ &mdash;,
60+ </li>
61+ <li>
62+ the main character buffer used by functions
63+ <code><a href="#XML_GetBuffer">XML_GetBuffer</a></code>
64+ and
65+ <code><a href="#XML_ParseBuffer">XML_ParseBuffer</a></code>
66+ (and thus also by plain
67+ <code><a href="#XML_Parse">XML_Parse</a></code>), and
68+ </li>
69+ <li>
70+ the <a href="#XML_SetElementDeclHandler">content model memory</a>
71+ (that is passed to the
72+ <a href="#XML_SetElementDeclHandler">element declaration handler</a>
73+ and freed by a call to
74+ <code><a href="#XML_FreeContentModel">XML_FreeContentModel</a></code>).
75+ </li>
76+ </ul>
77+
78+ <p>The amplification factor is calculated as ..</p>
79+ <pre>amplification := allocated / direct</pre>
80+ <p>
81+ .. while parsing, whereas
82+ <code>direct</code> is the number of bytes read from the primary document in parsing and
83+ <code>allocated</code> is the number of bytes of dynamic memory allocated in the parser hierarchy.
84+ </p>
85+
86+ <p>For a call to <code>XML_SetAllocTrackerMaximumAmplification</code> to succeed:</p>
87+ <ul>
88+ <li>parser <code>p</code> must be a non-<code>NULL</code> root parser (without any parent parsers) and</li>
89+ <li><code>maximumAmplificationFactor</code> must be non-<code>NaN</code> and greater than or equal to <code>1.0</code>.</li>
90+ </ul>
91+
92+ <p>
93+ <strong>Note:</strong>
94+ If you ever need to increase this value for non-attack payload,
95+ please <a href="https://github.com/libexpat/libexpat/issues">file a bug report</a>.
96+ </p>
97+
98+ <p>
99+ <strong>Note:</strong>
100+ Amplifications factors greater than 100 can been observed near the start of parsing
101+ even with benign files in practice.
102+
103+ So if you do reduce the maximum allowed amplification,
104+ please make sure that the activation threshold is still big enough
105+ to not end up with undesired false positives (i.e. benign files being rejected).
106+ </p>
107+</div>
108+
109+<h4 id="XML_SetAllocTrackerActivationThreshold">XML_SetAllocTrackerActivationThreshold</h4>
110+<pre class="fcndec">
111+/* Added in Expat 2.7.2. */
112+XML_Bool
113+XML_SetAllocTrackerActivationThreshold(XML_Parser p,
114+ unsigned long long activationThresholdBytes);
115+</pre>
116+<div class="fcndef">
117+ <p>
118+ Sets number of allocated bytes of dynamic memory
119+ needed to activate protection against disproportionate use of RAM
120+ (default: <code>64 MiB</code>)
121+ of parser <code>p</code> to <code>activationThresholdBytes</code>, and
122+ returns <code>XML_TRUE</code> upon success and <code>XML_FALSE</code> upon error.
123+ </p>
124+
125+ <p>
126+ <strong>Note:</strong>
127+ For types of allocations that intentionally bypass tracking and limiting, please see
128+ <code><a href="#XML_SetAllocTrackerMaximumAmplification">XML_SetAllocTrackerMaximumAmplification</a></code>
129+ above.
130+ </p>
131+
132+ <p>For a call to <code>XML_SetAllocTrackerActivationThreshold</code> to succeed:</p>
133+ <ul>
134+ <li>parser <code>p</code> must be a non-<code>NULL</code> root parser (without any parent parsers).</li>
135+ </ul>
136+
137+ <p>
138+ <strong>Note:</strong>
139+ If you ever need to increase this value for non-attack payload,
140+ please <a href="https://github.com/libexpat/libexpat/issues">file a bug report</a>.
141+ </p>
142+</div>
143+
144 <h4 id="XML_SetReparseDeferralEnabled">XML_SetReparseDeferralEnabled</h4>
145 <pre class="fcndec">
146 /* 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 0000000000..ca0e3a34f7
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2025-59375-17.patch
@@ -0,0 +1,28 @@
1From a6a2a49367f03f5d8a73c9027b45b59953ca27d8 Mon Sep 17 00:00:00 2001
2From: Sebastian Pipping <sebastian@pipping.org>
3Date: Wed, 10 Sep 2025 19:52:39 +0200
4Subject: [PATCH] docs: Promote the contract to call XML_FreeContentModel
5
6.. when registering a custom element declaration handler
7(via a call to function XML_SetElementDeclHandler)
8
9CVE: CVE-2025-59375
10Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/a6a2a49367f03f5d8a73c9027b45b59953ca27d8]
11Signed-off-by: Peter Marko <peter.marko@siemens.com>
12---
13 doc/reference.html | 2 +-
14 1 file changed, 1 insertion(+), 1 deletion(-)
15
16diff --git a/doc/reference.html b/doc/reference.html
17index 81da4e6c..564fc1b2 100644
18--- a/doc/reference.html
19+++ b/doc/reference.html
20@@ -1902,7 +1902,7 @@ struct XML_cp {
21 <p>Sets a handler for element declarations in a DTD. The handler gets
22 called with the name of the element in the declaration and a pointer
23 to a structure that contains the element model. It's the user code's
24-responsibility to free model when finished with it. See <code>
25+responsibility to free model when finished with via a call to <code>
26 <a href="#XML_FreeContentModel">XML_FreeContentModel</a></code>.
27 There is no need to free the model from the handler, it can be kept
28 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 0000000000..c29b301825
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2025-59375-18.patch
@@ -0,0 +1,74 @@
1From a21a3a8299e1ee0b0ae5ae2886a0746d088cf135 Mon Sep 17 00:00:00 2001
2From: Sebastian Pipping <sebastian@pipping.org>
3Date: Sun, 7 Sep 2025 16:00:35 +0200
4Subject: [PATCH] Changes: Document allocation tracking
5
6CVE: CVE-2025-59375
7Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/a21a3a8299e1ee0b0ae5ae2886a0746d088cf135]
8Signed-off-by: Peter Marko <peter.marko@siemens.com>
9---
10 Changes | 37 +++++++++++++++++++++++++++++++++++++
11 1 file changed, 37 insertions(+)
12
13diff --git a/Changes b/Changes
14index cb752151..ceb5c5dc 100644
15--- a/Changes
16+++ b/Changes
17@@ -30,6 +30,36 @@
18 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
19
20 Patches:
21+ Security fixes:
22+ #1018 #1034 CVE-2025-59375 -- Disallow use of disproportional amounts of
23+ dynamic memory from within an Expat parser (e.g. previously
24+ a ~250 KiB sized document was able to cause allocation of
25+ ~800 MiB from the heap, i.e. an "amplification" of factor
26+ ~3,300); once a threshold (that defaults to 64 MiB) is
27+ reached, a maximum amplification factor (that defaults to
28+ 100.0) is enforced, and violating documents are rejected
29+ with an out-of-memory error.
30+ There are two new API functions to fine-tune this new
31+ behavior:
32+ - XML_SetAllocTrackerActivationThreshold
33+ - XML_SetAllocTrackerMaximumAmplification .
34+ If you ever need to increase these defaults for non-attack
35+ XML payload, please file a bug report with libexpat.
36+ There is also a new environment variable
37+ EXPAT_MALLOC_DEBUG=(0|1|2) to control the verbosity
38+ of allocations debugging at runtime, disabled by default.
39+ Known impact is (reliable and easy) denial of service:
40+ 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
41+ (Base Score: 7.5, Temporal Score: 7.2)
42+ Please note that a layer of compression around XML can
43+ significantly reduce the minimum attack payload size.
44+ Distributors intending to backport (or cherry-pick) the
45+ fix need to copy 99% of the related pull request, not just
46+ the "lib: Implement tracking of dynamic memory allocations"
47+ commit, to not end up with a state that literally does both
48+ too much and too little at the same time. Appending ".diff"
49+ to the pull request URL could be of help.
50+
51 Bug fixes:
52 #980 #989 Restore event pointer behavior from Expat 2.6.4
53 (that the fix to CVE-2024-8176 changed in 2.7.0);
54@@ -39,6 +69,10 @@ Patches:
55 - XML_GetCurrentColumnNumber
56 - XML_GetCurrentLineNumber
57 - XML_GetInputContext
58+ #1034 docs: Promote the contract to call function
59+ XML_FreeContentModel when registering a custom
60+ element declaration handler (via a call to function
61+ XML_SetElementDeclHandler)
62
63 Special thanks to:
64 Berkay Eren Ürün
65@@ -71,6 +105,9 @@ Patches:
66 Linutronix
67 Red Hat
68 Siemens
69+ and
70+ OSS-Fuzz / ClusterFuzz
71+ Perl XML::Parser
72
73 Release 2.6.4 Wed November 6 2024
74 Security fixes:
diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-19.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-19.patch
new file mode 100644
index 0000000000..afd4d91d03
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2025-59375-19.patch
@@ -0,0 +1,103 @@
1From f4b5bb033dc4430bbd31dcae8a55f988360bcec5 Mon Sep 17 00:00:00 2001
2From: Sebastian Pipping <sebastian@pipping.org>
3Date: Wed, 17 Sep 2025 23:14:02 +0200
4Subject: [PATCH] lib: Document and regression-proof absence of integer
5 overflow from expat_realloc
6
7Matthew Fernandez (@Smattr) and I teamed up on whether function expat_realloc
8could be vulnerable to integer overflow in line:
9
10 mallocedPtr = parser->m_mem.realloc_fcn(mallocedPtr, sizeof(size_t) + size);
11 ^
12We ended up with a mathematical proof that, fortunately, the current code
13already is safe from overflow.
14
15The proof uses technique "proof by contradiction". Let's assume, there *was* a
16risk of integer overflow. For a risk of overflow, these four conditions would
17all need to be met, together:
18
19(1) `SIZE_MAX < sizeof(size_t) + size`
20 or we would not hit an overflow on `size_t`.
21
22(2) `size > prevSize`
23 or `expat_malloc` would have already not allocated earlier
24 as `expat_realloc` relies on `expat_malloc` for the initial allocation.
25
26(3) `rootParser->m_alloc_tracker.bytesAllocated >= sizeof(size_t) + prevSize`
27 or the previous allocation would be gone already or have bypassed accounting.
28 The code is not thread-safe in general, race conditions are off the table.
29
30(4) `rootParser->m_alloc_tracker.bytesAllocated + (size - prevSize) <= SIZE_MAX`
31 or `expat_heap_increase_tolerable` would have returned `false` and
32 the overflow line would not be reached.
33
34We encoded this for the Z3 Theorem Prover (https://github.com/Z3Prover/z3)
35and ended up with this document:
36
37 $ cat proof_v2.smt2
38 ; Copyright (c) 2025 Matthew Fernandez <matthew.fernandez@gmail.com>
39 ; Copyright (c) 2025 Sebastian Pipping <sebastian@pipping.org>
40 ; Licensed under the MIT license
41
42 ; (1), (2), (3), (4) form a contradiction
43
44 ; define `SIZE_MAX`
45 (declare-fun SIZE_MAX () (_ BitVec 64))
46 (assert (= SIZE_MAX #xffffffffffffffff))
47
48 ; define `sizeof(size_t)`
49 (declare-fun sizeof_size_t () (_ BitVec 64))
50 (assert (= sizeof_size_t #x0000000000000008))
51
52 ; claim we have inputs `size`, `prevSize`, and `bytesAllocated`
53 (declare-fun size () (_ BitVec 64))
54 (declare-fun prevSize () (_ BitVec 64))
55 (declare-fun bytesAllocated () (_ BitVec 64))
56
57 ; assume `SIZE_MAX - sizeof(size_t) < size` (1)
58 (assert (bvult (bvsub SIZE_MAX sizeof_size_t) size))
59
60 ; assume `bytesAllocated >= sizeof(size_t) + prevSize` (3)
61 (assert (bvuge bytesAllocated (bvadd sizeof_size_t prevSize)))
62
63 ; assume `bytesAllocated - prevSize <= SIZE_MAX - size` (4)
64 (assert (bvule (bvsub bytesAllocated prevSize) (bvsub SIZE_MAX size)))
65
66 ; assume `SIZE_MAX - sizeof(size_t) >= prevSize` (anti-overflow for 3)
67 (assert (bvuge (bvsub SIZE_MAX sizeof_size_t) prevSize))
68
69 ; prove we have a contradiction
70 (check-sat)
71
72Note that we operate on fixed-size bit vectors here, and hence had
73to transform the assertions to not allow integer overflow by themselves.
74
75Z3 confirms the contradiction, and thus the absence of integer overflow:
76
77 $ z3 -smt2 -model proof_v2.smt2
78 unsat
79
80Co-authored-by: Matthew Fernandez <matthew.fernandez@gmail.com>
81
82CVE: CVE-2025-59375
83Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/f4b5bb033dc4430bbd31dcae8a55f988360bcec5]
84Signed-off-by: Peter Marko <peter.marko@siemens.com>
85---
86 lib/xmlparse.c | 4 ++++
87 1 file changed, 4 insertions(+)
88
89diff --git a/lib/xmlparse.c b/lib/xmlparse.c
90index de159493..24fd7b97 100644
91--- a/lib/xmlparse.c
92+++ b/lib/xmlparse.c
93@@ -969,6 +969,10 @@ expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine) {
94 }
95 }
96
97+ // NOTE: Integer overflow detection has already been done for us
98+ // by expat_heap_increase_tolerable(..) above
99+ assert(SIZE_MAX - sizeof(size_t) >= size);
100+
101 // Actually allocate
102 mallocedPtr = parser->m_mem.realloc_fcn(mallocedPtr, sizeof(size_t) + size);
103
diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-20.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-20.patch
new file mode 100644
index 0000000000..80628f20fb
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2025-59375-20.patch
@@ -0,0 +1,285 @@
1From faf36f806c9065bfd9f0567b01924d5e27c4911c Mon Sep 17 00:00:00 2001
2From: Sebastian Pipping <sebastian@pipping.org>
3Date: Mon, 15 Sep 2025 18:05:23 +0200
4Subject: [PATCH] lib: Drop casts around malloc/realloc returns that C99 does
5 not need
6
7CVE: CVE-2025-59375
8Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/faf36f806c9065bfd9f0567b01924d5e27c4911c]
9Signed-off-by: Peter Marko <peter.marko@siemens.com>
10---
11 lib/xmlparse.c | 80 ++++++++++++++++++++++----------------------------
12 1 file changed, 35 insertions(+), 45 deletions(-)
13
14diff --git a/lib/xmlparse.c b/lib/xmlparse.c
15index 6e9c6fb2..fb8ad2e7 100644
16--- a/lib/xmlparse.c
17+++ b/lib/xmlparse.c
18@@ -1370,12 +1370,12 @@ parserCreate(const XML_Char *encodingName,
19 XML_Memory_Handling_Suite *mtemp;
20 #if XML_GE == 1
21 void *const sizeAndParser
22- = (XML_Parser)malloc(sizeof(size_t) + sizeof(struct XML_ParserStruct));
23+ = malloc(sizeof(size_t) + sizeof(struct XML_ParserStruct));
24 if (sizeAndParser != NULL) {
25 *(size_t *)sizeAndParser = sizeof(struct XML_ParserStruct);
26 parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t));
27 #else
28- parser = (XML_Parser)malloc(sizeof(struct XML_ParserStruct));
29+ parser = malloc(sizeof(struct XML_ParserStruct));
30 if (parser != NULL) {
31 #endif
32 mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem);
33@@ -1433,23 +1433,20 @@ parserCreate(const XML_Char *encodingName,
34 parser->m_bufferLim = NULL;
35
36 parser->m_attsSize = INIT_ATTS_SIZE;
37- parser->m_atts
38- = (ATTRIBUTE *)MALLOC(parser, parser->m_attsSize * sizeof(ATTRIBUTE));
39+ parser->m_atts = MALLOC(parser, parser->m_attsSize * sizeof(ATTRIBUTE));
40 if (parser->m_atts == NULL) {
41 FREE(parser, parser);
42 return NULL;
43 }
44 #ifdef XML_ATTR_INFO
45- parser->m_attInfo = (XML_AttrInfo *)MALLOC(
46- parser, parser->m_attsSize * sizeof(XML_AttrInfo));
47+ parser->m_attInfo = MALLOC(parser, parser->m_attsSize * sizeof(XML_AttrInfo));
48 if (parser->m_attInfo == NULL) {
49 FREE(parser, parser->m_atts);
50 FREE(parser, parser);
51 return NULL;
52 }
53 #endif
54- parser->m_dataBuf
55- = (XML_Char *)MALLOC(parser, INIT_DATA_BUF_SIZE * sizeof(XML_Char));
56+ parser->m_dataBuf = MALLOC(parser, INIT_DATA_BUF_SIZE * sizeof(XML_Char));
57 if (parser->m_dataBuf == NULL) {
58 FREE(parser, parser->m_atts);
59 #ifdef XML_ATTR_INFO
60@@ -2588,7 +2585,7 @@ XML_GetBuffer(XML_Parser parser, int len) {
61 }
62 // NOTE: We are avoiding MALLOC(..) here to leave limiting
63 // the input size to the application using Expat.
64- newBuf = (char *)parser->m_mem.malloc_fcn(bufferSize);
65+ newBuf = parser->m_mem.malloc_fcn(bufferSize);
66 if (newBuf == 0) {
67 parser->m_errorCode = XML_ERROR_NO_MEMORY;
68 return NULL;
69@@ -3133,7 +3130,7 @@ storeRawNames(XML_Parser parser) {
70 return XML_FALSE;
71 bufSize = nameLen + (int)rawNameLen;
72 if (bufSize > tag->bufEnd - tag->buf) {
73- char *temp = (char *)REALLOC(parser, tag->buf, bufSize);
74+ char *temp = REALLOC(parser, tag->buf, bufSize);
75 if (temp == NULL)
76 return XML_FALSE;
77 /* if tag->name.str points to tag->buf (only when namespace
78@@ -3459,10 +3456,10 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
79 tag = parser->m_freeTagList;
80 parser->m_freeTagList = parser->m_freeTagList->parent;
81 } else {
82- tag = (TAG *)MALLOC(parser, sizeof(TAG));
83+ tag = MALLOC(parser, sizeof(TAG));
84 if (! tag)
85 return XML_ERROR_NO_MEMORY;
86- tag->buf = (char *)MALLOC(parser, INIT_TAG_BUF_SIZE);
87+ tag->buf = MALLOC(parser, INIT_TAG_BUF_SIZE);
88 if (! tag->buf) {
89 FREE(parser, tag);
90 return XML_ERROR_NO_MEMORY;
91@@ -3495,7 +3492,7 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
92 }
93 bufSize = (int)(tag->bufEnd - tag->buf) << 1;
94 {
95- char *temp = (char *)REALLOC(parser, tag->buf, bufSize);
96+ char *temp = REALLOC(parser, tag->buf, bufSize);
97 if (temp == NULL)
98 return XML_ERROR_NO_MEMORY;
99 tag->buf = temp;
100@@ -3874,8 +3871,8 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
101 }
102 #endif
103
104- temp = (ATTRIBUTE *)REALLOC(parser, (void *)parser->m_atts,
105- parser->m_attsSize * sizeof(ATTRIBUTE));
106+ temp = REALLOC(parser, (void *)parser->m_atts,
107+ parser->m_attsSize * sizeof(ATTRIBUTE));
108 if (temp == NULL) {
109 parser->m_attsSize = oldAttsSize;
110 return XML_ERROR_NO_MEMORY;
111@@ -3893,8 +3890,8 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
112 }
113 # endif
114
115- temp2 = (XML_AttrInfo *)REALLOC(parser, (void *)parser->m_attInfo,
116- parser->m_attsSize * sizeof(XML_AttrInfo));
117+ temp2 = REALLOC(parser, (void *)parser->m_attInfo,
118+ parser->m_attsSize * sizeof(XML_AttrInfo));
119 if (temp2 == NULL) {
120 parser->m_attsSize = oldAttsSize;
121 return XML_ERROR_NO_MEMORY;
122@@ -4070,8 +4067,7 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
123 }
124 #endif
125
126- temp = (NS_ATT *)REALLOC(parser, parser->m_nsAtts,
127- nsAttsSize * sizeof(NS_ATT));
128+ temp = REALLOC(parser, parser->m_nsAtts, nsAttsSize * sizeof(NS_ATT));
129 if (! temp) {
130 /* Restore actual size of memory in m_nsAtts */
131 parser->m_nsAttsPower = oldNsAttsPower;
132@@ -4252,7 +4248,7 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
133 }
134 #endif
135
136- uri = (XML_Char *)MALLOC(parser, (n + EXPAND_SPARE) * sizeof(XML_Char));
137+ uri = MALLOC(parser, (n + EXPAND_SPARE) * sizeof(XML_Char));
138 if (! uri)
139 return XML_ERROR_NO_MEMORY;
140 binding->uriAlloc = n + EXPAND_SPARE;
141@@ -4498,8 +4494,8 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId,
142 }
143 #endif
144
145- XML_Char *temp = (XML_Char *)REALLOC(
146- parser, b->uri, sizeof(XML_Char) * (len + EXPAND_SPARE));
147+ XML_Char *temp
148+ = REALLOC(parser, b->uri, sizeof(XML_Char) * (len + EXPAND_SPARE));
149 if (temp == NULL)
150 return XML_ERROR_NO_MEMORY;
151 b->uri = temp;
152@@ -4507,7 +4503,7 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId,
153 }
154 parser->m_freeBindingList = b->nextTagBinding;
155 } else {
156- b = (BINDING *)MALLOC(parser, sizeof(BINDING));
157+ b = MALLOC(parser, sizeof(BINDING));
158 if (! b)
159 return XML_ERROR_NO_MEMORY;
160
161@@ -4525,8 +4521,7 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId,
162 }
163 #endif
164
165- b->uri
166- = (XML_Char *)MALLOC(parser, sizeof(XML_Char) * (len + EXPAND_SPARE));
167+ b->uri = MALLOC(parser, sizeof(XML_Char) * (len + EXPAND_SPARE));
168 if (! b->uri) {
169 FREE(parser, b);
170 return XML_ERROR_NO_MEMORY;
171@@ -5897,7 +5892,7 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
172 return XML_ERROR_NO_MEMORY;
173 }
174
175- char *const new_connector = (char *)REALLOC(
176+ char *const new_connector = REALLOC(
177 parser, parser->m_groupConnector, parser->m_groupSize *= 2);
178 if (new_connector == NULL) {
179 parser->m_groupSize /= 2;
180@@ -5917,15 +5912,14 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
181 }
182 #endif
183
184- int *const new_scaff_index = (int *)REALLOC(
185+ int *const new_scaff_index = REALLOC(
186 parser, dtd->scaffIndex, parser->m_groupSize * sizeof(int));
187 if (new_scaff_index == NULL)
188 return XML_ERROR_NO_MEMORY;
189 dtd->scaffIndex = new_scaff_index;
190 }
191 } else {
192- parser->m_groupConnector
193- = (char *)MALLOC(parser, parser->m_groupSize = 32);
194+ parser->m_groupConnector = MALLOC(parser, parser->m_groupSize = 32);
195 if (! parser->m_groupConnector) {
196 parser->m_groupSize = 0;
197 return XML_ERROR_NO_MEMORY;
198@@ -6086,8 +6080,7 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
199 // applications that are not using XML_FreeContentModel but
200 // plain free(..) or .free_fcn() to free the content model's
201 // memory are safe.
202- XML_Content *content
203- = (XML_Content *)parser->m_mem.malloc_fcn(sizeof(XML_Content));
204+ XML_Content *content = parser->m_mem.malloc_fcn(sizeof(XML_Content));
205 if (! content)
206 return XML_ERROR_NO_MEMORY;
207 content->quant = XML_CQUANT_NONE;
208@@ -6364,8 +6357,7 @@ processEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl,
209 openEntity = *freeEntityList;
210 *freeEntityList = openEntity->next;
211 } else {
212- openEntity
213- = (OPEN_INTERNAL_ENTITY *)MALLOC(parser, sizeof(OPEN_INTERNAL_ENTITY));
214+ openEntity = MALLOC(parser, sizeof(OPEN_INTERNAL_ENTITY));
215 if (! openEntity)
216 return XML_ERROR_NO_MEMORY;
217 }
218@@ -7164,8 +7156,8 @@ defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, XML_Bool isCdata,
219 if (type->nDefaultAtts == type->allocDefaultAtts) {
220 if (type->allocDefaultAtts == 0) {
221 type->allocDefaultAtts = 8;
222- type->defaultAtts = (DEFAULT_ATTRIBUTE *)MALLOC(
223- parser, type->allocDefaultAtts * sizeof(DEFAULT_ATTRIBUTE));
224+ type->defaultAtts
225+ = MALLOC(parser, type->allocDefaultAtts * sizeof(DEFAULT_ATTRIBUTE));
226 if (! type->defaultAtts) {
227 type->allocDefaultAtts = 0;
228 return 0;
229@@ -7190,8 +7182,8 @@ defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, XML_Bool isCdata,
230 }
231 #endif
232
233- temp = (DEFAULT_ATTRIBUTE *)REALLOC(parser, type->defaultAtts,
234- (count * sizeof(DEFAULT_ATTRIBUTE)));
235+ temp = REALLOC(parser, type->defaultAtts,
236+ (count * sizeof(DEFAULT_ATTRIBUTE)));
237 if (temp == NULL)
238 return 0;
239 type->allocDefaultAtts = count;
240@@ -8145,8 +8137,7 @@ poolGrow(STRING_POOL *pool) {
241 if (bytesToAllocate == 0)
242 return XML_FALSE;
243
244- temp = (BLOCK *)REALLOC(pool->parser, pool->blocks,
245- (unsigned)bytesToAllocate);
246+ temp = REALLOC(pool->parser, pool->blocks, (unsigned)bytesToAllocate);
247 if (temp == NULL)
248 return XML_FALSE;
249 pool->blocks = temp;
250@@ -8217,7 +8208,7 @@ nextScaffoldPart(XML_Parser parser) {
251 return -1;
252 }
253 #endif
254- dtd->scaffIndex = (int *)MALLOC(parser, parser->m_groupSize * sizeof(int));
255+ dtd->scaffIndex = MALLOC(parser, parser->m_groupSize * sizeof(int));
256 if (! dtd->scaffIndex)
257 return -1;
258 dtd->scaffIndex[0] = 0;
259@@ -8240,14 +8231,13 @@ nextScaffoldPart(XML_Parser parser) {
260 }
261 #endif
262
263- temp = (CONTENT_SCAFFOLD *)REALLOC(
264- parser, dtd->scaffold, dtd->scaffSize * 2 * sizeof(CONTENT_SCAFFOLD));
265+ temp = REALLOC(parser, dtd->scaffold,
266+ dtd->scaffSize * 2 * sizeof(CONTENT_SCAFFOLD));
267 if (temp == NULL)
268 return -1;
269 dtd->scaffSize *= 2;
270 } else {
271- temp = (CONTENT_SCAFFOLD *)MALLOC(parser, INIT_SCAFFOLD_ELEMENTS
272- * sizeof(CONTENT_SCAFFOLD));
273+ temp = MALLOC(parser, INIT_SCAFFOLD_ELEMENTS * sizeof(CONTENT_SCAFFOLD));
274 if (temp == NULL)
275 return -1;
276 dtd->scaffSize = INIT_SCAFFOLD_ELEMENTS;
277@@ -8304,7 +8294,7 @@ build_model(XML_Parser parser) {
278 // NOTE: We are avoiding MALLOC(..) here to so that
279 // applications that are not using XML_FreeContentModel but plain
280 // free(..) or .free_fcn() to free the content model's memory are safe.
281- ret = (XML_Content *)parser->m_mem.malloc_fcn(allocsize);
282+ ret = parser->m_mem.malloc_fcn(allocsize);
283 if (! ret)
284 return NULL;
285
diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-21.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-21.patch
new file mode 100644
index 0000000000..38bc0d1dd8
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2025-59375-21.patch
@@ -0,0 +1,196 @@
1From 4b43b8dacc96fd538254e17a69abc9745c3a2ed4 Mon Sep 17 00:00:00 2001
2From: Sebastian Pipping <sebastian@pipping.org>
3Date: Fri, 19 Sep 2025 23:32:46 +0200
4Subject: [PATCH] lib: Fix alignment of internal allocations for some non-amd64
5 architectures
6
7sparc32 is known to be affected.
8
9CVE: CVE-2025-59375
10Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/4b43b8dacc96fd538254e17a69abc9745c3a2ed4]
11Signed-off-by: Peter Marko <peter.marko@siemens.com>
12---
13 lib/internal.h | 6 ++++++
14 lib/xmlparse.c | 38 ++++++++++++++++++++++----------------
15 tests/alloc_tests.c | 13 ++++++++++---
16 3 files changed, 38 insertions(+), 19 deletions(-)
17
18diff --git a/lib/internal.h b/lib/internal.h
19index 6e087858..8f5edf48 100644
20--- a/lib/internal.h
21+++ b/lib/internal.h
22@@ -108,6 +108,7 @@
23 #endif
24
25 #include <limits.h> // ULONG_MAX
26+#include <stddef.h> // size_t
27
28 #if defined(_WIN32) \
29 && (! defined(__USE_MINGW_ANSI_STDIO) \
30@@ -150,6 +151,11 @@
31 #define EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT \
32 67108864 // 64 MiB, 2^26
33
34+// NOTE: If function expat_alloc was user facing, EXPAT_MALLOC_ALIGNMENT would
35+// have to take sizeof(long double) into account
36+#define EXPAT_MALLOC_ALIGNMENT sizeof(long long) // largest parser (sub)member
37+#define EXPAT_MALLOC_PADDING ((EXPAT_MALLOC_ALIGNMENT) - sizeof(size_t))
38+
39 /* NOTE END */
40
41 #include "expat.h" // so we can use type XML_Parser below
42diff --git a/lib/xmlparse.c b/lib/xmlparse.c
43index 24fd7b97..ce29ab6f 100644
44--- a/lib/xmlparse.c
45+++ b/lib/xmlparse.c
46@@ -850,14 +850,14 @@ static void *
47 # endif
48 expat_malloc(XML_Parser parser, size_t size, int sourceLine) {
49 // Detect integer overflow
50- if (SIZE_MAX - size < sizeof(size_t)) {
51+ if (SIZE_MAX - size < sizeof(size_t) + EXPAT_MALLOC_PADDING) {
52 return NULL;
53 }
54
55 const XML_Parser rootParser = getRootParserOf(parser, NULL);
56 assert(rootParser->m_parentParser == NULL);
57
58- const size_t bytesToAllocate = sizeof(size_t) + size;
59+ const size_t bytesToAllocate = sizeof(size_t) + EXPAT_MALLOC_PADDING + size;
60
61 if ((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated
62 < bytesToAllocate) {
63@@ -894,7 +894,7 @@ expat_malloc(XML_Parser parser, size_t size, int sourceLine) {
64 rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine);
65 }
66
67- return (char *)mallocedPtr + sizeof(size_t);
68+ return (char *)mallocedPtr + sizeof(size_t) + EXPAT_MALLOC_PADDING;
69 }
70
71 # if defined(XML_TESTING)
72@@ -914,8 +914,9 @@ expat_free(XML_Parser parser, void *ptr, int sourceLine) {
73
74 // Extract size (to the eyes of malloc_fcn/realloc_fcn) and
75 // the original pointer returned by malloc/realloc
76- void *const mallocedPtr = (char *)ptr - sizeof(size_t);
77- const size_t bytesAllocated = sizeof(size_t) + *(size_t *)mallocedPtr;
78+ void *const mallocedPtr = (char *)ptr - EXPAT_MALLOC_PADDING - sizeof(size_t);
79+ const size_t bytesAllocated
80+ = sizeof(size_t) + EXPAT_MALLOC_PADDING + *(size_t *)mallocedPtr;
81
82 // Update accounting
83 assert(rootParser->m_alloc_tracker.bytesAllocated >= bytesAllocated);
84@@ -954,7 +955,7 @@ expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine) {
85
86 // Extract original size (to the eyes of the caller) and the original
87 // pointer returned by malloc/realloc
88- void *mallocedPtr = (char *)ptr - sizeof(size_t);
89+ void *mallocedPtr = (char *)ptr - EXPAT_MALLOC_PADDING - sizeof(size_t);
90 const size_t prevSize = *(size_t *)mallocedPtr;
91
92 // Classify upcoming change
93@@ -971,10 +972,11 @@ expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine) {
94
95 // NOTE: Integer overflow detection has already been done for us
96 // by expat_heap_increase_tolerable(..) above
97- assert(SIZE_MAX - sizeof(size_t) >= size);
98+ assert(SIZE_MAX - sizeof(size_t) - EXPAT_MALLOC_PADDING >= size);
99
100 // Actually allocate
101- mallocedPtr = parser->m_mem.realloc_fcn(mallocedPtr, sizeof(size_t) + size);
102+ mallocedPtr = parser->m_mem.realloc_fcn(
103+ mallocedPtr, sizeof(size_t) + EXPAT_MALLOC_PADDING + size);
104
105 if (mallocedPtr == NULL) {
106 return NULL;
107@@ -1005,7 +1007,7 @@ expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine) {
108 // Update in-block recorded size
109 *(size_t *)mallocedPtr = size;
110
111- return (char *)mallocedPtr + sizeof(size_t);
112+ return (char *)mallocedPtr + sizeof(size_t) + EXPAT_MALLOC_PADDING;
113 }
114 #endif // XML_GE == 1
115
116@@ -1337,7 +1339,8 @@ parserCreate(const XML_Char *encodingName,
117 XML_Parser parser = NULL;
118
119 #if XML_GE == 1
120- const size_t increase = sizeof(size_t) + sizeof(struct XML_ParserStruct);
121+ const size_t increase
122+ = sizeof(size_t) + EXPAT_MALLOC_PADDING + sizeof(struct XML_ParserStruct);
123
124 if (parentParser != NULL) {
125 const XML_Parser rootParser = getRootParserOf(parentParser, NULL);
126@@ -1352,11 +1355,13 @@ parserCreate(const XML_Char *encodingName,
127 if (memsuite) {
128 XML_Memory_Handling_Suite *mtemp;
129 #if XML_GE == 1
130- void *const sizeAndParser = memsuite->malloc_fcn(
131- sizeof(size_t) + sizeof(struct XML_ParserStruct));
132+ void *const sizeAndParser
133+ = memsuite->malloc_fcn(sizeof(size_t) + EXPAT_MALLOC_PADDING
134+ + sizeof(struct XML_ParserStruct));
135 if (sizeAndParser != NULL) {
136 *(size_t *)sizeAndParser = sizeof(struct XML_ParserStruct);
137- parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t));
138+ parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t)
139+ + EXPAT_MALLOC_PADDING);
140 #else
141 parser = memsuite->malloc_fcn(sizeof(struct XML_ParserStruct));
142 if (parser != NULL) {
143@@ -1369,11 +1374,12 @@ parserCreate(const XML_Char *encodingName,
144 } else {
145 XML_Memory_Handling_Suite *mtemp;
146 #if XML_GE == 1
147- void *const sizeAndParser
148- = malloc(sizeof(size_t) + sizeof(struct XML_ParserStruct));
149+ void *const sizeAndParser = malloc(sizeof(size_t) + EXPAT_MALLOC_PADDING
150+ + sizeof(struct XML_ParserStruct));
151 if (sizeAndParser != NULL) {
152 *(size_t *)sizeAndParser = sizeof(struct XML_ParserStruct);
153- parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t));
154+ parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t)
155+ + EXPAT_MALLOC_PADDING);
156 #else
157 parser = malloc(sizeof(struct XML_ParserStruct));
158 if (parser != NULL) {
159diff --git a/tests/alloc_tests.c b/tests/alloc_tests.c
160index 644a4952..dabdf0da 100644
161--- a/tests/alloc_tests.c
162+++ b/tests/alloc_tests.c
163@@ -2091,6 +2091,13 @@ START_TEST(test_alloc_reset_after_external_entity_parser_create_fail) {
164 }
165 END_TEST
166
167+#if XML_GE == 1
168+static size_t
169+sizeRecordedFor(void *ptr) {
170+ return *(size_t *)((char *)ptr - EXPAT_MALLOC_PADDING - sizeof(size_t));
171+}
172+#endif // XML_GE == 1
173+
174 START_TEST(test_alloc_tracker_size_recorded) {
175 XML_Memory_Handling_Suite memsuite = {malloc, realloc, free};
176
177@@ -2106,16 +2113,16 @@ START_TEST(test_alloc_tracker_size_recorded) {
178 void *ptr = expat_malloc(parser, 10, -1);
179
180 assert_true(ptr != NULL);
181- assert_true(*((size_t *)ptr - 1) == 10);
182+ assert_true(sizeRecordedFor(ptr) == 10);
183
184 assert_true(expat_realloc(parser, ptr, SIZE_MAX / 2, -1) == NULL);
185
186- assert_true(*((size_t *)ptr - 1) == 10); // i.e. unchanged
187+ assert_true(sizeRecordedFor(ptr) == 10); // i.e. unchanged
188
189 ptr = expat_realloc(parser, ptr, 20, -1);
190
191 assert_true(ptr != NULL);
192- assert_true(*((size_t *)ptr - 1) == 20);
193+ assert_true(sizeRecordedFor(ptr) == 20);
194
195 expat_free(parser, ptr, -1);
196 #endif
diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-22.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-22.patch
new file mode 100644
index 0000000000..9716be8084
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2025-59375-22.patch
@@ -0,0 +1,37 @@
1From 5cc0010ad93868ec03248e4ac814272bc7d607bc Mon Sep 17 00:00:00 2001
2From: Sebastian Pipping <sebastian@pipping.org>
3Date: Fri, 19 Sep 2025 22:50:54 +0200
4Subject: [PATCH] tests: Fix test guard for test related to allocation tracking
5
6CVE: CVE-2025-59375
7Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/5cc0010ad93868ec03248e4ac814272bc7d607bc]
8Signed-off-by: Peter Marko <peter.marko@siemens.com>
9---
10 tests/alloc_tests.c | 14 ++++++--------
11 1 file changed, 6 insertions(+), 8 deletions(-)
12
13diff --git a/tests/alloc_tests.c b/tests/alloc_tests.c
14index dabdf0da..045447b0 100644
15--- a/tests/alloc_tests.c
16+++ b/tests/alloc_tests.c
17@@ -2362,14 +2362,12 @@ make_alloc_test_case(Suite *s) {
18 tcase_add_test__ifdef_xml_dtd(
19 tc_alloc, test_alloc_reset_after_external_entity_parser_create_fail);
20
21- tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_tracker_size_recorded);
22- tcase_add_test__ifdef_xml_dtd(tc_alloc,
23- test_alloc_tracker_maximum_amplification);
24- tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_tracker_threshold);
25- tcase_add_test__ifdef_xml_dtd(tc_alloc,
26- test_alloc_tracker_getbuffer_unlimited);
27- tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_tracker_api);
28+ tcase_add_test__if_xml_ge(tc_alloc, test_alloc_tracker_size_recorded);
29+ tcase_add_test__if_xml_ge(tc_alloc, test_alloc_tracker_maximum_amplification);
30+ tcase_add_test__if_xml_ge(tc_alloc, test_alloc_tracker_threshold);
31+ tcase_add_test__if_xml_ge(tc_alloc, test_alloc_tracker_getbuffer_unlimited);
32+ tcase_add_test__if_xml_ge(tc_alloc, test_alloc_tracker_api);
33
34 tcase_add_test(tc_alloc, test_mem_api_cycle);
35- tcase_add_test__ifdef_xml_dtd(tc_alloc, test_mem_api_unlimited);
36+ tcase_add_test__if_xml_ge(tc_alloc, test_mem_api_unlimited);
37 }
diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-23.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-23.patch
new file mode 100644
index 0000000000..60327df22b
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2025-59375-23.patch
@@ -0,0 +1,47 @@
1From 343594dc344e543acb7478d1283b50b299a1c110 Mon Sep 17 00:00:00 2001
2From: Sebastian Pipping <sebastian@pipping.org>
3Date: Fri, 19 Sep 2025 22:46:01 +0200
4Subject: [PATCH] tests: Add new test test_alloc_tracker_pointer_alignment
5
6CVE: CVE-2025-59375
7Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/343594dc344e543acb7478d1283b50b299a1c110]
8Signed-off-by: Peter Marko <peter.marko@siemens.com>
9---
10 tests/alloc_tests.c | 17 +++++++++++++++++
11 1 file changed, 17 insertions(+)
12
13diff --git a/tests/alloc_tests.c b/tests/alloc_tests.c
14index 045447b0..5ae6c6a7 100644
15--- a/tests/alloc_tests.c
16+++ b/tests/alloc_tests.c
17@@ -2132,6 +2132,22 @@ START_TEST(test_alloc_tracker_size_recorded) {
18 }
19 END_TEST
20
21+START_TEST(test_alloc_tracker_pointer_alignment) {
22+ XML_Parser parser = XML_ParserCreate(NULL);
23+#if XML_GE == 1
24+ assert_true(sizeof(long long) >= sizeof(size_t)); // self-test
25+ long long *const ptr
26+ = (long long *)expat_malloc(parser, 4 * sizeof(long long), -1);
27+ ptr[0] = 0LL;
28+ ptr[1] = 1LL;
29+ ptr[2] = 2LL;
30+ ptr[3] = 3LL;
31+ expat_free(parser, ptr, -1);
32+#endif
33+ XML_ParserFree(parser);
34+}
35+END_TEST
36+
37 START_TEST(test_alloc_tracker_maximum_amplification) {
38 if (g_reparseDeferralEnabledDefault == XML_TRUE) {
39 return;
40@@ -2363,6 +2379,7 @@ make_alloc_test_case(Suite *s) {
41 tc_alloc, test_alloc_reset_after_external_entity_parser_create_fail);
42
43 tcase_add_test__if_xml_ge(tc_alloc, test_alloc_tracker_size_recorded);
44+ tcase_add_test__if_xml_ge(tc_alloc, test_alloc_tracker_pointer_alignment);
45 tcase_add_test__if_xml_ge(tc_alloc, test_alloc_tracker_maximum_amplification);
46 tcase_add_test__if_xml_ge(tc_alloc, test_alloc_tracker_threshold);
47 tcase_add_test__if_xml_ge(tc_alloc, test_alloc_tracker_getbuffer_unlimited);
diff --git a/meta/recipes-core/expat/expat/CVE-2025-59375-24.patch b/meta/recipes-core/expat/expat/CVE-2025-59375-24.patch
new file mode 100644
index 0000000000..e51b2bb327
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2025-59375-24.patch
@@ -0,0 +1,36 @@
1From 6fe5df59a1229ca647d365a0e3a7e17fee4d4548 Mon Sep 17 00:00:00 2001
2From: Sebastian Pipping <sebastian@pipping.org>
3Date: Fri, 19 Sep 2025 23:49:18 +0200
4Subject: [PATCH] Changes: Document pull request #1047
5
6CVE: CVE-2025-59375
7Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/6fe5df59a1229ca647d365a0e3a7e17fee4d4548]
8Signed-off-by: Peter Marko <peter.marko@siemens.com>
9---
10 Changes | 5 +++++
11 1 file changed, 5 insertions(+)
12
13diff --git a/Changes b/Changes
14index 706a4ae1..58c222d9 100644
15--- a/Changes
16+++ b/Changes
17@@ -61,6 +61,9 @@ Patches:
18 to the pull request URL could be of help.
19
20 Bug fixes:
21+ #1046 #1047 Fix alignment of internal allocations for some non-amd64
22+ architectures (e.g. sparc32); fixes up on the fix to
23+ CVE-2025-59375 in release 2.7.2 from #1034
24 #980 #989 Restore event pointer behavior from Expat 2.6.4
25 (that the fix to CVE-2024-8176 changed in 2.7.0);
26 affected API functions are:
27@@ -76,7 +79,9 @@ Patches:
28
29 Special thanks to:
30 Berkay Eren Ürün
31+ Rolf Eike Beer
32 and
33+ Clang/GCC UndefinedBehaviorSanitizer
34 Perl XML::Parser
35
36 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 816beaa8a3..1d2d818ecf 100644
--- a/meta/recipes-core/expat/expat_2.6.4.bb
+++ b/meta/recipes-core/expat/expat_2.6.4.bb
@@ -16,6 +16,31 @@ SRC_URI = "${GITHUB_BASE_URI}/download/R_${VERSION_TAG}/expat-${PV}.tar.bz2 \
16 file://CVE-2024-8176-03.patch \ 16 file://CVE-2024-8176-03.patch \
17 file://CVE-2024-8176-04.patch \ 17 file://CVE-2024-8176-04.patch \
18 file://CVE-2024-8176-05.patch \ 18 file://CVE-2024-8176-05.patch \
19 file://CVE-2025-59375-00.patch \
20 file://CVE-2025-59375-01.patch \
21 file://CVE-2025-59375-02.patch \
22 file://CVE-2025-59375-03.patch \
23 file://CVE-2025-59375-04.patch \
24 file://CVE-2025-59375-05.patch \
25 file://CVE-2025-59375-06.patch \
26 file://CVE-2025-59375-07.patch \
27 file://CVE-2025-59375-08.patch \
28 file://CVE-2025-59375-09.patch \
29 file://CVE-2025-59375-10.patch \
30 file://CVE-2025-59375-11.patch \
31 file://CVE-2025-59375-12.patch \
32 file://CVE-2025-59375-13.patch \
33 file://CVE-2025-59375-14.patch \
34 file://CVE-2025-59375-15.patch \
35 file://CVE-2025-59375-16.patch \
36 file://CVE-2025-59375-17.patch \
37 file://CVE-2025-59375-18.patch \
38 file://CVE-2025-59375-19.patch \
39 file://CVE-2025-59375-20.patch \
40 file://CVE-2025-59375-21.patch \
41 file://CVE-2025-59375-22.patch \
42 file://CVE-2025-59375-23.patch \
43 file://CVE-2025-59375-24.patch \
19 " 44 "
20 45
21GITHUB_BASE_URI = "https://github.com/libexpat/libexpat/releases/" 46GITHUB_BASE_URI = "https://github.com/libexpat/libexpat/releases/"