summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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/"