From 543de38e9f3e89c046be6ae5be577e771854467b Mon Sep 17 00:00:00 2001 From: fly_fzc <2385803914@qq.com> Date: Fri, 10 Oct 2025 15:05:22 +0800 Subject: [PATCH] fix CVE-2025-59375 --- ...-function-dtdCreate-use-macro-MALLOC.patch | 47 ++ ...pools-use-macros-MALLOC-FREE-REALLOC.patch | 109 ++++ ...n-hash-tables-use-macros-MALLOC-and-.patch | 127 ++++ ...function-copyString-use-macro-MALLOC.patch | 62 ++ ...ake-function-dtdReset-use-macro-FREE.patch | 64 ++ ...e-function-dtdDestroy-use-macro-FREE.patch | 68 ++ ...ke-function-dtdCopy-use-macro-MALLOC.patch | 52 ++ ...acking-of-dynamic-memory-allocations.patch | 602 ++++++++++++++++++ ...Free-and-XML_FreeContentModel-match-.patch | 42 ++ ...Mem-functions-from-allocation-tracki.patch | 53 ++ ...main-input-buffer-from-allocation-tr.patch | 65 ++ ...content-model-from-allocation-tracki.patch | 57 ++ ...ation-tracker-config-to-existing-arg.patch | 68 ++ ...defined-XML_DTD-XML_GE-1-to-XML_GE-1.patch | 313 +++++++++ ...o-XML_GetFeatureList-and-XML_Feature.patch | 42 ++ expat.spec | 20 +- 16 files changed, 1790 insertions(+), 1 deletion(-) create mode 100644 backport-0001-CVE-2025-59375-lib-Make-function-dtdCreate-use-macro-MALLOC.patch create mode 100644 backport-0002-CVE-2025-59375-lib-Make-string-pools-use-macros-MALLOC-FREE-REALLOC.patch create mode 100644 backport-0003-CVE-2025-59375-lib-Make-function-hash-tables-use-macros-MALLOC-and-.patch create mode 100644 backport-0004-CVE-2025-59375-lib-Make-function-copyString-use-macro-MALLOC.patch create mode 100644 backport-0005-CVE-2025-59375-lib-Make-function-dtdReset-use-macro-FREE.patch create mode 100644 backport-0006-CVE-2025-59375-lib-Make-function-dtdDestroy-use-macro-FREE.patch create mode 100644 backport-0007-CVE-2025-59375-lib-Make-function-dtdCopy-use-macro-MALLOC.patch create mode 100644 backport-0008-CVE-2025-59375-lib-Implement-tracking-of-dynamic-memory-allocations.patch create mode 100644 backport-0009-CVE-2025-59375-lib-Make-XML_MemFree-and-XML_FreeContentModel-match-.patch create mode 100644 backport-0010-CVE-2025-59375-lib-Exclude-XML_Mem-functions-from-allocation-tracki.patch create mode 100644 backport-0011-CVE-2025-59375-lib-Exclude-the-main-input-buffer-from-allocation-tr.patch create mode 100644 backport-0012-CVE-2025-59375-lib-Exclude-the-content-model-from-allocation-tracki.patch create mode 100644 backport-0013-CVE-2025-59375-xmlwf-Wire-allocation-tracker-config-to-existing-arg.patch create mode 100644 backport-pre-0008-CVE-2025-59375-Simplify-defined-XML_DTD-XML_GE-1-to-XML_GE-1.patch create mode 100644 backport-pre-0008-CVE-2025-59375-lib-Add-XML_GE-to-XML_GetFeatureList-and-XML_Feature.patch diff --git a/backport-0001-CVE-2025-59375-lib-Make-function-dtdCreate-use-macro-MALLOC.patch b/backport-0001-CVE-2025-59375-lib-Make-function-dtdCreate-use-macro-MALLOC.patch new file mode 100644 index 0000000..ed5bec5 --- /dev/null +++ b/backport-0001-CVE-2025-59375-lib-Make-function-dtdCreate-use-macro-MALLOC.patch @@ -0,0 +1,47 @@ +From 0872c189db6e457084fca335662a9cb49e8ec4c7 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping +Date: Mon, 1 Sep 2025 18:06:59 +0200 +Subject: [PATCH] lib: Make function dtdCreate use macro MALLOC + +.. and give its body access to the parser for upcoming changes +--- + lib/xmlparse.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index 25f786ec..b9d6eed1 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -555,7 +555,7 @@ static XML_Bool setContext(XML_Parser parser, const XML_Char *context); + + static void FASTCALL normalizePublicId(XML_Char *s); + +-static DTD *dtdCreate(const XML_Memory_Handling_Suite *ms); ++static DTD *dtdCreate(XML_Parser parser); + /* do not call if m_parentParser != NULL */ + static void dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms); + static void dtdDestroy(DTD *p, XML_Bool isDocEntity, +@@ -1170,7 +1170,7 @@ parserCreate(const XML_Char *encodingName, + if (dtd) + parser->m_dtd = dtd; + else { +- parser->m_dtd = dtdCreate(&parser->m_mem); ++ parser->m_dtd = dtdCreate(parser); + if (parser->m_dtd == NULL) { + FREE(parser, parser->m_dataBuf); + FREE(parser, parser->m_atts); +@@ -7128,8 +7128,9 @@ normalizePublicId(XML_Char *publicId) { + } + + static DTD * +-dtdCreate(const XML_Memory_Handling_Suite *ms) { +- DTD *p = ms->malloc_fcn(sizeof(DTD)); ++dtdCreate(XML_Parser parser) { ++ const XML_Memory_Handling_Suite *const ms = &parser->m_mem; ++ DTD *p = MALLOC(parser, sizeof(DTD)); + if (p == NULL) + return p; + poolInit(&(p->pool), ms); +-- +2.33.0 + diff --git a/backport-0002-CVE-2025-59375-lib-Make-string-pools-use-macros-MALLOC-FREE-REALLOC.patch b/backport-0002-CVE-2025-59375-lib-Make-string-pools-use-macros-MALLOC-FREE-REALLOC.patch new file mode 100644 index 0000000..1cfda2c --- /dev/null +++ b/backport-0002-CVE-2025-59375-lib-Make-string-pools-use-macros-MALLOC-FREE-REALLOC.patch @@ -0,0 +1,109 @@ +From 8768dadae479d9f2e984b747fb2ba79bb78de94f Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping +Date: Mon, 1 Sep 2025 18:10:26 +0200 +Subject: [PATCH] lib: Make string pools use macros MALLOC, FREE, REALLOC + +--- + lib/xmlparse.c | 27 +++++++++++++-------------- + 1 file changed, 13 insertions(+), 14 deletions(-) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index b9d6eed1..a56c71ea 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -357,7 +357,7 @@ typedef struct { + const XML_Char *end; + XML_Char *ptr; + XML_Char *start; +- const XML_Memory_Handling_Suite *mem; ++ XML_Parser parser; + } STRING_POOL; + + /* The XML_Char before the name is used to determine whether +@@ -574,8 +574,7 @@ static void FASTCALL hashTableIterInit(HASH_TABLE_ITER *iter, + static void FASTCALL hashTableIterInit(HASH_TABLE_ITER *, const HASH_TABLE *); + static NAMED *FASTCALL hashTableIterNext(HASH_TABLE_ITER *); + +-static void FASTCALL poolInit(STRING_POOL *, +- const XML_Memory_Handling_Suite *ms); ++static void FASTCALL poolInit(STRING_POOL *, XML_Parser parser); + static void FASTCALL poolClear(STRING_POOL *); + static void FASTCALL poolDestroy(STRING_POOL *); + static XML_Char *poolAppend(STRING_POOL *pool, const ENCODING *enc, +@@ -1204,8 +1203,8 @@ parserCreate(const XML_Char *encodingName, + + parser->m_protocolEncodingName = NULL; + +- poolInit(&parser->m_tempPool, &(parser->m_mem)); +- poolInit(&parser->m_temp2Pool, &(parser->m_mem)); ++ poolInit(&parser->m_tempPool, parser); ++ poolInit(&parser->m_temp2Pool, parser); + parserInit(parser, encodingName); + + if (encodingName && ! parser->m_protocolEncodingName) { +@@ -7133,8 +7132,8 @@ dtdCreate(XML_Parser parser) { + DTD *p = MALLOC(parser, sizeof(DTD)); + if (p == NULL) + return p; +- poolInit(&(p->pool), ms); +- poolInit(&(p->entityValuePool), ms); ++ poolInit(&(p->pool), parser); ++ poolInit(&(p->entityValuePool), parser); + hashTableInit(&(p->generalEntities), ms); + hashTableInit(&(p->elementTypes), ms); + hashTableInit(&(p->attributeIds), ms); +@@ -7598,13 +7597,13 @@ hashTableIterNext(HASH_TABLE_ITER *iter) { + } + + static void FASTCALL +-poolInit(STRING_POOL *pool, const XML_Memory_Handling_Suite *ms) { ++poolInit(STRING_POOL *pool, XML_Parser parser) { + pool->blocks = NULL; + pool->freeBlocks = NULL; + pool->start = NULL; + pool->ptr = NULL; + pool->end = NULL; +- pool->mem = ms; ++ pool->parser = parser; + } + + static void FASTCALL +@@ -7631,13 +7630,13 @@ poolDestroy(STRING_POOL *pool) { + BLOCK *p = pool->blocks; + while (p) { + BLOCK *tem = p->next; +- pool->mem->free_fcn(p); ++ FREE(pool->parser, p); + p = tem; + } + p = pool->freeBlocks; + while (p) { + BLOCK *tem = p->next; +- pool->mem->free_fcn(p); ++ FREE(pool->parser, p); + p = tem; + } + } +@@ -7792,8 +7791,8 @@ poolGrow(STRING_POOL *pool) { + if (bytesToAllocate == 0) + return XML_FALSE; + +- temp = (BLOCK *)pool->mem->realloc_fcn(pool->blocks, +- (unsigned)bytesToAllocate); ++ temp = (BLOCK *)REALLOC(pool->parser, pool->blocks, ++ (unsigned)bytesToAllocate); + if (temp == NULL) + return XML_FALSE; + pool->blocks = temp; +@@ -7833,7 +7832,7 @@ poolGrow(STRING_POOL *pool) { + if (bytesToAllocate == 0) + return XML_FALSE; + +- tem = pool->mem->malloc_fcn(bytesToAllocate); ++ tem = MALLOC(pool->parser, bytesToAllocate); + if (! tem) + return XML_FALSE; + tem->size = blockSize; +-- +2.33.0 + diff --git a/backport-0003-CVE-2025-59375-lib-Make-function-hash-tables-use-macros-MALLOC-and-.patch b/backport-0003-CVE-2025-59375-lib-Make-function-hash-tables-use-macros-MALLOC-and-.patch new file mode 100644 index 0000000..f15e88f --- /dev/null +++ b/backport-0003-CVE-2025-59375-lib-Make-function-hash-tables-use-macros-MALLOC-and-.patch @@ -0,0 +1,127 @@ +From 4fc6f1ee9f2b282cfe446bf645c992e37f8c3e15 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping +Date: Mon, 1 Sep 2025 18:14:09 +0200 +Subject: [PATCH] lib: Make function hash tables use macros MALLOC and FREE + +--- + lib/xmlparse.c | 34 ++++++++++++++++------------------ + 1 file changed, 16 insertions(+), 18 deletions(-) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index a56c71ea..a65b0265 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -234,7 +234,7 @@ typedef struct { + unsigned char power; + size_t size; + size_t used; +- const XML_Memory_Handling_Suite *mem; ++ XML_Parser parser; + } HASH_TABLE; + + static size_t keylen(KEY s); +@@ -566,8 +566,7 @@ static int copyEntityTable(XML_Parser oldParser, HASH_TABLE *newTable, + const HASH_TABLE *); + static NAMED *lookup(XML_Parser parser, HASH_TABLE *table, KEY name, + size_t createSize); +-static void FASTCALL hashTableInit(HASH_TABLE *, +- const XML_Memory_Handling_Suite *ms); ++static void FASTCALL hashTableInit(HASH_TABLE *, XML_Parser parser); + static void FASTCALL hashTableClear(HASH_TABLE *); + static void FASTCALL hashTableDestroy(HASH_TABLE *); + static void FASTCALL hashTableIterInit(HASH_TABLE_ITER *, const HASH_TABLE *); +@@ -7128,19 +7127,18 @@ normalizePublicId(XML_Char *publicId) { + + static DTD * + dtdCreate(XML_Parser parser) { +- const XML_Memory_Handling_Suite *const ms = &parser->m_mem; + DTD *p = MALLOC(parser, sizeof(DTD)); + if (p == NULL) + return p; + poolInit(&(p->pool), parser); + poolInit(&(p->entityValuePool), parser); +- hashTableInit(&(p->generalEntities), ms); +- hashTableInit(&(p->elementTypes), ms); +- hashTableInit(&(p->attributeIds), ms); +- hashTableInit(&(p->prefixes), ms); ++ hashTableInit(&(p->generalEntities), parser); ++ hashTableInit(&(p->elementTypes), parser); ++ hashTableInit(&(p->attributeIds), parser); ++ hashTableInit(&(p->prefixes), parser); + #ifdef XML_DTD + p->paramEntityRead = XML_FALSE; +- hashTableInit(&(p->paramEntities), ms); ++ hashTableInit(&(p->paramEntities), parser); + #endif /* XML_DTD */ + p->defaultPrefix.name = NULL; + p->defaultPrefix.binding = NULL; +@@ -7475,7 +7473,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) { + /* table->size is a power of 2 */ + table->size = (size_t)1 << INIT_POWER; + tsize = table->size * sizeof(NAMED *); +- table->v = table->mem->malloc_fcn(tsize); ++ table->v = MALLOC(table->parser, tsize); + if (! table->v) { + table->size = 0; + return NULL; +@@ -7515,7 +7513,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) { + } + + size_t tsize = newSize * sizeof(NAMED *); +- NAMED **newV = table->mem->malloc_fcn(tsize); ++ NAMED **newV = MALLOC(table->parser, tsize); + if (! newV) + return NULL; + memset(newV, 0, tsize); +@@ -7531,7 +7529,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) { + } + newV[j] = table->v[i]; + } +- table->mem->free_fcn(table->v); ++ FREE(table->parser, table->v); + table->v = newV; + table->power = newPower; + table->size = newSize; +@@ -7544,7 +7542,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) { + } + } + } +- table->v[i] = table->mem->malloc_fcn(createSize); ++ table->v[i] = MALLOC(table->parser, createSize); + if (! table->v[i]) + return NULL; + memset(table->v[i], 0, createSize); +@@ -7557,7 +7555,7 @@ static void FASTCALL + hashTableClear(HASH_TABLE *table) { + size_t i; + for (i = 0; i < table->size; i++) { +- table->mem->free_fcn(table->v[i]); ++ FREE(table->parser, table->v[i]); + table->v[i] = NULL; + } + table->used = 0; +@@ -7567,17 +7565,17 @@ static void FASTCALL + hashTableDestroy(HASH_TABLE *table) { + size_t i; + for (i = 0; i < table->size; i++) +- table->mem->free_fcn(table->v[i]); +- table->mem->free_fcn(table->v); ++ FREE(table->parser, table->v[i]); ++ FREE(table->parser, table->v); + } + + static void FASTCALL +-hashTableInit(HASH_TABLE *p, const XML_Memory_Handling_Suite *ms) { ++hashTableInit(HASH_TABLE *p, XML_Parser parser) { + p->power = 0; + p->size = 0; + p->used = 0; + p->v = NULL; +- p->mem = ms; ++ p->parser = parser; + } + + static void FASTCALL +-- +2.33.0 + diff --git a/backport-0004-CVE-2025-59375-lib-Make-function-copyString-use-macro-MALLOC.patch b/backport-0004-CVE-2025-59375-lib-Make-function-copyString-use-macro-MALLOC.patch new file mode 100644 index 0000000..3aea2b7 --- /dev/null +++ b/backport-0004-CVE-2025-59375-lib-Make-function-copyString-use-macro-MALLOC.patch @@ -0,0 +1,62 @@ +From 51487ad9d760faa4809b0f8e189d2f666317e41a Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping +Date: Mon, 1 Sep 2025 17:45:50 +0200 +Subject: [PATCH] lib: Make function copyString use macro MALLOC + +--- + lib/xmlparse.c | 11 +++++------ + 1 file changed, 5 insertions(+), 6 deletions(-) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index a65b0265..c0576abd 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -593,8 +593,7 @@ static XML_Content *build_model(XML_Parser parser); + static ELEMENT_TYPE *getElementType(XML_Parser parser, const ENCODING *enc, + const char *ptr, const char *end); + +-static XML_Char *copyString(const XML_Char *s, +- const XML_Memory_Handling_Suite *memsuite); ++static XML_Char *copyString(const XML_Char *s, XML_Parser parser); + + static unsigned long generate_hash_secret_salt(XML_Parser parser); + static XML_Bool startParsing(XML_Parser parser); +@@ -1235,7 +1234,7 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) { + parser->m_processor = prologInitProcessor; + XmlPrologStateInit(&parser->m_prologState); + if (encodingName != NULL) { +- parser->m_protocolEncodingName = copyString(encodingName, &(parser->m_mem)); ++ parser->m_protocolEncodingName = copyString(encodingName, parser); + } + parser->m_curBase = NULL; + XmlInitEncoding(&parser->m_initEncoding, &parser->m_encoding, 0); +@@ -1423,7 +1422,7 @@ XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName) { + parser->m_protocolEncodingName = NULL; + else { + /* Copy the new encoding name into allocated memory */ +- parser->m_protocolEncodingName = copyString(encodingName, &(parser->m_mem)); ++ parser->m_protocolEncodingName = copyString(encodingName, parser); + if (! parser->m_protocolEncodingName) + return XML_STATUS_ERROR; + } +@@ -8071,7 +8070,7 @@ getElementType(XML_Parser parser, const ENCODING *enc, const char *ptr, + } + + static XML_Char * +-copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) { ++copyString(const XML_Char *s, XML_Parser parser) { + size_t charsRequired = 0; + XML_Char *result; + +@@ -8083,7 +8082,7 @@ copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) { + charsRequired++; + + /* Now allocate space for the copy */ +- result = memsuite->malloc_fcn(charsRequired * sizeof(XML_Char)); ++ result = MALLOC(parser, charsRequired * sizeof(XML_Char)); + if (result == NULL) + return NULL; + /* Copy the original into place */ +-- +2.33.0 + diff --git a/backport-0005-CVE-2025-59375-lib-Make-function-dtdReset-use-macro-FREE.patch b/backport-0005-CVE-2025-59375-lib-Make-function-dtdReset-use-macro-FREE.patch new file mode 100644 index 0000000..3b25b0f --- /dev/null +++ b/backport-0005-CVE-2025-59375-lib-Make-function-dtdReset-use-macro-FREE.patch @@ -0,0 +1,64 @@ +From b3f0bda5f5e979781469532f7c304f7e223568d5 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping +Date: Mon, 1 Sep 2025 17:48:02 +0200 +Subject: [PATCH] lib: Make function dtdReset use macro FREE + +--- + lib/xmlparse.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index c0576abd..65fcce30 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -557,7 +557,7 @@ static void FASTCALL normalizePublicId(XML_Char *s); + + static DTD *dtdCreate(XML_Parser parser); + /* do not call if m_parentParser != NULL */ +-static void dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms); ++static void dtdReset(DTD *p, XML_Parser parser); + static void dtdDestroy(DTD *p, XML_Bool isDocEntity, + const XML_Memory_Handling_Suite *ms); + static int dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, +@@ -1386,7 +1386,7 @@ XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) { + FREE(parser, (void *)parser->m_protocolEncodingName); + parser->m_protocolEncodingName = NULL; + parserInit(parser, encodingName); +- dtdReset(parser->m_dtd, &parser->m_mem); ++ dtdReset(parser->m_dtd, parser); + return XML_TRUE; + } + +@@ -7157,7 +7157,7 @@ dtdCreate(XML_Parser parser) { + } + + static void +-dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) { ++dtdReset(DTD *p, XML_Parser parser) { + HASH_TABLE_ITER iter; + hashTableIterInit(&iter, &(p->elementTypes)); + for (;;) { +@@ -7165,7 +7165,7 @@ dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) { + if (! e) + break; + if (e->allocDefaultAtts != 0) +- ms->free_fcn(e->defaultAtts); ++ FREE(parser, e->defaultAtts); + } + hashTableClear(&(p->generalEntities)); + #ifdef XML_DTD +@@ -7182,9 +7182,9 @@ dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) { + + p->in_eldecl = XML_FALSE; + +- ms->free_fcn(p->scaffIndex); ++ FREE(parser, p->scaffIndex); + p->scaffIndex = NULL; +- ms->free_fcn(p->scaffold); ++ FREE(parser, p->scaffold); + p->scaffold = NULL; + + p->scaffLevel = 0; +-- +2.33.0 + diff --git a/backport-0006-CVE-2025-59375-lib-Make-function-dtdDestroy-use-macro-FREE.patch b/backport-0006-CVE-2025-59375-lib-Make-function-dtdDestroy-use-macro-FREE.patch new file mode 100644 index 0000000..662a63e --- /dev/null +++ b/backport-0006-CVE-2025-59375-lib-Make-function-dtdDestroy-use-macro-FREE.patch @@ -0,0 +1,68 @@ +From 53a3eda0ae2e0317afd071b72b41976053d82732 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping +Date: Mon, 1 Sep 2025 17:50:59 +0200 +Subject: [PATCH] lib: Make function dtdDestroy use macro FREE + +--- + lib/xmlparse.c | 16 +++++++--------- + 1 file changed, 7 insertions(+), 9 deletions(-) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index 65fcce30..e7df97da 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -558,8 +558,7 @@ static void FASTCALL normalizePublicId(XML_Char *s); + static DTD *dtdCreate(XML_Parser parser); + /* do not call if m_parentParser != NULL */ + static void dtdReset(DTD *p, XML_Parser parser); +-static void dtdDestroy(DTD *p, XML_Bool isDocEntity, +- const XML_Memory_Handling_Suite *ms); ++static void dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser); + static int dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, + const XML_Memory_Handling_Suite *ms); + static int copyEntityTable(XML_Parser oldParser, HASH_TABLE *, STRING_POOL *, +@@ -1689,8 +1688,7 @@ XML_ParserFree(XML_Parser parser) { + #else + if (parser->m_dtd) + #endif /* XML_DTD */ +- dtdDestroy(parser->m_dtd, (XML_Bool)! parser->m_parentParser, +- &parser->m_mem); ++ dtdDestroy(parser->m_dtd, (XML_Bool)! parser->m_parentParser, parser); + FREE(parser, (void *)parser->m_atts); + #ifdef XML_ATTR_INFO + FREE(parser, (void *)parser->m_attInfo); +@@ -7198,7 +7196,7 @@ dtdReset(DTD *p, XML_Parser parser) { + } + + static void +-dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) { ++dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser) { + HASH_TABLE_ITER iter; + hashTableIterInit(&iter, &(p->elementTypes)); + for (;;) { +@@ -7206,7 +7204,7 @@ dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) { + if (! e) + break; + if (e->allocDefaultAtts != 0) +- ms->free_fcn(e->defaultAtts); ++ FREE(parser, e->defaultAtts); + } + hashTableDestroy(&(p->generalEntities)); + #ifdef XML_DTD +@@ -7218,10 +7216,10 @@ dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) { + poolDestroy(&(p->pool)); + poolDestroy(&(p->entityValuePool)); + if (isDocEntity) { +- ms->free_fcn(p->scaffIndex); +- ms->free_fcn(p->scaffold); ++ FREE(parser, p->scaffIndex); ++ FREE(parser, p->scaffold); + } +- ms->free_fcn(p); ++ FREE(parser, p); + } + + /* Do a deep copy of the DTD. Return 0 for out of memory, non-zero otherwise. +-- +2.33.0 + diff --git a/backport-0007-CVE-2025-59375-lib-Make-function-dtdCopy-use-macro-MALLOC.patch b/backport-0007-CVE-2025-59375-lib-Make-function-dtdCopy-use-macro-MALLOC.patch new file mode 100644 index 0000000..947246d --- /dev/null +++ b/backport-0007-CVE-2025-59375-lib-Make-function-dtdCopy-use-macro-MALLOC.patch @@ -0,0 +1,52 @@ +From 4e7a5d03daf672f20c73d40dc8970385c18b30d3 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping +Date: Mon, 1 Sep 2025 17:52:58 +0200 +Subject: [PATCH] lib: Make function dtdCopy use macro MALLOC + +--- + lib/xmlparse.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index e7df97da..9f0a8b3e 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -560,7 +560,7 @@ static DTD *dtdCreate(XML_Parser parser); + static void dtdReset(DTD *p, XML_Parser parser); + static void dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser); + static int dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, +- const XML_Memory_Handling_Suite *ms); ++ XML_Parser parser); + static int copyEntityTable(XML_Parser oldParser, HASH_TABLE *, STRING_POOL *, + const HASH_TABLE *); + static NAMED *lookup(XML_Parser parser, HASH_TABLE *table, KEY name, +@@ -1576,7 +1576,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context, + parser->m_prologState.inEntityValue = oldInEntityValue; + if (context) { + #endif /* XML_DTD */ +- if (! dtdCopy(oldParser, parser->m_dtd, oldDtd, &parser->m_mem) ++ if (! dtdCopy(oldParser, parser->m_dtd, oldDtd, parser) + || ! setContext(parser, context)) { + XML_ParserFree(parser); + return NULL; +@@ -7227,7 +7227,7 @@ dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser) { + */ + static int + dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, +- const XML_Memory_Handling_Suite *ms) { ++ XML_Parser parser) { + HASH_TABLE_ITER iter; + + /* Copy the prefix table. */ +@@ -7308,7 +7308,7 @@ dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, + } + #endif + newE->defaultAtts +- = ms->malloc_fcn(oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE)); ++ = MALLOC(parser, oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE)); + if (! newE->defaultAtts) { + return 0; + } +-- +2.33.0 + diff --git a/backport-0008-CVE-2025-59375-lib-Implement-tracking-of-dynamic-memory-allocations.patch b/backport-0008-CVE-2025-59375-lib-Implement-tracking-of-dynamic-memory-allocations.patch new file mode 100644 index 0000000..806f133 --- /dev/null +++ b/backport-0008-CVE-2025-59375-lib-Implement-tracking-of-dynamic-memory-allocations.patch @@ -0,0 +1,602 @@ +From cfce28e171676fe6f70d17b97ed8a59eaeb83f15 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping +Date: Mon, 1 Sep 2025 17:34:58 +0200 +Subject: [PATCH] lib: Implement tracking of dynamic memory allocations + +**PLEASE NOTE** that distributors intending to backport (or cherry-pick) +this fix need to copy 99% of the related pull request, not just this +commit, to not end up with a state that literally does both too much and +too little at the same time. Appending ".diff" to the pull request URL +could be of help. +--- + lib/expat.h | 15 +- + lib/internal.h | 5 + + lib/libexpat.def.cmake | 3 + + lib/xmlparse.c | 337 +++++++++++++++++++- + tests/runtests.c | 32 +++++++++++++++++++ + xmlwf/xmlwf.c | 2 + + xmlwf/xmlwf_helpgen.py | 2 + + 7 files changed, 353 insertions(+), 11 deletions(-) + +diff --git a/lib/expat.h b/lib/expat.h +index 610e1ddc..66a253c1 100644 +--- a/lib/expat.h ++++ b/lib/expat.h +@@ -1032,7 +1032,10 @@ enum XML_FeatureEnum { + XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT, + XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT, + /* Added in Expat 2.6.0. */ +- XML_FEATURE_GE ++ XML_FEATURE_GE, ++ /* Added in Expat 2.7.2. */ ++ XML_FEATURE_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT, ++ XML_FEATURE_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT, + /* Additional features must be added to the end of this enum. */ + }; + +@@ -1057,6 +1060,16 @@ XML_SetBillionLaughsAttackProtectionMaximumAmplification( + XMLPARSEAPI(XML_Bool) + XML_SetBillionLaughsAttackProtectionActivationThreshold( + XML_Parser parser, unsigned long long activationThresholdBytes); ++ ++/* Added in Expat 2.7.2. */ ++XMLPARSEAPI(XML_Bool) ++XML_SetAllocTrackerMaximumAmplification(XML_Parser parser, ++ float maximumAmplificationFactor); ++ ++/* Added in Expat 2.7.2. */ ++XMLPARSEAPI(XML_Bool) ++XML_SetAllocTrackerActivationThreshold( ++ XML_Parser parser, unsigned long long activationThresholdBytes); + #endif + + /* Added in Expat 2.6.0. */ +diff --git a/lib/internal.h b/lib/internal.h +index 6bde6ae6..eb67cf50 100644 +--- a/lib/internal.h ++++ b/lib/internal.h +@@ -148,6 +148,11 @@ + 100.0f + #define EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT \ + 8388608 // 8 MiB, 2^23 ++ ++#define EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT 100.0f ++#define EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT \ ++ 67108864 // 64 MiB, 2^26 ++ + /* NOTE END */ + + #include "expat.h" // so we can use type XML_Parser below +diff --git a/lib/libexpat.def.cmake b/lib/libexpat.def.cmake +index 10ee9cd6..7a3a7ec0 100644 +--- a/lib/libexpat.def.cmake ++++ b/lib/libexpat.def.cmake +@@ -79,3 +79,6 @@ EXPORTS + ; added with version 2.4.0 + @_EXPAT_COMMENT_DTD@ XML_SetBillionLaughsAttackProtectionActivationThreshold @69 + @_EXPAT_COMMENT_DTD@ XML_SetBillionLaughsAttackProtectionMaximumAmplification @70 ++; added with version 2.7.2 ++@_EXPAT_COMMENT_DTD_OR_GE@ XML_SetAllocTrackerMaximumAmplification @72 ++@_EXPAT_COMMENT_DTD_OR_GE@ XML_SetAllocTrackerActivationThreshold @73 +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index 9f0a8b3e..fcf1cfdd 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -452,6 +452,14 @@ typedef struct accounting { + unsigned long long activationThresholdBytes; + } ACCOUNTING; + ++typedef struct MALLOC_TRACKER { ++ XmlBigCount bytesAllocated; ++ XmlBigCount peakBytesAllocated; // updated live only for debug level >=2 ++ unsigned long debugLevel; ++ float maximumAmplificationFactor; // >=1.0 ++ XmlBigCount activationThresholdBytes; ++} MALLOC_TRACKER; ++ + typedef struct entity_stats { + unsigned int countEverOpened; + unsigned int currentDepth; +@@ -599,7 +607,8 @@ static XML_Bool startParsing(XML_Parser parser); + + static XML_Parser parserCreate(const XML_Char *encodingName, + const XML_Memory_Handling_Suite *memsuite, +- const XML_Char *nameSep, DTD *dtd); ++ const XML_Char *nameSep, DTD *dtd, ++ XML_Parser parentParser); + + static void parserInit(XML_Parser parser, const XML_Char *encodingName); + +@@ -769,14 +778,220 @@ struct XML_ParserStruct { + unsigned long m_hash_secret_salt; + #if XML_GE == 1 + ACCOUNTING m_accounting; ++ MALLOC_TRACKER m_alloc_tracker; + ENTITY_STATS m_entity_stats; + #endif + XML_Bool m_reenter; + }; + +-#define MALLOC(parser, s) (parser->m_mem.malloc_fcn((s))) +-#define REALLOC(parser, p, s) (parser->m_mem.realloc_fcn((p), (s))) +-#define FREE(parser, p) (parser->m_mem.free_fcn((p))) ++#if XML_GE == 1 ++# define MALLOC(parser, s) (expat_malloc((parser), (s), __LINE__)) ++# define REALLOC(parser, p, s) (expat_realloc((parser), (p), (s), __LINE__)) ++# define FREE(parser, p) (expat_free((parser), (p), __LINE__)) ++#else ++# define MALLOC(parser, s) (parser->m_mem.malloc_fcn((s))) ++# define REALLOC(parser, p, s) (parser->m_mem.realloc_fcn((p), (s))) ++# define FREE(parser, p) (parser->m_mem.free_fcn((p))) ++#endif ++ ++#if XML_GE == 1 ++static void ++expat_heap_stat(XML_Parser rootParser, char operator, XmlBigCount absDiff, ++ XmlBigCount newTotal, XmlBigCount peakTotal, int sourceLine) { ++ // NOTE: This can be +infinity or -nan ++ const float amplification ++ = (float)newTotal / (float)rootParser->m_accounting.countBytesDirect; ++ fprintf( ++ stderr, ++ "expat: Allocations(%p): Direct " EXPAT_FMT_ULL("10") ", allocated %c" EXPAT_FMT_ULL( ++ "10") " to " EXPAT_FMT_ULL("10") " (" EXPAT_FMT_ULL("10") " peak), amplification %8.2f (xmlparse.c:%d)\n", ++ (void *)rootParser, rootParser->m_accounting.countBytesDirect, operator, ++ absDiff, newTotal, peakTotal, (double)amplification, sourceLine); ++} ++ ++static bool ++expat_heap_increase_tolerable(XML_Parser rootParser, XmlBigCount increase, ++ int sourceLine) { ++ assert(rootParser != NULL); ++ assert(increase > 0); ++ ++ XmlBigCount newTotal = 0; ++ bool tolerable = true; ++ ++ // Detect integer overflow ++ if ((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated < increase) { ++ tolerable = false; ++ } else { ++ newTotal = rootParser->m_alloc_tracker.bytesAllocated + increase; ++ ++ if (newTotal >= rootParser->m_alloc_tracker.activationThresholdBytes) { ++ assert(newTotal > 0); ++ // NOTE: This can be +infinity when dividing by zero but not -nan ++ const float amplification ++ = (float)newTotal / (float)rootParser->m_accounting.countBytesDirect; ++ if (amplification ++ > rootParser->m_alloc_tracker.maximumAmplificationFactor) { ++ tolerable = false; ++ } ++ } ++ } ++ ++ if (! tolerable && (rootParser->m_alloc_tracker.debugLevel >= 1)) { ++ expat_heap_stat(rootParser, '+', increase, newTotal, newTotal, sourceLine); ++ } ++ ++ return tolerable; ++} ++ ++static void * ++expat_malloc(XML_Parser parser, size_t size, int sourceLine) { ++ // Detect integer overflow ++ if (SIZE_MAX - size < sizeof(size_t)) { ++ return NULL; ++ } ++ ++ const XML_Parser rootParser = getRootParserOf(parser, NULL); ++ assert(rootParser->m_parentParser == NULL); ++ ++ const size_t bytesToAllocate = sizeof(size_t) + size; ++ ++ if ((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated ++ < bytesToAllocate) { ++ return NULL; // i.e. signal integer overflow as out-of-memory ++ } ++ ++ if (! expat_heap_increase_tolerable(rootParser, bytesToAllocate, ++ sourceLine)) { ++ return NULL; // i.e. signal violation as out-of-memory ++ } ++ ++ // Actually allocate ++ void *const mallocedPtr = parser->m_mem.malloc_fcn(bytesToAllocate); ++ ++ if (mallocedPtr == NULL) { ++ return NULL; ++ } ++ ++ // Update in-block recorded size ++ *(size_t *)mallocedPtr = size; ++ ++ // Update accounting ++ rootParser->m_alloc_tracker.bytesAllocated += bytesToAllocate; ++ ++ // Report as needed ++ if (rootParser->m_alloc_tracker.debugLevel >= 2) { ++ if (rootParser->m_alloc_tracker.bytesAllocated ++ > rootParser->m_alloc_tracker.peakBytesAllocated) { ++ rootParser->m_alloc_tracker.peakBytesAllocated ++ = rootParser->m_alloc_tracker.bytesAllocated; ++ } ++ expat_heap_stat(rootParser, '+', bytesToAllocate, ++ rootParser->m_alloc_tracker.bytesAllocated, ++ rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine); ++ } ++ ++ return (char *)mallocedPtr + sizeof(size_t); ++} ++ ++static void ++expat_free(XML_Parser parser, void *ptr, int sourceLine) { ++ assert(parser != NULL); ++ ++ if (ptr == NULL) { ++ return; ++ } ++ ++ const XML_Parser rootParser = getRootParserOf(parser, NULL); ++ assert(rootParser->m_parentParser == NULL); ++ ++ // Extract size (to the eyes of malloc_fcn/realloc_fcn) and ++ // the original pointer returned by malloc/realloc ++ void *const mallocedPtr = (char *)ptr - sizeof(size_t); ++ const size_t bytesAllocated = sizeof(size_t) + *(size_t *)mallocedPtr; ++ ++ // Update accounting ++ assert(rootParser->m_alloc_tracker.bytesAllocated >= bytesAllocated); ++ rootParser->m_alloc_tracker.bytesAllocated -= bytesAllocated; ++ ++ // Report as needed ++ if (rootParser->m_alloc_tracker.debugLevel >= 2) { ++ expat_heap_stat(rootParser, '-', bytesAllocated, ++ rootParser->m_alloc_tracker.bytesAllocated, ++ rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine); ++ } ++ ++ // NOTE: This may be freeing rootParser, so freeing has to come last ++ parser->m_mem.free_fcn(mallocedPtr); ++} ++ ++static void * ++expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine) { ++ assert(parser != NULL); ++ ++ if (ptr == NULL) { ++ return expat_malloc(parser, size, sourceLine); ++ } ++ ++ if (size == 0) { ++ expat_free(parser, ptr, sourceLine); ++ return NULL; ++ } ++ ++ const XML_Parser rootParser = getRootParserOf(parser, NULL); ++ assert(rootParser->m_parentParser == NULL); ++ ++ // Extract original size (to the eyes of the caller) and the original ++ // pointer returned by malloc/realloc ++ void *mallocedPtr = (char *)ptr - sizeof(size_t); ++ const size_t prevSize = *(size_t *)mallocedPtr; ++ ++ // Classify upcoming change ++ const bool isIncrease = (size > prevSize); ++ const size_t absDiff ++ = (size > prevSize) ? (size - prevSize) : (prevSize - size); ++ ++ // Ask for permission from accounting ++ if (isIncrease) { ++ if (! expat_heap_increase_tolerable(rootParser, absDiff, sourceLine)) { ++ return NULL; // i.e. signal violation as out-of-memory ++ } ++ } ++ ++ // Actually allocate ++ mallocedPtr = parser->m_mem.realloc_fcn(mallocedPtr, sizeof(size_t) + size); ++ ++ if (mallocedPtr == NULL) { ++ return NULL; ++ } ++ ++ // Update accounting ++ if (isIncrease) { ++ assert((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated ++ >= absDiff); ++ rootParser->m_alloc_tracker.bytesAllocated += absDiff; ++ } else { // i.e. decrease ++ assert(rootParser->m_alloc_tracker.bytesAllocated >= absDiff); ++ rootParser->m_alloc_tracker.bytesAllocated -= absDiff; ++ } ++ ++ // Report as needed ++ if (rootParser->m_alloc_tracker.debugLevel >= 2) { ++ if (rootParser->m_alloc_tracker.bytesAllocated ++ > rootParser->m_alloc_tracker.peakBytesAllocated) { ++ rootParser->m_alloc_tracker.peakBytesAllocated ++ = rootParser->m_alloc_tracker.bytesAllocated; ++ } ++ expat_heap_stat(rootParser, isIncrease ? '+' : '-', absDiff, ++ rootParser->m_alloc_tracker.bytesAllocated, ++ rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine); ++ } ++ ++ // Update in-block recorded size ++ *(size_t *)mallocedPtr = size; ++ ++ return (char *)mallocedPtr + sizeof(size_t); ++} ++#endif // XML_GE == 1 + + XML_Parser XMLCALL + XML_ParserCreate(const XML_Char *encodingName) { +@@ -1100,19 +1315,40 @@ XML_Parser XMLCALL + XML_ParserCreate_MM(const XML_Char *encodingName, + const XML_Memory_Handling_Suite *memsuite, + const XML_Char *nameSep) { +- return parserCreate(encodingName, memsuite, nameSep, NULL); ++ return parserCreate(encodingName, memsuite, nameSep, NULL, NULL); + } + + static XML_Parser + parserCreate(const XML_Char *encodingName, + const XML_Memory_Handling_Suite *memsuite, const XML_Char *nameSep, +- DTD *dtd) { +- XML_Parser parser; ++ DTD *dtd, XML_Parser parentParser) { ++ XML_Parser parser = NULL; ++ ++#if XML_GE == 1 ++ const size_t increase = sizeof(size_t) + sizeof(struct XML_ParserStruct); ++ ++ if (parentParser != NULL) { ++ const XML_Parser rootParser = getRootParserOf(parentParser, NULL); ++ if (! expat_heap_increase_tolerable(rootParser, increase, __LINE__)) { ++ return NULL; ++ } ++ } ++#else ++ UNUSED_P(parentParser); ++#endif + + if (memsuite) { + XML_Memory_Handling_Suite *mtemp; ++#if XML_GE == 1 ++ void *const sizeAndParser = memsuite->malloc_fcn( ++ sizeof(size_t) + sizeof(struct XML_ParserStruct)); ++ if (sizeAndParser != NULL) { ++ *(size_t *)sizeAndParser = sizeof(struct XML_ParserStruct); ++ parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t)); ++#else + parser = memsuite->malloc_fcn(sizeof(struct XML_ParserStruct)); + if (parser != NULL) { ++#endif + mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem); + mtemp->malloc_fcn = memsuite->malloc_fcn; + mtemp->realloc_fcn = memsuite->realloc_fcn; +@@ -1120,18 +1356,67 @@ parserCreate(const XML_Char *encodingName, + } + } else { + XML_Memory_Handling_Suite *mtemp; ++#if XML_GE == 1 ++ void *const sizeAndParser ++ = (XML_Parser)malloc(sizeof(size_t) + sizeof(struct XML_ParserStruct)); ++ if (sizeAndParser != NULL) { ++ *(size_t *)sizeAndParser = sizeof(struct XML_ParserStruct); ++ parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t)); ++#else + parser = (XML_Parser)malloc(sizeof(struct XML_ParserStruct)); + if (parser != NULL) { ++#endif + mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem); + mtemp->malloc_fcn = malloc; + mtemp->realloc_fcn = realloc; + mtemp->free_fcn = free; + } +- } ++ } // cppcheck-suppress[memleak symbolName=sizeAndParser] // Cppcheck >=2.18.0 + + if (! parser) + return parser; + ++#if XML_GE == 1 ++ // Initialize .m_alloc_tracker ++ memset(&parser->m_alloc_tracker, 0, sizeof(MALLOC_TRACKER)); ++ if (parentParser == NULL) { ++ parser->m_alloc_tracker.debugLevel ++ = getDebugLevel("EXPAT_MALLOC_DEBUG", 0u); ++ parser->m_alloc_tracker.maximumAmplificationFactor ++ = EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT; ++ parser->m_alloc_tracker.activationThresholdBytes ++ = EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT; ++ ++ // NOTE: This initialization needs to come this early because these fields ++ // are read by allocation tracking code ++ parser->m_parentParser = NULL; ++ parser->m_accounting.countBytesDirect = 0; ++ } else { ++ parser->m_parentParser = parentParser; ++ } ++ ++ // Record XML_ParserStruct allocation we did a few lines up before ++ const XML_Parser rootParser = getRootParserOf(parser, NULL); ++ assert(rootParser->m_parentParser == NULL); ++ assert(SIZE_MAX - rootParser->m_alloc_tracker.bytesAllocated >= increase); ++ rootParser->m_alloc_tracker.bytesAllocated += increase; ++ ++ // Report on allocation ++ if (rootParser->m_alloc_tracker.debugLevel >= 2) { ++ if (rootParser->m_alloc_tracker.bytesAllocated ++ > rootParser->m_alloc_tracker.peakBytesAllocated) { ++ rootParser->m_alloc_tracker.peakBytesAllocated ++ = rootParser->m_alloc_tracker.bytesAllocated; ++ } ++ ++ expat_heap_stat(rootParser, '+', increase, ++ rootParser->m_alloc_tracker.bytesAllocated, ++ rootParser->m_alloc_tracker.peakBytesAllocated, __LINE__); ++ } ++#else ++ parser->m_parentParser = NULL; ++#endif // XML_GE == 1 ++ + parser->m_buffer = NULL; + parser->m_bufferLim = NULL; + +@@ -1295,7 +1580,6 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) { + parser->m_unknownEncodingMem = NULL; + parser->m_unknownEncodingRelease = NULL; + parser->m_unknownEncodingData = NULL; +- parser->m_parentParser = NULL; + parser->m_parsingStatus.parsing = XML_INITIALIZED; + // Reentry can only be triggered inside m_processor calls + parser->m_reenter = XML_FALSE; +@@ -1530,9 +1814,10 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context, + */ + if (parser->m_ns) { + XML_Char tmp[2] = {parser->m_namespaceSeparator, 0}; +- parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd); ++ parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd, oldParser); + } else { +- parser = parserCreate(encodingName, &parser->m_mem, NULL, newDtd); ++ parser ++ = parserCreate(encodingName, &parser->m_mem, NULL, newDtd, oldParser); + } + + if (! parser) +@@ -2714,6 +2999,13 @@ XML_GetFeatureList(void) { + EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT}, + /* Added in Expat 2.6.0. */ + {XML_FEATURE_GE, XML_L("XML_GE"), 0}, ++ /* Added in Expat 2.7.2. */ ++ {XML_FEATURE_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT, ++ XML_L("XML_AT_MAX_AMP"), ++ (long int)EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT}, ++ {XML_FEATURE_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT, ++ XML_L("XML_AT_ACT_THRES"), ++ (long int)EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT}, + #endif + {XML_FEATURE_END, NULL, 0}}; + +@@ -2742,6 +3034,29 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold( + parser->m_accounting.activationThresholdBytes = activationThresholdBytes; + return XML_TRUE; + } ++ ++XML_Bool XMLCALL ++XML_SetAllocTrackerMaximumAmplification(XML_Parser parser, ++ float maximumAmplificationFactor) { ++ if ((parser == NULL) || (parser->m_parentParser != NULL) ++ || isnan(maximumAmplificationFactor) ++ || (maximumAmplificationFactor < 1.0f)) { ++ return XML_FALSE; ++ } ++ parser->m_alloc_tracker.maximumAmplificationFactor ++ = maximumAmplificationFactor; ++ return XML_TRUE; ++} ++ ++XML_Bool XMLCALL ++XML_SetAllocTrackerActivationThreshold( ++ XML_Parser parser, unsigned long long activationThresholdBytes) { ++ if ((parser == NULL) || (parser->m_parentParser != NULL)) { ++ return XML_FALSE; ++ } ++ parser->m_alloc_tracker.activationThresholdBytes = activationThresholdBytes; ++ return XML_TRUE; ++} + #endif /* XML_GE == 1 */ + + XML_Bool XMLCALL +diff --git a/tests/runtests.c b/tests/runtests.c +index 3683394..6e3fa18 100644 +--- a/tests/runtests.c ++++ b/tests/runtests.c +@@ -39,7 +39,7 @@ + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +- ++#include + #include + + #if defined(NDEBUG) +@@ -10883,6 +10883,32 @@ START_TEST(test_nsalloc_long_attr_prefix) { + } + END_TEST + ++static char const *_check_current_filename = NULL; ++static int _check_current_lineno = -1; ++static jmp_buf env; ++void ++_fail(const char *file, int line, const char *msg) { ++ /* Always print the error message so it isn't lost. In this case, ++ we have a failure, so there's no reason to be quiet about what ++ it is. ++ */ ++ _check_current_filename = file; ++ _check_current_lineno = line; ++ if (msg != NULL) { ++ const int has_newline = (msg[strlen(msg) - 1] == '\n'); ++ fprintf(stderr, "ERROR: %s%s", msg, has_newline ? "" : "\n"); ++ } ++ longjmp(env, 1); ++} ++ ++# define fail(msg) _fail(__FILE__, __LINE__, msg) ++# define assert_true(cond) \ ++ do { \ ++ if (! (cond)) { \ ++ _fail(__FILE__, __LINE__, "check failed: " #cond); \ ++ } \ ++ } while (0) ++ + /* Test attribute handling in the face of a dodgy reallocator */ + START_TEST(test_nsalloc_realloc_attributes) { + const char *text = " +Date: Sun, 7 Sep 2025 12:18:08 +0200 +Subject: [PATCH] lib: Make XML_MemFree and XML_FreeContentModel match their + siblings + +.. XML_MemMalloc and XML_MemRealloc in structure, prior to upcoming changes +--- + lib/xmlparse.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index fcf1cfdd..5d27cd45 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -2778,8 +2778,9 @@ XML_GetCurrentColumnNumber(XML_Parser parser) { + + void XMLCALL + XML_FreeContentModel(XML_Parser parser, XML_Content *model) { +- if (parser != NULL) +- FREE(parser, model); ++ if (parser == NULL) ++ return; ++ FREE(parser, model); + } + + void *XMLCALL +@@ -2798,8 +2799,9 @@ XML_MemRealloc(XML_Parser parser, void *ptr, size_t size) { + + void XMLCALL + XML_MemFree(XML_Parser parser, void *ptr) { +- if (parser != NULL) +- FREE(parser, ptr); ++ if (parser == NULL) ++ return; ++ FREE(parser, ptr); + } + + void XMLCALL +-- +2.33.0 + diff --git a/backport-0010-CVE-2025-59375-lib-Exclude-XML_Mem-functions-from-allocation-tracki.patch b/backport-0010-CVE-2025-59375-lib-Exclude-XML_Mem-functions-from-allocation-tracki.patch new file mode 100644 index 0000000..38d5aca --- /dev/null +++ b/backport-0010-CVE-2025-59375-lib-Exclude-XML_Mem-functions-from-allocation-tracki.patch @@ -0,0 +1,53 @@ +From 96c7467281c72028aada525c1d3822512758b266 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping +Date: Sun, 7 Sep 2025 12:06:43 +0200 +Subject: [PATCH] lib: Exclude XML_Mem* functions from allocation tracking + +.. so that allocations by the user application +are not being limited. +--- + lib/xmlparse.c | 16 +++++++++++++--- + 1 file changed, 13 insertions(+), 3 deletions(-) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index 5d27cd45..8145a049 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -2787,21 +2787,31 @@ void *XMLCALL + XML_MemMalloc(XML_Parser parser, size_t size) { + if (parser == NULL) + return NULL; +- return MALLOC(parser, size); ++ ++ // NOTE: We are avoiding MALLOC(..) here to not include ++ // user allocations with allocation tracking and limiting. ++ return parser->m_mem.malloc_fcn(size); + } + + void *XMLCALL + XML_MemRealloc(XML_Parser parser, void *ptr, size_t size) { + if (parser == NULL) + return NULL; +- return REALLOC(parser, ptr, size); ++ ++ // NOTE: We are avoiding REALLOC(..) here to not include ++ // user allocations with allocation tracking and limiting. ++ return parser->m_mem.realloc_fcn(ptr, size); + } + + void XMLCALL + XML_MemFree(XML_Parser parser, void *ptr) { + if (parser == NULL) + return; +- FREE(parser, ptr); ++ ++ // NOTE: We are avoiding FREE(..) here because XML_MemMalloc and ++ // XML_MemRealloc are not using MALLOC(..) and REALLOC(..) ++ // but plain .malloc_fcn(..) and .realloc_fcn(..), internally. ++ parser->m_mem.free_fcn(ptr); + } + + void XMLCALL +-- +2.33.0 + diff --git a/backport-0011-CVE-2025-59375-lib-Exclude-the-main-input-buffer-from-allocation-tr.patch b/backport-0011-CVE-2025-59375-lib-Exclude-the-main-input-buffer-from-allocation-tr.patch new file mode 100644 index 0000000..c311684 --- /dev/null +++ b/backport-0011-CVE-2025-59375-lib-Exclude-the-main-input-buffer-from-allocation-tr.patch @@ -0,0 +1,65 @@ +From ae4086198d710a62a0a1560007b81307dba72909 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping +Date: Tue, 9 Sep 2025 21:34:28 +0200 +Subject: [PATCH] lib: Exclude the main input buffer from allocation tracking + +.. so that control of the input buffer size remains with the +application using Expat +--- + lib/xmlparse.c | 19 +++++++++++++++---- + 1 file changed, 15 insertions(+), 4 deletions(-) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index 8145a049..00139b94 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -1979,7 +1979,10 @@ XML_ParserFree(XML_Parser parser) { + FREE(parser, (void *)parser->m_attInfo); + #endif + FREE(parser, parser->m_groupConnector); +- FREE(parser, parser->m_buffer); ++ // NOTE: We are avoiding FREE(..) here because parser->m_buffer ++ // is not being allocated with MALLOC(..) but with plain ++ // .malloc_fcn(..). ++ parser->m_mem.free_fcn(parser->m_buffer); + FREE(parser, parser->m_dataBuf); + FREE(parser, parser->m_nsAtts); + FREE(parser, parser->m_unknownEncodingMem); +@@ -2573,7 +2576,9 @@ XML_GetBuffer(XML_Parser parser, int len) { + parser->m_errorCode = XML_ERROR_NO_MEMORY; + return NULL; + } +- newBuf = (char *)MALLOC(parser, bufferSize); ++ // NOTE: We are avoiding MALLOC(..) here to leave limiting ++ // the input size to the application using Expat. ++ newBuf = (char *)parser->m_mem.malloc_fcn(bufferSize); + if (newBuf == 0) { + parser->m_errorCode = XML_ERROR_NO_MEMORY; + return NULL; +@@ -2584,7 +2589,10 @@ XML_GetBuffer(XML_Parser parser, int len) { + memcpy(newBuf, &parser->m_bufferPtr[-keep], + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr) + + keep); +- FREE(parser, parser->m_buffer); ++ // NOTE: We are avoiding FREE(..) here because parser->m_buffer ++ // is not being allocated with MALLOC(..) but with plain ++ // .malloc_fcn(..). ++ parser->m_mem.free_fcn(parser->m_buffer); + parser->m_buffer = newBuf; + parser->m_bufferEnd + = parser->m_buffer +@@ -2600,7 +2608,10 @@ XML_GetBuffer(XML_Parser parser, int len) { + if (parser->m_bufferPtr) { + memcpy(newBuf, parser->m_bufferPtr, + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr)); +- FREE(parser, parser->m_buffer); ++ // NOTE: We are avoiding FREE(..) here because parser->m_buffer ++ // is not being allocated with MALLOC(..) but with plain ++ // .malloc_fcn(..). ++ parser->m_mem.free_fcn(parser->m_buffer); + parser->m_bufferEnd + = newBuf + + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr); +-- +2.33.0 + diff --git a/backport-0012-CVE-2025-59375-lib-Exclude-the-content-model-from-allocation-tracki.patch b/backport-0012-CVE-2025-59375-lib-Exclude-the-content-model-from-allocation-tracki.patch new file mode 100644 index 0000000..c8031d5 --- /dev/null +++ b/backport-0012-CVE-2025-59375-lib-Exclude-the-content-model-from-allocation-tracki.patch @@ -0,0 +1,57 @@ +From 7e35240dc97e9fd4f609e31f27c27b659535e436 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping +Date: Thu, 11 Sep 2025 00:27:05 +0200 +Subject: [PATCH] lib: Exclude the content model from allocation tracking + +.. so that applications that are not using XML_FreeContentModel +but plain free(..) or .free_fcn() to free the content model's +memory are safe +--- + lib/xmlparse.c | 16 +++++++++++++--- + 1 file changed, 13 insertions(+), 3 deletions(-) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index 00139b94..d0b6e0cd 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -2791,7 +2791,10 @@ void XMLCALL + XML_FreeContentModel(XML_Parser parser, XML_Content *model) { + if (parser == NULL) + return; +- FREE(parser, model); ++ ++ // NOTE: We are avoiding FREE(..) here because the content model ++ // has been created using plain .malloc_fcn(..) rather than MALLOC(..). ++ parser->m_mem.free_fcn(model); + } + + void *XMLCALL +@@ -6069,8 +6072,12 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, + case XML_ROLE_CONTENT_EMPTY: + if (dtd->in_eldecl) { + if (parser->m_elementDeclHandler) { ++ // NOTE: We are avoiding MALLOC(..) here to so that ++ // applications that are not using XML_FreeContentModel but ++ // plain free(..) or .free_fcn() to free the content model's ++ // memory are safe. + XML_Content *content +- = (XML_Content *)MALLOC(parser, sizeof(XML_Content)); ++ = (XML_Content *)parser->m_mem.malloc_fcn(sizeof(XML_Content)); + if (! content) + return XML_ERROR_NO_MEMORY; + content->quant = XML_CQUANT_NONE; +@@ -8285,7 +8292,10 @@ build_model(XML_Parser parser) { + const size_t allocsize = (dtd->scaffCount * sizeof(XML_Content) + + (dtd->contentStringLen * sizeof(XML_Char))); + +- ret = (XML_Content *)MALLOC(parser, allocsize); ++ // NOTE: We are avoiding MALLOC(..) here to so that ++ // applications that are not using XML_FreeContentModel but plain ++ // free(..) or .free_fcn() to free the content model's memory are safe. ++ ret = (XML_Content *)parser->m_mem.malloc_fcn(allocsize); + if (! ret) + return NULL; + +-- +2.33.0 + diff --git a/backport-0013-CVE-2025-59375-xmlwf-Wire-allocation-tracker-config-to-existing-arg.patch b/backport-0013-CVE-2025-59375-xmlwf-Wire-allocation-tracker-config-to-existing-arg.patch new file mode 100644 index 0000000..f117694 --- /dev/null +++ b/backport-0013-CVE-2025-59375-xmlwf-Wire-allocation-tracker-config-to-existing-arg.patch @@ -0,0 +1,68 @@ +From 78366891a586f293aeff60a14a55e4afe1169586 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping +Date: Tue, 2 Sep 2025 16:44:00 +0200 +Subject: [PATCH] xmlwf: Wire allocation tracker config to existing arguments + -a and -b + +--- + xmlwf/xmlwf.c | 7 +++++-- + xmlwf/xmlwf_helpgen.py | 4 ++-- + 2 files changed, 7 insertions(+), 4 deletions(-) + +diff --git a/xmlwf/xmlwf.c b/xmlwf/xmlwf.c +index b9d0a7fc..14206d9e 100644 +--- a/xmlwf/xmlwf.c ++++ b/xmlwf/xmlwf.c +@@ -913,11 +913,11 @@ usage(const XML_Char *prog, int rc) { + T(" -t write no XML output for [t]iming of plain parsing\n") + T(" -N enable adding doctype and [n]otation declarations\n") + T("\n") +- T("billion laughs attack protection:\n") ++ T("amplification attack protection (e.g. billion laughs):\n") + T(" NOTE: If you ever need to increase these values for non-attack payload, please file a bug report.\n") + T("\n") + T(" -a FACTOR set maximum tolerated [a]mplification factor (default: 100.0)\n") +- T(" -b BYTES set number of output [b]ytes needed to activate (default: 8 MiB)\n") ++ T(" -b BYTES set number of output [b]ytes needed to activate (default: 8 MiB/64 MiB)\n") + T("\n") + T("info arguments:\n") + T(" -h show this [h]elp message and exit\n") +@@ -1181,12 +1181,15 @@ tmain(int argc, XML_Char **argv) { + #if XML_GE == 1 + XML_SetBillionLaughsAttackProtectionMaximumAmplification( + parser, attackMaximumAmplification); ++ XML_SetAllocTrackerMaximumAmplification(parser, ++ attackMaximumAmplification); + #endif + } + if (attackThresholdGiven) { + #if XML_GE == 1 + XML_SetBillionLaughsAttackProtectionActivationThreshold( + parser, attackThresholdBytes); ++ XML_SetAllocTrackerActivationThreshold(parser, attackThresholdBytes); + #else + (void)attackThresholdBytes; // silence -Wunused-but-set-variable + #endif +diff --git a/xmlwf/xmlwf_helpgen.py b/xmlwf/xmlwf_helpgen.py +index 2360820d..e91c285c 100755 +--- a/xmlwf/xmlwf_helpgen.py ++++ b/xmlwf/xmlwf_helpgen.py +@@ -84,13 +84,13 @@ output_mode.add_argument('-m', action='store_true', help='write [m]eta XML, not + output_mode.add_argument('-t', action='store_true', help='write no XML output for [t]iming of plain parsing') + output_related.add_argument('-N', action='store_true', help='enable adding doctype and [n]otation declarations') + +-billion_laughs = parser.add_argument_group('billion laughs attack protection', ++billion_laughs = parser.add_argument_group('amplification attack protection (e.g. billion laughs)', + description='NOTE: ' + 'If you ever need to increase these values ' + 'for non-attack payload, please file a bug report.') + billion_laughs.add_argument('-a', metavar='FACTOR', + help='set maximum tolerated [a]mplification factor (default: 100.0)') +-billion_laughs.add_argument('-b', metavar='BYTES', help='set number of output [b]ytes needed to activate (default: 8 MiB)') ++billion_laughs.add_argument('-b', metavar='BYTES', help='set number of output [b]ytes needed to activate (default: 8 MiB/64 MiB)') + + parser.add_argument('files', metavar='FILE', nargs='*', help='file to process (default: STDIN)') + +-- +2.33.0 + diff --git a/backport-pre-0008-CVE-2025-59375-Simplify-defined-XML_DTD-XML_GE-1-to-XML_GE-1.patch b/backport-pre-0008-CVE-2025-59375-Simplify-defined-XML_DTD-XML_GE-1-to-XML_GE-1.patch new file mode 100644 index 0000000..dcb2b4d --- /dev/null +++ b/backport-pre-0008-CVE-2025-59375-Simplify-defined-XML_DTD-XML_GE-1-to-XML_GE-1.patch @@ -0,0 +1,313 @@ +From caa27198637683b15d810737bb8a6a81af19bfa5 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping +Date: Fri, 27 Oct 2023 18:47:37 +0200 +Subject: [PATCH] Simplify "defined(XML_DTD) || XML_GE == 1" to "XML_GE == 1" + +--- + lib/expat.h | 2 +- + lib/internal.h | 2 +- + lib/xmlparse.c | 56 ++++++++++++++++++++--------------------- + xmlwf/xmlwf.c | 4 +-- + 4 files changed, 32 insertions(+), 32 deletions(-) + +diff --git a/lib/expat.h b/lib/expat.h +index 99113247..b794892d 100644 +--- a/lib/expat.h ++++ b/lib/expat.h +@@ -1038,7 +1038,7 @@ typedef struct { + XMLPARSEAPI(const XML_Feature *) + XML_GetFeatureList(void); + +-#if defined(XML_DTD) || XML_GE == 1 ++#if XML_GE == 1 + /* Added in Expat 2.4.0 for XML_DTD defined and + * added in Expat 2.6.0 for XML_GE == 1. */ + XMLPARSEAPI(XML_Bool) +diff --git a/lib/internal.h b/lib/internal.h +index 1851925c..03c8fdec 100644 +--- a/lib/internal.h ++++ b/lib/internal.h +@@ -154,7 +154,7 @@ extern "C" { + void _INTERNAL_trim_to_complete_utf8_characters(const char *from, + const char **fromLimRef); + +-#if defined(XML_DTD) || XML_GE == 1 ++#if XML_GE == 1 + unsigned long long testingAccountingGetCountBytesDirect(XML_Parser parser); + unsigned long long testingAccountingGetCountBytesIndirect(XML_Parser parser); + const char *unsignedCharToPrintable(unsigned char c); +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index 6a38dbe2..ea976017 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -424,7 +424,7 @@ enum XML_Account { + XML_ACCOUNT_NONE /* i.e. do not account, was accounted already */ + }; + +-#if defined(XML_DTD) || XML_GE == 1 ++#if XML_GE == 1 + typedef unsigned long long XmlBigCount; + typedef struct accounting { + XmlBigCount countBytesDirect; +@@ -440,7 +440,7 @@ typedef struct entity_stats { + unsigned int maximumDepthSeen; + int debugLevel; + } ENTITY_STATS; +-#endif /* defined(XML_DTD) || XML_GE == 1 */ ++#endif /* XML_GE == 1 */ + + typedef enum XML_Error PTRCALL Processor(XML_Parser parser, const char *start, + const char *end, const char **endPtr); +@@ -582,7 +582,7 @@ static XML_Parser parserCreate(const XML_Char *encodingName, + + static void parserInit(XML_Parser parser, const XML_Char *encodingName); + +-#if defined(XML_DTD) || XML_GE == 1 ++#if XML_GE == 1 + static float accountingGetCurrentAmplification(XML_Parser rootParser); + static void accountingReportStats(XML_Parser originParser, const char *epilog); + static void accountingOnAbort(XML_Parser originParser); +@@ -605,7 +605,7 @@ static void entityTrackingOnClose(XML_Parser parser, ENTITY *entity, + + static XML_Parser getRootParserOf(XML_Parser parser, + unsigned int *outLevelDiff); +-#endif /* defined(XML_DTD) || XML_GE == 1 */ ++#endif /* XML_GE == 1 */ + + static unsigned long getDebugLevel(const char *variableName, + unsigned long defaultDebugLevel); +@@ -730,7 +730,7 @@ struct XML_ParserStruct { + enum XML_ParamEntityParsing m_paramEntityParsing; + #endif + unsigned long m_hash_secret_salt; +-#if defined(XML_DTD) || XML_GE == 1 ++#if XML_GE == 1 + ACCOUNTING m_accounting; + ENTITY_STATS m_entity_stats; + #endif +@@ -1190,7 +1190,7 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) { + #endif + parser->m_hash_secret_salt = 0; + +-#if defined(XML_DTD) || XML_GE == 1 ++#if XML_GE == 1 + memset(&parser->m_accounting, 0, sizeof(ACCOUNTING)); + parser->m_accounting.debugLevel = getDebugLevel("EXPAT_ACCOUNTING_DEBUG", 0u); + parser->m_accounting.maximumAmplificationFactor +@@ -2546,7 +2546,7 @@ XML_GetFeatureList(void) { + #ifdef XML_ATTR_INFO + {XML_FEATURE_ATTR_INFO, XML_L("XML_ATTR_INFO"), 0}, + #endif +-#if defined(XML_DTD) || XML_GE == 1 ++#if XML_GE == 1 + /* Added in Expat 2.4.0 for XML_DTD defined and + * added in Expat 2.6.0 for XML_GE == 1. */ + {XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT, +@@ -2563,7 +2563,7 @@ XML_GetFeatureList(void) { + return features; + } + +-#if defined(XML_DTD) || XML_GE == 1 ++#if XML_GE == 1 + XML_Bool XMLCALL + XML_SetBillionLaughsAttackProtectionMaximumAmplification( + XML_Parser parser, float maximumAmplificationFactor) { +@@ -2585,7 +2585,7 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold( + parser->m_accounting.activationThresholdBytes = activationThresholdBytes; + return XML_TRUE; + } +-#endif /* defined(XML_DTD) || XML_GE == 1 */ ++#endif /* XML_GE == 1 */ + + XML_Bool XMLCALL + XML_SetReparseDeferralEnabled(XML_Parser parser, XML_Bool enabled) { +@@ -2671,13 +2671,13 @@ externalEntityInitProcessor2(XML_Parser parser, const char *start, + int tok = XmlContentTok(parser->m_encoding, start, end, &next); + switch (tok) { + case XML_TOK_BOM: +-#if defined(XML_DTD) || XML_GE == 1 ++#if XML_GE == 1 + if (! accountingDiffTolerated(parser, tok, start, next, __LINE__, + XML_ACCOUNT_DIRECT)) { + accountingOnAbort(parser); + return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; + } +-#endif /* defined(XML_DTD) || XML_GE == 1 */ ++#endif /* XML_GE == 1 */ + + /* If we are at the end of the buffer, this would cause the next stage, + i.e. externalEntityInitProcessor3, to pass control directly to +@@ -2791,7 +2791,7 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, + for (;;) { + const char *next = s; /* XmlContentTok doesn't always set the last arg */ + int tok = XmlContentTok(enc, s, end, &next); +-#if defined(XML_DTD) || XML_GE == 1 ++#if XML_GE == 1 + const char *accountAfter + = ((tok == XML_TOK_TRAILING_RSQB) || (tok == XML_TOK_TRAILING_CR)) + ? (haveMore ? s /* i.e. 0 bytes */ : end) +@@ -2857,14 +2857,14 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, + XML_Char ch = (XML_Char)XmlPredefinedEntityName( + enc, s + enc->minBytesPerChar, next - enc->minBytesPerChar); + if (ch) { +-#if defined(XML_DTD) || XML_GE == 1 ++#if XML_GE == 1 + /* NOTE: We are replacing 4-6 characters original input for 1 character + * so there is no amplification and hence recording without + * protection. */ + accountingDiffTolerated(parser, tok, (char *)&ch, + ((char *)&ch) + sizeof(XML_Char), __LINE__, + XML_ACCOUNT_ENTITY_EXPANSION); +-#endif /* defined(XML_DTD) || XML_GE == 1 */ ++#endif /* XML_GE == 1 */ + if (parser->m_characterDataHandler) + parser->m_characterDataHandler(parser->m_handlerArg, &ch, 1); + else if (parser->m_defaultHandler) +@@ -4066,7 +4066,7 @@ doCdataSection(XML_Parser parser, const ENCODING *enc, const char **startPtr, + for (;;) { + const char *next = s; /* in case of XML_TOK_NONE or XML_TOK_PARTIAL */ + int tok = XmlCdataSectionTok(enc, s, end, &next); +-#if defined(XML_DTD) || XML_GE == 1 ++#if XML_GE == 1 + if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, account)) { + accountingOnAbort(parser); + return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; +@@ -4218,7 +4218,7 @@ doIgnoreSection(XML_Parser parser, const ENCODING *enc, const char **startPtr, + *eventPP = s; + *startPtr = NULL; + tok = XmlIgnoreSectionTok(enc, s, end, &next); +-# if defined(XML_DTD) || XML_GE == 1 ++# if XML_GE == 1 + if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, + XML_ACCOUNT_DIRECT)) { + accountingOnAbort(parser); +@@ -4310,7 +4310,7 @@ processXmlDecl(XML_Parser parser, int isGeneralTextEntity, const char *s, + const XML_Char *storedversion = NULL; + int standalone = -1; + +-#if defined(XML_DTD) || XML_GE == 1 ++#if XML_GE == 1 + if (! accountingDiffTolerated(parser, XML_TOK_XML_DECL, s, next, __LINE__, + XML_ACCOUNT_DIRECT)) { + accountingOnAbort(parser); +@@ -4517,7 +4517,7 @@ entityValueInitProcessor(XML_Parser parser, const char *s, const char *end, + is not valid to have multiple BOMs. + */ + else if (tok == XML_TOK_BOM) { +-# if defined(XML_DTD) || XML_GE == 1 ++# if XML_GE == 1 + if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, + XML_ACCOUNT_DIRECT)) { + accountingOnAbort(parser); +@@ -4733,7 +4733,7 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, + } + } + role = XmlTokenRole(&parser->m_prologState, tok, s, next, enc); +-#if defined(XML_DTD) || XML_GE == 1 ++#if XML_GE == 1 + switch (role) { + case XML_ROLE_INSTANCE_START: // bytes accounted in contentProcessor + case XML_ROLE_XML_DECL: // bytes accounted in processXmlDecl +@@ -5057,7 +5057,7 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, + break; + case XML_ROLE_ENTITY_VALUE: + if (dtd->keepProcessing) { +-#if defined(XML_DTD) || XML_GE == 1 ++#if XML_GE == 1 + // This will store the given replacement text in + // parser->m_declEntity->textPtr. + enum XML_Error result = callStoreEntityValue( +@@ -5708,7 +5708,7 @@ epilogProcessor(XML_Parser parser, const char *s, const char *end, + for (;;) { + const char *next = NULL; + int tok = XmlPrologTok(parser->m_encoding, s, end, &next); +-#if defined(XML_DTD) || XML_GE == 1 ++#if XML_GE == 1 + if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, + XML_ACCOUNT_DIRECT)) { + accountingOnAbort(parser); +@@ -5873,7 +5873,7 @@ internalEntityProcessor(XML_Parser parser, const char *s, const char *end, + return result; + } + +-#if defined(XML_DTD) || XML_GE == 1 ++#if XML_GE == 1 + entityTrackingOnClose(parser, entity, __LINE__); + #endif + entity->open = XML_FALSE; +@@ -5952,7 +5952,7 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, + const char *next + = ptr; /* XmlAttributeValueTok doesn't always set the last arg */ + int tok = XmlAttributeValueTok(enc, ptr, end, &next); +-#if defined(XML_DTD) || XML_GE == 1 ++#if XML_GE == 1 + if (! accountingDiffTolerated(parser, tok, ptr, next, __LINE__, account)) { + accountingOnAbort(parser); + return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; +@@ -6017,14 +6017,14 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, + XML_Char ch = (XML_Char)XmlPredefinedEntityName( + enc, ptr + enc->minBytesPerChar, next - enc->minBytesPerChar); + if (ch) { +-#if defined(XML_DTD) || XML_GE == 1 ++#if XML_GE == 1 + /* NOTE: We are replacing 4-6 characters original input for 1 character + * so there is no amplification and hence recording without + * protection. */ + accountingDiffTolerated(parser, tok, (char *)&ch, + ((char *)&ch) + sizeof(XML_Char), __LINE__, + XML_ACCOUNT_ENTITY_EXPANSION); +-#endif /* defined(XML_DTD) || XML_GE == 1 */ ++#endif /* XML_GE == 1 */ + if (! poolAppendChar(pool, ch)) + return XML_ERROR_NO_MEMORY; + break; +@@ -6166,7 +6166,7 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc, + = entityTextPtr; /* XmlEntityValueTok doesn't always set the last arg */ + int tok = XmlEntityValueTok(enc, entityTextPtr, entityTextEnd, &next); + +-# if defined(XML_DTD) || XML_GE == 1 ++# if XML_GE == 1 + if (! accountingDiffTolerated(parser, tok, entityTextPtr, next, __LINE__, + account)) { + accountingOnAbort(parser); +@@ -7743,7 +7743,7 @@ copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) { + return result; + } + +-#if defined(XML_DTD) || XML_GE == 1 ++#if XML_GE == 1 + + static float + accountingGetCurrentAmplification(XML_Parser rootParser) { +@@ -8474,7 +8474,7 @@ unsignedCharToPrintable(unsigned char c) { + assert(0); /* never gets here */ + } + +-#endif /* defined(XML_DTD) || XML_GE == 1 */ ++#endif /* XML_GE == 1 */ + + static unsigned long + getDebugLevel(const char *variableName, unsigned long defaultDebugLevel) { +diff --git a/xmlwf/xmlwf.c b/xmlwf/xmlwf.c +index dfce51fa..2b1cf5ff 100644 +--- a/xmlwf/xmlwf.c ++++ b/xmlwf/xmlwf.c +@@ -1157,13 +1157,13 @@ tmain(int argc, XML_Char **argv) { + } + + if (attackMaximumAmplification != -1.0f) { +-#if defined(XML_DTD) || XML_GE == 1 ++#if XML_GE == 1 + XML_SetBillionLaughsAttackProtectionMaximumAmplification( + parser, attackMaximumAmplification); + #endif + } + if (attackThresholdGiven) { +-#if defined(XML_DTD) || XML_GE == 1 ++#if XML_GE == 1 + XML_SetBillionLaughsAttackProtectionActivationThreshold( + parser, attackThresholdBytes); + #else +-- +2.33.0 + diff --git a/backport-pre-0008-CVE-2025-59375-lib-Add-XML_GE-to-XML_GetFeatureList-and-XML_Feature.patch b/backport-pre-0008-CVE-2025-59375-lib-Add-XML_GE-to-XML_GetFeatureList-and-XML_Feature.patch new file mode 100644 index 0000000..cc34801 --- /dev/null +++ b/backport-pre-0008-CVE-2025-59375-lib-Add-XML_GE-to-XML_GetFeatureList-and-XML_Feature.patch @@ -0,0 +1,42 @@ +From 8a6c61de4a425977e357cafd8667a0d7771ce292 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping +Date: Thu, 26 Oct 2023 01:29:03 +0200 +Subject: [PATCH] lib: Add XML_GE to XML_GetFeatureList and XML_FeatureEnum + +Co-authored-by: Snild Dolkow +--- + lib/expat.h | 4 +++- + lib/xmlparse.c | 2 ++ + 2 files changed, 5 insertions(+), 1 deletion(-) + +diff --git a/lib/expat.h b/lib/expat.h +index b794892d..4ea92439 100644 +--- a/lib/expat.h ++++ b/lib/expat.h +@@ -1025,7 +1025,9 @@ enum XML_FeatureEnum { + XML_FEATURE_ATTR_INFO, + /* Added in Expat 2.4.0. */ + XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT, +- XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT ++ XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT, ++ /* Added in Expat 2.6.0. */ ++ XML_FEATURE_GE + /* Additional features must be added to the end of this enum. */ + }; + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index 3e03fa74..80b3a091 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -2556,6 +2556,8 @@ XML_GetFeatureList(void) { + {XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT, + XML_L("XML_BLAP_ACT_THRES"), + EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT}, ++ /* Added in Expat 2.6.0. */ ++ {XML_FEATURE_GE, XML_L("XML_GE"), 0}, + #endif + {XML_FEATURE_END, NULL, 0}}; + +-- +2.33.0 + diff --git a/expat.spec b/expat.spec index 6aba21e..6f7f894 100644 --- a/expat.spec +++ b/expat.spec @@ -1,7 +1,7 @@ %define Rversion %(echo %{version} | sed -e 's/\\./_/g' -e 's/^/R_/') Name: expat Version: 2.5.0 -Release: 9 +Release: 10 Summary: An XML parser library License: MIT URL: https://libexpat.github.io/ @@ -44,6 +44,21 @@ Patch34: backport-010-CVE-2024-8176.patch Patch35: backport-011-CVE-2024-8176.patch Patch36: backport-Stop-updating-m_eventPtr-on-exit-for-reentry.patch Patch37: backport-Make-parser-m_eventPtr-handling-clearer.patch +Patch38: backport-0001-CVE-2025-59375-lib-Make-function-dtdCreate-use-macro-MALLOC.patch +Patch39: backport-0002-CVE-2025-59375-lib-Make-string-pools-use-macros-MALLOC-FREE-REALLOC.patch +Patch40: backport-0003-CVE-2025-59375-lib-Make-function-hash-tables-use-macros-MALLOC-and-.patch +Patch41: backport-0004-CVE-2025-59375-lib-Make-function-copyString-use-macro-MALLOC.patch +Patch42: backport-0005-CVE-2025-59375-lib-Make-function-dtdReset-use-macro-FREE.patch +Patch43: backport-0006-CVE-2025-59375-lib-Make-function-dtdDestroy-use-macro-FREE.patch +Patch44: backport-0007-CVE-2025-59375-lib-Make-function-dtdCopy-use-macro-MALLOC.patch +Patch45: backport-pre-0008-CVE-2025-59375-lib-Add-XML_GE-to-XML_GetFeatureList-and-XML_Feature.patch +Patch46: backport-pre-0008-CVE-2025-59375-Simplify-defined-XML_DTD-XML_GE-1-to-XML_GE-1.patch +Patch47: backport-0008-CVE-2025-59375-lib-Implement-tracking-of-dynamic-memory-allocations.patch +Patch48: backport-0009-CVE-2025-59375-lib-Make-XML_MemFree-and-XML_FreeContentModel-match-.patch +Patch49: backport-0010-CVE-2025-59375-lib-Exclude-XML_Mem-functions-from-allocation-tracki.patch +Patch50: backport-0011-CVE-2025-59375-lib-Exclude-the-main-input-buffer-from-allocation-tr.patch +Patch51: backport-0012-CVE-2025-59375-lib-Exclude-the-content-model-from-allocation-tracki.patch +Patch52: backport-0013-CVE-2025-59375-xmlwf-Wire-allocation-tracker-config-to-existing-arg.patch BuildRequires: sed,autoconf,automake,gcc-c++,libtool,xmlto @@ -92,6 +107,9 @@ find %{buildroot} -type f -name changelog -delete %{_mandir}/man1/* %changelog +* Fri Oct 10 2025 fuanan - 2.5.0-10 +- fix CVE-2025-59375 + * Tue Apr 08 2025 zhuofeng <1107893276@qq.com> - 2.5.0-9 - backport upstream patches -- Gitee