diff --git a/backport-001-CVE-2023-52425.patch b/backport-001-CVE-2023-52425.patch
new file mode 100644
index 0000000000000000000000000000000000000000..148738538dd438af6953228683d330244ba60b82
--- /dev/null
+++ b/backport-001-CVE-2023-52425.patch
@@ -0,0 +1,223 @@
+From 6cc9677838ce4e68680f7877d71032ca6481ee56 Mon Sep 17 00:00:00 2001
+From: Snild Dolkow
+Date: Thu, 17 Aug 2023 16:25:26 +0200
+Subject: [PATCH] Skip parsing after repeated partials on the same token
+
+Reference: https://github.com/libexpat/libexpat/pull/789/commits/9cdf9b8d77d5c2c2a27d15fb68dd3f83cafb45a1
+Conflict: remove basic_test.c
+ change xmlparse.c
+
+MIME-Version: 1.0
+Content-Type: text/plain; charset=utf-8
+Content-Transfer-Encoding: 8bit
+
+When the parse buffer contains the starting bytes of a token but not
+all of them, we cannot parse the token to completion. We call this a
+partial token. When this happens, the parse position is reset to the
+start of the token, and the parse() call returns. The client is then
+expected to provide more data and call parse() again.
+
+In extreme cases, this means that the bytes of a token may be parsed
+many times: once for every buffer refill required before the full token
+is present in the buffer.
+
+Math:
+ Assume there's a token of T bytes
+ Assume the client fills the buffer in chunks of X bytes
+ We'll try to parse X, 2X, 3X, 4X ... until mX == T (technically >=)
+ That's (m²+m)X/2 = (T²/X+T)/2 bytes parsed (arithmetic progression)
+ While it is alleviated by larger refills, this amounts to O(T²)
+
+Expat grows its internal buffer by doubling it when necessary, but has
+no way to inform the client about how much space is available. Instead,
+we add a heuristic that skips parsing when we've repeatedly stopped on
+an incomplete token. Specifically:
+
+ * Only try to parse if we have a certain amount of data buffered
+ * Every time we stop on an incomplete token, double the threshold
+ * As soon as any token completes, the threshold is reset
+
+This means that when we get stuck on an incomplete token, the threshold
+grows exponentially, effectively making the client perform larger buffer
+fills, limiting how many times we can end up re-parsing the same bytes.
+
+Math:
+ Assume there's a token of T bytes
+ Assume the client fills the buffer in chunks of X bytes
+ We'll try to parse X, 2X, 4X, 8X ... until (2^k)X == T (or larger)
+ That's (2^(k+1)-1)X bytes parsed -- e.g. 15X if T = 8X
+ This is equal to 2T-X, which amounts to O(T)
+
+We could've chosen a faster growth rate, e.g. 4 or 8. Those seem to
+increase performance further, at the cost of further increasing the
+risk of growing the buffer more than necessary. This can easily be
+adjusted in the future, if desired.
+
+This is all completely transparent to the client, except for:
+1. possible delay of some callbacks (when our heuristic overshoots)
+2. apps that never do isFinal=XML_TRUE could miss data at the end
+
+For the affected testdata, this change shows a 100-400x speedup.
+The recset.xml benchmark shows no clear change either way.
+
+Before:
+benchmark -n ../testdata/largefiles/recset.xml 65535 3
+ 3 loops, with buffer size 65535. Average time per loop: 0.270223
+benchmark -n ../testdata/largefiles/aaaaaa_attr.xml 4096 3
+ 3 loops, with buffer size 4096. Average time per loop: 15.033048
+benchmark -n ../testdata/largefiles/aaaaaa_cdata.xml 4096 3
+ 3 loops, with buffer size 4096. Average time per loop: 0.018027
+benchmark -n ../testdata/largefiles/aaaaaa_comment.xml 4096 3
+ 3 loops, with buffer size 4096. Average time per loop: 11.775362
+benchmark -n ../testdata/largefiles/aaaaaa_tag.xml 4096 3
+ 3 loops, with buffer size 4096. Average time per loop: 11.711414
+benchmark -n ../testdata/largefiles/aaaaaa_text.xml 4096 3
+ 3 loops, with buffer size 4096. Average time per loop: 0.019362
+
+After:
+./run.sh benchmark -n ../testdata/largefiles/recset.xml 65535 3
+ 3 loops, with buffer size 65535. Average time per loop: 0.269030
+./run.sh benchmark -n ../testdata/largefiles/aaaaaa_attr.xml 4096 3
+ 3 loops, with buffer size 4096. Average time per loop: 0.044794
+./run.sh benchmark -n ../testdata/largefiles/aaaaaa_cdata.xml 4096 3
+ 3 loops, with buffer size 4096. Average time per loop: 0.016377
+./run.sh benchmark -n ../testdata/largefiles/aaaaaa_comment.xml 4096 3
+ 3 loops, with buffer size 4096. Average time per loop: 0.027022
+./run.sh benchmark -n ../testdata/largefiles/aaaaaa_tag.xml 4096 3
+ 3 loops, with buffer size 4096. Average time per loop: 0.099360
+./run.sh benchmark -n ../testdata/largefiles/aaaaaa_text.xml 4096 3
+ 3 loops, with buffer size 4096. Average time per loop: 0.017956
+---
+ lib/xmlparse.c | 58 +++++++++++++++++++++++++++++---------------
+ 1 file changed, 39 insertions(+), 19 deletions(-)
+
+diff --git a/lib/xmlparse.c b/lib/xmlparse.c
+index 5ba56eae..32df1eb9 100644
+--- a/lib/xmlparse.c
++++ b/lib/xmlparse.c
+@@ -65,6 +65,7 @@
+ # endif
+ #endif
+
++#include
+ #include
+ #include /* memset(), memcpy() */
+ #include
+@@ -613,6 +614,7 @@ struct XML_ParserStruct {
+ const char *m_bufferLim;
+ XML_Index m_parseEndByteIndex;
+ const char *m_parseEndPtr;
++ size_t m_partialTokenBytesBefore; /* used in heuristic to avoid O(n^2) */
+ XML_Char *m_dataBuf;
+ XML_Char *m_dataBufEnd;
+ XML_StartElementHandler m_startElementHandler;
+@@ -944,6 +946,32 @@ get_hash_secret_salt(XML_Parser parser) {
+ return parser->m_hash_secret_salt;
+ }
+
++static enum XML_Error
++callProcessor(XML_Parser parser, const char *start, const char *end,
++ const char **endPtr) {
++ const size_t have_now = EXPAT_SAFE_PTR_DIFF(end, start);
++
++ if (! parser->m_parsingStatus.finalBuffer) {
++ // Heuristic: don't try to parse a partial token again until the amount of
++ // available data has increased significantly.
++ const size_t had_before = parser->m_partialTokenBytesBefore;
++ const bool enough = (have_now >= 2 * had_before);
++
++ if (! enough) {
++ *endPtr = start; // callers may expect this to be set
++ return XML_ERROR_NONE;
++ }
++ }
++ const enum XML_Error ret = parser->m_processor(parser, start, end, endPtr);
++ // if we consumed nothing, remember what we had on this parse attempt.
++ if (*endPtr == start) {
++ parser->m_partialTokenBytesBefore = have_now;
++ } else {
++ parser->m_partialTokenBytesBefore = 0;
++ }
++ return ret;
++}
++
+ static XML_Bool /* only valid for root parser */
+ startParsing(XML_Parser parser) {
+ /* hash functions must be initialized before setContext() is called */
+@@ -1117,6 +1145,7 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) {
+ parser->m_bufferEnd = parser->m_buffer;
+ parser->m_parseEndByteIndex = 0;
+ parser->m_parseEndPtr = NULL;
++ parser->m_partialTokenBytesBefore = 0;
+ parser->m_declElementType = NULL;
+ parser->m_declAttributeId = NULL;
+ parser->m_declEntity = NULL;
+@@ -1849,29 +1878,20 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) {
+ to detect errors based on that fact.
+ */
+ parser->m_errorCode
+- = parser->m_processor(parser, parser->m_bufferPtr,
+- parser->m_parseEndPtr, &parser->m_bufferPtr);
++ = callProcessor(parser, parser->m_bufferPtr, parser->m_parseEndPtr,
++ &parser->m_bufferPtr);
+
+ if (parser->m_errorCode == XML_ERROR_NONE) {
+ switch (parser->m_parsingStatus.parsing) {
+ case XML_SUSPENDED:
+- /* It is hard to be certain, but it seems that this case
+- * cannot occur. This code is cleaning up a previous parse
+- * with no new data (since len == 0). Changing the parsing
+- * state requires getting to execute a handler function, and
+- * there doesn't seem to be an opportunity for that while in
+- * this circumstance.
+- *
+- * Given the uncertainty, we retain the code but exclude it
+- * from coverage tests.
+- *
+- * LCOV_EXCL_START
+- */
++ /* While we added no new data, the finalBuffer flag may have caused
++ * us to parse previously-unparsed data in the internal buffer.
++ * If that triggered a callback to the application, it would have
++ * had an opportunity to suspend parsing. */
+ XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr,
+ parser->m_bufferPtr, &parser->m_position);
+ parser->m_positionPtr = parser->m_bufferPtr;
+ return XML_STATUS_SUSPENDED;
+- /* LCOV_EXCL_STOP */
+ case XML_INITIALIZED:
+ case XML_PARSING:
+ parser->m_parsingStatus.parsing = XML_FINISHED;
+@@ -1901,7 +1921,7 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) {
+ parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal;
+
+ parser->m_errorCode
+- = parser->m_processor(parser, s, parser->m_parseEndPtr = s + len, &end);
++ = callProcessor(parser, s, parser->m_parseEndPtr = s + len, &end);
+
+ if (parser->m_errorCode != XML_ERROR_NONE) {
+ parser->m_eventEndPtr = parser->m_eventPtr;
+@@ -2004,8 +2024,8 @@ XML_ParseBuffer(XML_Parser parser, int len, int isFinal) {
+ parser->m_parseEndByteIndex += len;
+ parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal;
+
+- parser->m_errorCode = parser->m_processor(
+- parser, start, parser->m_parseEndPtr, &parser->m_bufferPtr);
++ parser->m_errorCode = callProcessor(parser, start, parser->m_parseEndPtr,
++ &parser->m_bufferPtr);
+
+ if (parser->m_errorCode != XML_ERROR_NONE) {
+ parser->m_eventEndPtr = parser->m_eventPtr;
+@@ -2192,7 +2212,7 @@ XML_ResumeParser(XML_Parser parser) {
+ }
+ parser->m_parsingStatus.parsing = XML_PARSING;
+
+- parser->m_errorCode = parser->m_processor(
++ parser->m_errorCode = callProcessor(
+ parser, parser->m_bufferPtr, parser->m_parseEndPtr, &parser->m_bufferPtr);
+
+ if (parser->m_errorCode != XML_ERROR_NONE) {
+--
+2.33.0
+
+
diff --git a/backport-001-CVE-2023-52426.patch b/backport-001-CVE-2023-52426.patch
new file mode 100644
index 0000000000000000000000000000000000000000..c1e3caf399ffd7bf06fd5ffb3ae0fa2b01866648
--- /dev/null
+++ b/backport-001-CVE-2023-52426.patch
@@ -0,0 +1,72 @@
+From 62e8805678b10ec0642ed78e15b914c01fe81699 Mon Sep 17 00:00:00 2001
+From: Sebastian Pipping
+Date: Thu, 26 Oct 2023 00:59:52 +0200
+Subject: [PATCH] cmake: Introduce option EXPAT_GE to control macro XML_GE
+
+Reference: https://github.com//libexpat/libexpat/commit/daa89e42c005cc7f4f7af9eee271ae0723d30300
+Conflict: remove expat_config_h_cmake__expected.txt
+ change expat_shy_set to option in CMakeLists.txt
+---
+ CMakeLists.txt | 9 +++++++++
+ expat_config.h.cmake | 3 +++
+ 2 files changed, 12 insertions(+)
+
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index 2b4c13c..4f31132 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -140,6 +140,8 @@ expat_shy_set(EXPAT_CONTEXT_BYTES 1024 CACHE STRING "Define to specify how much
+ mark_as_advanced(EXPAT_CONTEXT_BYTES)
+ expat_shy_set(EXPAT_DTD ON CACHE BOOL "Define to make parameter entity parsing functionality available")
+ mark_as_advanced(EXPAT_DTD)
++option(EXPAT_GE "Define to make general entity parsing functionality available" ON)
++mark_as_advanced(EXPAT_GE)
+ expat_shy_set(EXPAT_NS ON CACHE BOOL "Define to make XML Namespaces functionality available")
+ mark_as_advanced(EXPAT_NS)
+ expat_shy_set(EXPAT_WARNINGS_AS_ERRORS OFF CACHE BOOL "Treat all compiler warnings as errors")
+@@ -172,6 +174,11 @@ endif()
+ #
+ # Environment checks
+ #
++if(EXPAT_DTD AND NOT EXPAT_GE)
++ message(SEND_ERROR "Option EXPAT_DTD requires that EXPAT_GE is also enabled.")
++ message(SEND_ERROR "Please either enable option EXPAT_GE (recommended) or disable EXPAT_DTD also.")
++endif()
++
+ if(EXPAT_WITH_LIBBSD)
+ find_library(LIB_BSD NAMES bsd)
+ if(NOT LIB_BSD)
+@@ -274,6 +281,7 @@ endif()
+
+ _expat_copy_bool_int(EXPAT_ATTR_INFO XML_ATTR_INFO)
+ _expat_copy_bool_int(EXPAT_DTD XML_DTD)
++_expat_copy_bool_int(EXPAT_GE XML_GE)
+ _expat_copy_bool_int(EXPAT_LARGE_SIZE XML_LARGE_SIZE)
+ _expat_copy_bool_int(EXPAT_MIN_SIZE XML_MIN_SIZE)
+ _expat_copy_bool_int(EXPAT_NS XML_NS)
+@@ -893,6 +901,7 @@ message(STATUS " // Advanced options, changes not advised")
+ message(STATUS " Attributes info .......... ${EXPAT_ATTR_INFO}")
+ message(STATUS " Context bytes ............ ${EXPAT_CONTEXT_BYTES}")
+ message(STATUS " DTD support .............. ${EXPAT_DTD}")
++message(STATUS " General entities ......... ${EXPAT_GE}")
+ message(STATUS " Large size ............... ${EXPAT_LARGE_SIZE}")
+ message(STATUS " Minimum size ............. ${EXPAT_MIN_SIZE}")
+ message(STATUS " Namespace support ........ ${EXPAT_NS}")
+diff --git a/expat_config.h.cmake b/expat_config.h.cmake
+index 78fcb4c..330945e 100644
+--- a/expat_config.h.cmake
++++ b/expat_config.h.cmake
+@@ -103,6 +103,9 @@
+ /* Define to make parameter entity parsing functionality available. */
+ #cmakedefine XML_DTD
+
++/* Define as 1/0 to enable/disable support for general entities. */
++#define XML_GE @XML_GE@
++
+ /* Define to make XML Namespaces functionality available. */
+ #cmakedefine XML_NS
+
+--
+2.33.0
+
+
diff --git a/backport-001-CVE-2024-45490.patch b/backport-001-CVE-2024-45490.patch
new file mode 100644
index 0000000000000000000000000000000000000000..d9db9ecf13535f9283f197031f607fd5a48b6da0
--- /dev/null
+++ b/backport-001-CVE-2024-45490.patch
@@ -0,0 +1,46 @@
+From d728c268c46879c5c4b8479e60f8fa7804de22d7 Mon Sep 17 00:00:00 2001
+From: Sebastian Pipping
+Date: Sun, 25 Aug 2024 19:09:51 +0200
+Subject: [PATCH] doc: Document that XML_Parse/XML_ParseBuffer reject "len < 0"
+
+---
+ doc/reference.html | 10 +++++++++-
+ 1 file changed, 9 insertions(+), 1 deletion(-)
+
+diff --git a/doc/reference.html b/doc/reference.html
+index f4584b6..7d30fae 100644
+--- a/doc/reference.html
++++ b/doc/reference.html
+@@ -1098,7 +1098,9 @@ containing part (or perhaps all) of the document. The number of bytes of s
+ that are part of the document is indicated by len
. This means
+ that s
doesn't have to be null terminated. It also means that
+ if len
is larger than the number of bytes in the block of
+-memory that s
points at, then a memory fault is likely. The
++memory that s
points at, then a memory fault is likely.
++Negative values for len
are rejected since Expat 2.2.1.
++The
+ isFinal
parameter informs the parser that this is the last
+ piece of the document. Frequently, the last piece is empty (i.e.
+ len
is zero.)
+@@ -1114,11 +1116,17 @@ XML_ParseBuffer(XML_Parser p,
+ int isFinal);
+
+
++
+ This is just like XML_Parse
,
+ except in this case Expat provides the buffer. By obtaining the
+ buffer from Expat with the XML_GetBuffer
function, the application can avoid double
+ copying of the input.
++
++
++
++Negative values for len
are rejected since Expat 2.6.3.
++
+
+
+ XML_GetBuffer
+--
+2.33.0
+
+
diff --git a/backport-002-CVE-2023-52425.patch b/backport-002-CVE-2023-52425.patch
new file mode 100644
index 0000000000000000000000000000000000000000..6ed28d3b9037162a2ad6ad4c82190977694f52ca
--- /dev/null
+++ b/backport-002-CVE-2023-52425.patch
@@ -0,0 +1,40 @@
+From c3a4816e175ede7da1a692a50d6251efdfe41a45 Mon Sep 17 00:00:00 2001
+From: Snild Dolkow
+Date: Mon, 4 Sep 2023 17:21:14 +0200
+Subject: [PATCH] Don't update partial token heuristic on error
+
+Reference: https://github.com/libexpat/libexpat/pull/789/commits/1b9d398517befeb944cbbadadf10992b07e96fa2
+Conflict: no
+
+Suggested-by: Sebastian Pipping
+---
+ lib/xmlparse.c | 12 +++++++-----
+ 1 file changed, 7 insertions(+), 5 deletions(-)
+
+diff --git a/lib/xmlparse.c b/lib/xmlparse.c
+index 32df1eb9..a8414dd7 100644
+--- a/lib/xmlparse.c
++++ b/lib/xmlparse.c
+@@ -963,11 +963,13 @@ callProcessor(XML_Parser parser, const char *start, const char *end,
+ }
+ }
+ const enum XML_Error ret = parser->m_processor(parser, start, end, endPtr);
+- // if we consumed nothing, remember what we had on this parse attempt.
+- if (*endPtr == start) {
+- parser->m_partialTokenBytesBefore = have_now;
+- } else {
+- parser->m_partialTokenBytesBefore = 0;
++ if (ret == XML_ERROR_NONE) {
++ // if we consumed nothing, remember what we had on this parse attempt.
++ if (*endPtr == start) {
++ parser->m_partialTokenBytesBefore = have_now;
++ } else {
++ parser->m_partialTokenBytesBefore = 0;
++ }
+ }
+ return ret;
+ }
+--
+2.33.0
+
+
diff --git a/backport-002-CVE-2023-52426.patch b/backport-002-CVE-2023-52426.patch
new file mode 100644
index 0000000000000000000000000000000000000000..2855206ba8ea958b679c74e7735bbd4395050f82
--- /dev/null
+++ b/backport-002-CVE-2023-52426.patch
@@ -0,0 +1,30 @@
+From ed87a4793404e91c0cc0c81435fcfcc64a8be9f4 Mon Sep 17 00:00:00 2001
+From: Sebastian Pipping
+Date: Thu, 26 Oct 2023 00:45:23 +0200
+Subject: [PATCH 02/17] configure.ac: Define macro XML_GE as 1
+
+Reference: https://github.com//libexpat/libexpat/commit/ed87a4793404e91c0cc0c81435fcfcc64a8be9f4
+Conflict: remove expat_config_h_in__expected.txt
+
+---
+ configure.ac | 2 ++
+ 2 files changed, 3 insertions(+)
+
+diff --git a/expat/configure.ac b/expat/configure.ac
+index c9f95bca..fec4ecd0 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -303,6 +303,8 @@ AC_SUBST(FILEMAP)
+ dnl Some basic configuration:
+ AC_DEFINE([XML_NS], 1,
+ [Define to make XML Namespaces functionality available.])
++AC_DEFINE([XML_GE], 1,
++ [Define as 1/0 to enable/disable support for general entities.])
+ AC_DEFINE([XML_DTD], 1,
+ [Define to make parameter entity parsing functionality available.])
+ AC_DEFINE([XML_DEV_URANDOM], 1,
+--
+2.37.3.windows.1
+
+
+
diff --git a/backport-002-CVE-2024-45490.patch b/backport-002-CVE-2024-45490.patch
new file mode 100644
index 0000000000000000000000000000000000000000..1eb748cf582fbdf84cf7c6a89a47a07c5006edec
--- /dev/null
+++ b/backport-002-CVE-2024-45490.patch
@@ -0,0 +1,31 @@
+From a5d580af424bde0c83ad64fcc8bd3beff1db317d Mon Sep 17 00:00:00 2001
+From: Sebastian Pipping
+Date: Mon, 19 Aug 2024 22:26:07 +0200
+Subject: [PATCH] lib: Reject negative len for XML_ParseBuffer
+
+Reported by TaiYou
+---
+ lib/xmlparse.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/lib/xmlparse.c b/lib/xmlparse.c
+index bd6aa72..8b9046e 100644
+--- a/lib/xmlparse.c
++++ b/lib/xmlparse.c
+@@ -2016,6 +2016,12 @@ XML_ParseBuffer(XML_Parser parser, int len, int isFinal) {
+
+ if (parser == NULL)
+ return XML_STATUS_ERROR;
++
++ if (len < 0) {
++ parser->m_errorCode = XML_ERROR_INVALID_ARGUMENT;
++ return XML_STATUS_ERROR;
++ }
++
+ switch (parser->m_parsingStatus.parsing) {
+ case XML_SUSPENDED:
+ parser->m_errorCode = XML_ERROR_SUSPENDED;
+--
+2.33.0
+
+
diff --git a/backport-003-CVE-2023-52425.patch b/backport-003-CVE-2023-52425.patch
new file mode 100644
index 0000000000000000000000000000000000000000..bacc4df0a03273fee6ae2a6891d1a76acc0b9086
--- /dev/null
+++ b/backport-003-CVE-2023-52425.patch
@@ -0,0 +1,62 @@
+From af7d2acf60b2d42506c7fb7e61ed3dbc7989dd01 Mon Sep 17 00:00:00 2001
+From: Snild Dolkow
+Date: Thu, 31 Aug 2023 12:36:43 +0200
+Subject: [PATCH] Always consume BOM bytes when found in prolog
+
+Reference: https://github.com/libexpat/libexpat/commit/b1e955449cea6bb5862cd249e659c2123bd95a9e
+Conflict: change xmlparse.c
+
+The byte order mark is not correctly consumed when followed by an
+incomplete token in a non-final parse. This results in the BOM staying
+in the buffer, causing an invalid token error later.
+
+This was not detected by existing tests because they either parse
+everything in one call, or add a single byte at a time.
+
+By moving forward when we find a BOM, we make sure that the BOM
+bytes are properly consumed in all cases.
+---
+ lib/xmlparse.c | 18 +++++++++---------
+ 1 file changed, 9 insertions(+), 9 deletions(-)
+
+diff --git a/lib/xmlparse.c b/lib/xmlparse.c
+index daceacf..184997d 100644
+--- a/lib/xmlparse.c
++++ b/lib/xmlparse.c
+@@ -4502,15 +4502,15 @@ entityValueInitProcessor(XML_Parser parser, const char *s, const char *end,
+ parser->m_processor = entityValueProcessor;
+ return entityValueProcessor(parser, next, end, nextPtr);
+ }
+- /* If we are at the end of the buffer, this would cause XmlPrologTok to
+- return XML_TOK_NONE on the next call, which would then cause the
+- function to exit with *nextPtr set to s - that is what we want for other
+- tokens, but not for the BOM - we would rather like to skip it;
+- then, when this routine is entered the next time, XmlPrologTok will
+- return XML_TOK_INVALID, since the BOM is still in the buffer
++ /* XmlPrologTok has now set the encoding based on the BOM it found, and we
++ must move s and nextPtr forward to consume the BOM.
++
++ If we didn't, and got XML_TOK_NONE from the next XmlPrologTok call, we
++ would leave the BOM in the buffer and return. On the next call to this
++ function, our XmlPrologTok call would return XML_TOK_INVALID, since it
++ is not valid to have multiple BOMs.
+ */
+- else if (tok == XML_TOK_BOM && next == end
+- && ! parser->m_parsingStatus.finalBuffer) {
++ else if (tok == XML_TOK_BOM) {
+ # if defined(XML_DTD) || XML_GE == 1
+ if (! accountingDiffTolerated(parser, tok, s, next, __LINE__,
+ XML_ACCOUNT_DIRECT)) {
+@@ -4520,7 +4520,7 @@ entityValueInitProcessor(XML_Parser parser, const char *s, const char *end,
+ # endif
+
+ *nextPtr = next;
+- return XML_ERROR_NONE;
++ s = next;
+ }
+ /* If we get this token, we have the start of what might be a
+ normal tag, but not a declaration (i.e. it doesn't begin with
+--
+2.33.0
+
+
diff --git a/backport-003-CVE-2023-52426.patch b/backport-003-CVE-2023-52426.patch
new file mode 100644
index 0000000000000000000000000000000000000000..1b8e99e414ebb4fa35f19608bd3b2ed58ec4226c
--- /dev/null
+++ b/backport-003-CVE-2023-52426.patch
@@ -0,0 +1,399 @@
+From 34ef2a26ab33c724c666b33ce08f13983c79c612 Mon Sep 17 00:00:00 2001
+From: Sebastian Pipping
+Date: Thu, 26 Oct 2023 00:43:22 +0200
+Subject: [PATCH] lib|xmlwf|cmake: Extend scope of billion laughs attack
+ protection
+
+.. from "defined(XML_DTD)" to "defined(XML_DTD) || XML_GE==1".
+
+Reference: https://github.com//libexpat/libexpat/commit/ff958ebde28854845b28167695a678cc49a50c4b
+Conflict: remove _expat_def_file_toggle in CMakeLists.txt of win32
+ remove _EXPAT_COMMENT_DTD_OR_GE in libexpat.def.cmake of MS VC++
+ adapt entityValueInitProcessor
+ adapt internalEntityProcessor
+---
+ lib/expat.h | 8 +++---
+ lib/internal.h | 2 +-
+ lib/xmlparse.c | 71 ++++++++++++++++++++++++++------------------------
+ xmlwf/xmlwf.c | 18 +++++++------
+ 4 files changed, 53 insertions(+), 46 deletions(-)
+
+diff --git a/lib/expat.h b/lib/expat.h
+index 1c83563..33c94af 100644
+--- a/lib/expat.h
++++ b/lib/expat.h
+@@ -1038,13 +1038,15 @@ typedef struct {
+ XMLPARSEAPI(const XML_Feature *)
+ XML_GetFeatureList(void);
+
+-#ifdef XML_DTD
+-/* Added in Expat 2.4.0. */
++#if defined(XML_DTD) || 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)
+ XML_SetBillionLaughsAttackProtectionMaximumAmplification(
+ XML_Parser parser, float maximumAmplificationFactor);
+
+-/* Added in Expat 2.4.0. */
++/* Added in Expat 2.4.0 for XML_DTD defined and
++ * added in Expat 2.6.0 for XML_GE == 1. */
+ XMLPARSEAPI(XML_Bool)
+ XML_SetBillionLaughsAttackProtectionActivationThreshold(
+ XML_Parser parser, unsigned long long activationThresholdBytes);
+diff --git a/lib/internal.h b/lib/internal.h
+index e09f533..1851925 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)
++#if defined(XML_DTD) || 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 9458b09..ecc09e9 100644
+--- a/lib/xmlparse.c
++++ b/lib/xmlparse.c
+@@ -408,7 +408,7 @@ enum XML_Account {
+ XML_ACCOUNT_NONE /* i.e. do not account, was accounted already */
+ };
+
+-#ifdef XML_DTD
++#if defined(XML_DTD) || XML_GE == 1
+ typedef unsigned long long XmlBigCount;
+ typedef struct accounting {
+ XmlBigCount countBytesDirect;
+@@ -424,7 +424,7 @@ typedef struct entity_stats {
+ unsigned int maximumDepthSeen;
+ int debugLevel;
+ } ENTITY_STATS;
+-#endif /* XML_DTD */
++#endif /* defined(XML_DTD) || XML_GE == 1 */
+
+ typedef enum XML_Error PTRCALL Processor(XML_Parser parser, const char *start,
+ const char *end, const char **endPtr);
+@@ -562,7 +562,7 @@ static XML_Parser parserCreate(const XML_Char *encodingName,
+
+ static void parserInit(XML_Parser parser, const XML_Char *encodingName);
+
+-#ifdef XML_DTD
++#if defined(XML_DTD) || XML_GE == 1
+ static float accountingGetCurrentAmplification(XML_Parser rootParser);
+ static void accountingReportStats(XML_Parser originParser, const char *epilog);
+ static void accountingOnAbort(XML_Parser originParser);
+@@ -585,7 +585,7 @@ static void entityTrackingOnClose(XML_Parser parser, ENTITY *entity,
+
+ static XML_Parser getRootParserOf(XML_Parser parser,
+ unsigned int *outLevelDiff);
+-#endif /* XML_DTD */
++#endif /* defined(XML_DTD) || XML_GE == 1 */
+
+ static unsigned long getDebugLevel(const char *variableName,
+ unsigned long defaultDebugLevel);
+@@ -703,7 +703,7 @@ struct XML_ParserStruct {
+ enum XML_ParamEntityParsing m_paramEntityParsing;
+ #endif
+ unsigned long m_hash_secret_salt;
+-#ifdef XML_DTD
++#if defined(XML_DTD) || XML_GE == 1
+ ACCOUNTING m_accounting;
+ ENTITY_STATS m_entity_stats;
+ #endif
+@@ -1163,7 +1163,7 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) {
+ #endif
+ parser->m_hash_secret_salt = 0;
+
+-#ifdef XML_DTD
++#if defined(XML_DTD) || XML_GE == 1
+ memset(&parser->m_accounting, 0, sizeof(ACCOUNTING));
+ parser->m_accounting.debugLevel = getDebugLevel("EXPAT_ACCOUNTING_DEBUG", 0u);
+ parser->m_accounting.maximumAmplificationFactor
+@@ -2522,8 +2522,9 @@ XML_GetFeatureList(void) {
+ #ifdef XML_ATTR_INFO
+ {XML_FEATURE_ATTR_INFO, XML_L("XML_ATTR_INFO"), 0},
+ #endif
+-#ifdef XML_DTD
+- /* Added in Expat 2.4.0. */
++#if defined(XML_DTD) || 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,
+ XML_L("XML_BLAP_MAX_AMP"),
+ (long int)
+@@ -2537,7 +2538,7 @@ XML_GetFeatureList(void) {
+ return features;
+ }
+
+-#ifdef XML_DTD
++#if defined(XML_DTD) || XML_GE == 1
+ XML_Bool XMLCALL
+ XML_SetBillionLaughsAttackProtectionMaximumAmplification(
+ XML_Parser parser, float maximumAmplificationFactor) {
+@@ -2559,7 +2560,7 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold(
+ parser->m_accounting.activationThresholdBytes = activationThresholdBytes;
+ return XML_TRUE;
+ }
+-#endif /* XML_DTD */
++#endif /* defined(XML_DTD) || XML_GE == 1 */
+
+ /* Initially tag->rawName always points into the parse buffer;
+ for those TAG instances opened while the current parse buffer was
+@@ -2645,13 +2646,13 @@ externalEntityInitProcessor2(XML_Parser parser, const char *start,
+ int tok = XmlContentTok(parser->m_encoding, start, end, &next);
+ switch (tok) {
+ case XML_TOK_BOM:
+-#ifdef XML_DTD
++#if defined(XML_DTD) || XML_GE == 1
+ if (! accountingDiffTolerated(parser, tok, start, next, __LINE__,
+ XML_ACCOUNT_DIRECT)) {
+ accountingOnAbort(parser);
+ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
+ }
+-#endif /* XML_DTD */
++#endif /* defined(XML_DTD) || 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
+@@ -2765,7 +2766,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);
+-#ifdef XML_DTD
++#if defined(XML_DTD) || XML_GE == 1
+ const char *accountAfter
+ = ((tok == XML_TOK_TRAILING_RSQB) || (tok == XML_TOK_TRAILING_CR))
+ ? (haveMore ? s /* i.e. 0 bytes */ : end)
+@@ -2831,14 +2832,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) {
+-#ifdef XML_DTD
++#if defined(XML_DTD) || 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 /* XML_DTD */
++#endif /* defined(XML_DTD) || XML_GE == 1 */
+ if (parser->m_characterDataHandler)
+ parser->m_characterDataHandler(parser->m_handlerArg, &ch, 1);
+ else if (parser->m_defaultHandler)
+@@ -4040,7 +4041,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);
+-#ifdef XML_DTD
++#if defined(XML_DTD) || XML_GE == 1
+ if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, account)) {
+ accountingOnAbort(parser);
+ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
+@@ -4192,7 +4193,7 @@ doIgnoreSection(XML_Parser parser, const ENCODING *enc, const char **startPtr,
+ *eventPP = s;
+ *startPtr = NULL;
+ tok = XmlIgnoreSectionTok(enc, s, end, &next);
+-# ifdef XML_DTD
++# if defined(XML_DTD) || XML_GE == 1
+ if (! accountingDiffTolerated(parser, tok, s, next, __LINE__,
+ XML_ACCOUNT_DIRECT)) {
+ accountingOnAbort(parser);
+@@ -4284,7 +4285,7 @@ processXmlDecl(XML_Parser parser, int isGeneralTextEntity, const char *s,
+ const XML_Char *storedversion = NULL;
+ int standalone = -1;
+
+-#ifdef XML_DTD
++#if defined(XML_DTD) || XML_GE == 1
+ if (! accountingDiffTolerated(parser, XML_TOK_XML_DECL, s, next, __LINE__,
+ XML_ACCOUNT_DIRECT)) {
+ accountingOnAbort(parser);
+@@ -4491,7 +4492,7 @@ entityValueInitProcessor(XML_Parser parser, const char *s, const char *end,
+ */
+ else if (tok == XML_TOK_BOM && next == end
+ && ! parser->m_parsingStatus.finalBuffer) {
+-# ifdef XML_DTD
++# if defined(XML_DTD) || XML_GE == 1
+ if (! accountingDiffTolerated(parser, tok, s, next, __LINE__,
+ XML_ACCOUNT_DIRECT)) {
+ accountingOnAbort(parser);
+@@ -4707,11 +4708,13 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
+ }
+ }
+ role = XmlTokenRole(&parser->m_prologState, tok, s, next, enc);
+-#ifdef XML_DTD
++#if defined(XML_DTD) || XML_GE == 1
+ switch (role) {
+ case XML_ROLE_INSTANCE_START: // bytes accounted in contentProcessor
+ case XML_ROLE_XML_DECL: // bytes accounted in processXmlDecl
+- case XML_ROLE_TEXT_DECL: // bytes accounted in processXmlDecl
++# ifdef XML_DTD
++ case XML_ROLE_TEXT_DECL: // bytes accounted in processXmlDecl
++# endif
+ break;
+ default:
+ if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, account)) {
+@@ -5648,7 +5651,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);
+-#ifdef XML_DTD
++#if defined(XML_DTD) || XML_GE == 1
+ if (! accountingDiffTolerated(parser, tok, s, next, __LINE__,
+ XML_ACCOUNT_DIRECT)) {
+ accountingOnAbort(parser);
+@@ -5728,7 +5731,7 @@ processInternalEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl) {
+ return XML_ERROR_NO_MEMORY;
+ }
+ entity->open = XML_TRUE;
+-#ifdef XML_DTD
++#if defined(XML_DTD) || XML_GE == 1
+ entityTrackingOnOpen(parser, entity, __LINE__);
+ #endif
+ entity->processed = 0;
+@@ -5762,9 +5765,9 @@ processInternalEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl) {
+ entity->processed = (int)(next - textStart);
+ parser->m_processor = internalEntityProcessor;
+ } else {
+-#ifdef XML_DTD
++#if defined(XML_DTD) || XML_GE == 1
+ entityTrackingOnClose(parser, entity, __LINE__);
+-#endif /* XML_DTD */
++#endif /* defined(XML_DTD) || XML_GE == 1 */
+ entity->open = XML_FALSE;
+ parser->m_openInternalEntities = openEntity->next;
+ /* put openEntity back in list of free instances */
+@@ -5813,7 +5816,7 @@ internalEntityProcessor(XML_Parser parser, const char *s, const char *end,
+ return result;
+ }
+
+-#ifdef XML_DTD
++#if defined(XML_DTD) || XML_GE == 1
+ entityTrackingOnClose(parser, entity, __LINE__);
+ #endif
+ entity->open = XML_FALSE;
+@@ -5892,7 +5895,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);
+-#ifdef XML_DTD
++#if defined(XML_DTD) || XML_GE == 1
+ if (! accountingDiffTolerated(parser, tok, ptr, next, __LINE__, account)) {
+ accountingOnAbort(parser);
+ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
+@@ -5957,14 +5960,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) {
+-#ifdef XML_DTD
++#if defined(XML_DTD) || 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 /* XML_DTD */
++#endif /* defined(XML_DTD) || XML_GE == 1 */
+ if (! poolAppendChar(pool, ch))
+ return XML_ERROR_NO_MEMORY;
+ break;
+@@ -6042,14 +6045,14 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
+ enum XML_Error result;
+ const XML_Char *textEnd = entity->textPtr + entity->textLen;
+ entity->open = XML_TRUE;
+-#ifdef XML_DTD
++#if defined(XML_DTD) || XML_GE == 1
+ entityTrackingOnOpen(parser, entity, __LINE__);
+ #endif
+ result = appendAttributeValue(parser, parser->m_internalEncoding,
+ isCdata, (const char *)entity->textPtr,
+ (const char *)textEnd, pool,
+ XML_ACCOUNT_ENTITY_EXPANSION);
+-#ifdef XML_DTD
++#if defined(XML_DTD) || XML_GE == 1
+ entityTrackingOnClose(parser, entity, __LINE__);
+ #endif
+ entity->open = XML_FALSE;
+@@ -6105,7 +6108,7 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc,
+ = entityTextPtr; /* XmlEntityValueTok doesn't always set the last arg */
+ int tok = XmlEntityValueTok(enc, entityTextPtr, entityTextEnd, &next);
+
+-#ifdef XML_DTD
++#if defined(XML_DTD) || XML_GE == 1
+ if (! accountingDiffTolerated(parser, tok, entityTextPtr, next, __LINE__,
+ account)) {
+ accountingOnAbort(parser);
+@@ -7651,7 +7654,7 @@ copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) {
+ return result;
+ }
+
+-#ifdef XML_DTD
++#if defined(XML_DTD) || XML_GE == 1
+
+ static float
+ accountingGetCurrentAmplification(XML_Parser rootParser) {
+@@ -8386,7 +8389,7 @@ unsignedCharToPrintable(unsigned char c) {
+ assert(0); /* never gets here */
+ }
+
+-#endif /* XML_DTD */
++#endif /* defined(XML_DTD) || XML_GE == 1 */
+
+ static unsigned long
+ getDebugLevel(const char *variableName, unsigned long defaultDebugLevel) {
+diff --git a/xmlwf/xmlwf.c b/xmlwf/xmlwf.c
+index 471f2a2..be23f5a 100644
+--- a/xmlwf/xmlwf.c
++++ b/xmlwf/xmlwf.c
+@@ -1062,9 +1062,10 @@ tmain(int argc, XML_Char **argv) {
+ " (needs a floating point number greater or equal than 1.0)"));
+ exit(XMLWF_EXIT_USAGE_ERROR);
+ }
+-#ifndef XML_DTD
+- ftprintf(stderr, T("Warning: Given amplification limit ignored") T(
+- ", xmlwf has been compiled without DTD support.\n"));
++#if ! defined(XML_DTD) && XML_GE == 0
++ ftprintf(stderr,
++ T("Warning: Given amplification limit ignored")
++ T(", xmlwf has been compiled without DTD/GE support.\n"));
+ #endif
+ break;
+ }
+@@ -1083,9 +1084,10 @@ tmain(int argc, XML_Char **argv) {
+ exit(XMLWF_EXIT_USAGE_ERROR);
+ }
+ attackThresholdGiven = XML_TRUE;
+-#ifndef XML_DTD
+- ftprintf(stderr, T("Warning: Given attack threshold ignored") T(
+- ", xmlwf has been compiled without DTD support.\n"));
++#if ! defined(XML_DTD) && XML_GE == 0
++ ftprintf(stderr,
++ T("Warning: Given attack threshold ignored")
++ T(", xmlwf has been compiled without DTD/GE support.\n"));
+ #endif
+ break;
+ }
+@@ -1120,13 +1122,13 @@ tmain(int argc, XML_Char **argv) {
+ }
+
+ if (attackMaximumAmplification != -1.0f) {
+-#ifdef XML_DTD
++#if defined(XML_DTD) || XML_GE == 1
+ XML_SetBillionLaughsAttackProtectionMaximumAmplification(
+ parser, attackMaximumAmplification);
+ #endif
+ }
+ if (attackThresholdGiven) {
+-#ifdef XML_DTD
++#if defined(XML_DTD) || XML_GE == 1
+ XML_SetBillionLaughsAttackProtectionActivationThreshold(
+ parser, attackThresholdBytes);
+ #else
+--
+2.33.0
+
+
diff --git a/backport-003-CVE-2024-45490.patch b/backport-003-CVE-2024-45490.patch
new file mode 100644
index 0000000000000000000000000000000000000000..db670edd9b037aa7b439ccc3e52f0830c195e532
--- /dev/null
+++ b/backport-003-CVE-2024-45490.patch
@@ -0,0 +1,84 @@
+From a882e725dd057db98907f6b03b733f0f6889aee7 Mon Sep 17 00:00:00 2001
+From: Sebastian Pipping
+Date: Tue, 20 Aug 2024 22:57:12 +0200
+Subject: [PATCH] tests: Cover "len < 0" for both XML_Parse and XML_ParseBuffer
+
+---
+ tests/runtests.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 53 insertions(+)
+
+diff --git a/tests/runtests.c b/tests/runtests.c
+index 02c8c85..4649359 100644
+--- a/tests/runtests.c
++++ b/tests/runtests.c
+@@ -3978,6 +3978,57 @@ START_TEST(test_empty_parse) {
+ }
+ END_TEST
+
++/* Test XML_Parse for len < 0 */
++START_TEST(test_negative_len_parse) {
++ const char *const doc = "";
++ for (int isFinal = 0; isFinal < 2; isFinal++) {
++ XML_Parser parser = XML_ParserCreate(NULL);
++
++ if (XML_GetErrorCode(parser) != XML_ERROR_NONE)
++ fail("There was not supposed to be any initial parse error.");
++
++ const enum XML_Status status = XML_Parse(parser, doc, -1, isFinal);
++
++ if (status != XML_STATUS_ERROR)
++ fail("Negative len was expected to fail the parse but did not.");
++
++ if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_ARGUMENT)
++ fail("Parse error does not match XML_ERROR_INVALID_ARGUMENT.");
++
++ XML_ParserFree(parser);
++ }
++}
++END_TEST
++
++/* Test XML_ParseBuffer for len < 0 */
++START_TEST(test_negative_len_parse_buffer) {
++ const char *const doc = "";
++ for (int isFinal = 0; isFinal < 2; isFinal++) {
++ XML_Parser parser = XML_ParserCreate(NULL);
++
++ if (XML_GetErrorCode(parser) != XML_ERROR_NONE)
++ fail("There was not supposed to be any initial parse error.");
++
++ void *const buffer = XML_GetBuffer(parser, (int)strlen(doc));
++
++ if (buffer == NULL)
++ fail("XML_GetBuffer failed.");
++
++ memcpy(buffer, doc, strlen(doc));
++
++ const enum XML_Status status = XML_ParseBuffer(parser, -1, isFinal);
++
++ if (status != XML_STATUS_ERROR)
++ fail("Negative len was expected to fail the parse but did not.");
++
++ if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_ARGUMENT)
++ fail("Parse error does not match XML_ERROR_INVALID_ARGUMENT.");
++
++ XML_ParserFree(parser);
++ }
++}
++END_TEST
++
+ /* Test odd corners of the XML_GetBuffer interface */
+ static enum XML_Status
+ get_feature(enum XML_FeatureEnum feature_id, long *presult) {
+@@ -12474,6 +12525,8 @@ make_suite(void) {
+ tcase_add_test__ifdef_xml_dtd(tc_basic, test_user_parameters);
+ tcase_add_test__ifdef_xml_dtd(tc_basic, test_ext_entity_ref_parameter);
+ tcase_add_test(tc_basic, test_empty_parse);
++ tcase_add_test(tc_basic, test_negative_len_parse);
++ tcase_add_test(tc_basic, test_negative_len_parse_buffer);
+ tcase_add_test(tc_basic, test_get_buffer_1);
+ tcase_add_test(tc_basic, test_get_buffer_2);
+ #if defined(XML_CONTEXT_BYTES)
+--
+2.33.0
+
+
diff --git a/backport-004-CVE-2023-52425.patch b/backport-004-CVE-2023-52425.patch
new file mode 100644
index 0000000000000000000000000000000000000000..e2f9b8de673d04b26b0751a5629b0d012e6e561f
--- /dev/null
+++ b/backport-004-CVE-2023-52425.patch
@@ -0,0 +1,41 @@
+From a7b9a07cd50a4422194f64eb50181fcaec3ef0cf Mon Sep 17 00:00:00 2001
+From: Snild Dolkow
+Date: Thu, 24 Aug 2023 09:31:31 +0200
+Subject: [PATCH] tests: Move triplet_start_checker flag check after isFinal=1
+ call
+
+Reference: https://github.com/libexpat/libexpat/pull/745/commits/d52b4141496bd26bd716d88c67af8f2250bd0da6
+Conflict: remove ns_tests.c
+ change runtests.c
+
+There is no guarantee that the callback will happen before the parse
+call with isFinal=XML_TRUE. Let's move the assertion to a location
+where we know it must have happened.
+---
+ tests/runtests.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/tests/runtests.c b/tests/runtests.c
+index 45ba5d59..8f1d11f0 100644
+--- a/tests/runtests.c
++++ b/tests/runtests.c
+@@ -6527,13 +6527,13 @@ START_TEST(test_return_ns_triplet) {
+ if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_FALSE)
+ == XML_STATUS_ERROR)
+ xml_failure(g_parser);
+- if (! triplet_start_flag)
+- fail("triplet_start_checker not invoked");
+ /* Check that unsetting "return triplets" fails while still parsing */
+ XML_SetReturnNSTriplet(g_parser, XML_FALSE);
+ if (_XML_Parse_SINGLE_BYTES(g_parser, epilog, (int)strlen(epilog), XML_TRUE)
+ == XML_STATUS_ERROR)
+ xml_failure(g_parser);
++ if (! triplet_start_flag)
++ fail("triplet_start_checker not invoked");
+ if (! triplet_end_flag)
+ fail("triplet_end_checker not invoked");
+ if (dummy_handler_flags
+--
+2.33.0
+
+
diff --git a/backport-004-CVE-2023-52426.patch b/backport-004-CVE-2023-52426.patch
new file mode 100644
index 0000000000000000000000000000000000000000..bd2f4811a06fe8c3d507e0df2c2c95ba5162c1dd
--- /dev/null
+++ b/backport-004-CVE-2023-52426.patch
@@ -0,0 +1,185 @@
+From 2b127c20b220b673cf52c6be8bef725bf04cbeaf Mon Sep 17 00:00:00 2001
+From: Sebastian Pipping
+Date: Thu, 26 Oct 2023 18:32:11 +0200
+Subject: [PATCH 07/17] lib: Make XML_GE==0 use self-references as entity
+ replacement text
+
+Reference: https://github.com//libexpat/libexpat/commit/2b127c20b220b673cf52c6be8bef725bf04cbeaf
+Conflict: NA
+
+---
+ lib/xmlparse.c | 81 +++++++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 72 insertions(+), 9 deletions(-)
+
+diff --git a/lib/xmlparse.c b/lib/xmlparse.c
+index db148b21..6a38dbe2 100644
+--- a/lib/xmlparse.c
++++ b/lib/xmlparse.c
+@@ -512,9 +512,13 @@ static enum XML_Error appendAttributeValue(XML_Parser parser, const ENCODING *,
+ static ATTRIBUTE_ID *getAttributeId(XML_Parser parser, const ENCODING *enc,
+ const char *start, const char *end);
+ static int setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *);
++#if XML_GE == 1
+ static enum XML_Error storeEntityValue(XML_Parser parser, const ENCODING *enc,
+ const char *start, const char *end,
+ enum XML_Account account);
++#else
++static enum XML_Error storeSelfEntityValue(XML_Parser parser, ENTITY *entity);
++#endif
+ static int reportProcessingInstruction(XML_Parser parser, const ENCODING *enc,
+ const char *start, const char *end);
+ static int reportComment(XML_Parser parser, const ENCODING *enc,
+@@ -5053,6 +5057,9 @@ 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
++ // This will store the given replacement text in
++ // parser->m_declEntity->textPtr.
+ enum XML_Error result
+ = storeEntityValue(parser, enc, s + enc->minBytesPerChar,
+ next - enc->minBytesPerChar, XML_ACCOUNT_NONE);
+@@ -5073,6 +5080,25 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
+ poolDiscard(&dtd->entityValuePool);
+ if (result != XML_ERROR_NONE)
+ return result;
++#else
++ // This will store "&entity123;" in parser->m_declEntity->textPtr
++ // to end up as "&entity123;" in the handler.
++ if (parser->m_declEntity != NULL) {
++ const enum XML_Error result
++ = storeSelfEntityValue(parser, parser->m_declEntity);
++ if (result != XML_ERROR_NONE)
++ return result;
++
++ if (parser->m_entityDeclHandler) {
++ *eventEndPP = s;
++ parser->m_entityDeclHandler(
++ parser->m_handlerArg, parser->m_declEntity->name,
++ parser->m_declEntity->is_param, parser->m_declEntity->textPtr,
++ parser->m_declEntity->textLen, parser->m_curBase, 0, 0, 0);
++ handleDefault = XML_FALSE;
++ }
++ }
++#endif
+ }
+ break;
+ case XML_ROLE_DOCTYPE_SYSTEM_ID:
+@@ -5131,6 +5157,16 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
+ }
+ break;
+ case XML_ROLE_ENTITY_COMPLETE:
++#if XML_GE == 0
++ // This will store "&entity123;" in entity->textPtr
++ // to end up as "&entity123;" in the handler.
++ if (parser->m_declEntity != NULL) {
++ const enum XML_Error result
++ = storeSelfEntityValue(parser, parser->m_declEntity);
++ if (result != XML_ERROR_NONE)
++ return result;
++ }
++#endif
+ if (dtd->keepProcessing && parser->m_declEntity
+ && parser->m_entityDeclHandler) {
+ *eventEndPP = s;
+@@ -6103,6 +6139,7 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
+ /* not reached */
+ }
+
++#if XML_GE == 1
+ static enum XML_Error
+ storeEntityValue(XML_Parser parser, const ENCODING *enc,
+ const char *entityTextPtr, const char *entityTextEnd,
+@@ -6110,12 +6147,12 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc,
+ DTD *const dtd = parser->m_dtd; /* save one level of indirection */
+ STRING_POOL *pool = &(dtd->entityValuePool);
+ enum XML_Error result = XML_ERROR_NONE;
+-#ifdef XML_DTD
++# ifdef XML_DTD
+ int oldInEntityValue = parser->m_prologState.inEntityValue;
+ parser->m_prologState.inEntityValue = 1;
+-#else
++# else
+ UNUSED_P(account);
+-#endif /* XML_DTD */
++# endif /* XML_DTD */
+ /* never return Null for the value argument in EntityDeclHandler,
+ since this would indicate an external entity; therefore we
+ have to make sure that entityValuePool.start is not null */
+@@ -6129,18 +6166,18 @@ 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 defined(XML_DTD) || XML_GE == 1
+ if (! accountingDiffTolerated(parser, tok, entityTextPtr, next, __LINE__,
+ account)) {
+ accountingOnAbort(parser);
+ result = XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
+ goto endEntityValue;
+ }
+-#endif
++# endif
+
+ switch (tok) {
+ case XML_TOK_PARAM_ENTITY_REF:
+-#ifdef XML_DTD
++# ifdef XML_DTD
+ if (parser->m_isParamEntity || enc != parser->m_encoding) {
+ const XML_Char *name;
+ ENTITY *entity;
+@@ -6202,7 +6239,7 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc,
+ }
+ break;
+ }
+-#endif /* XML_DTD */
++# endif /* XML_DTD */
+ /* In the internal subset, PE references are not legal
+ within markup declarations, e.g entity values in this case. */
+ parser->m_eventPtr = entityTextPtr;
+@@ -6283,12 +6320,38 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc,
+ entityTextPtr = next;
+ }
+ endEntityValue:
+-#ifdef XML_DTD
++# ifdef XML_DTD
+ parser->m_prologState.inEntityValue = oldInEntityValue;
+-#endif /* XML_DTD */
++# endif /* XML_DTD */
+ return result;
+ }
+
++#else /* XML_GE == 0 */
++
++static enum XML_Error
++storeSelfEntityValue(XML_Parser parser, ENTITY *entity) {
++ // This will store "&entity123;" in entity->textPtr
++ // to end up as "&entity123;" in the handler.
++ const char *const entity_start = "&";
++ const char *const entity_end = ";";
++
++ STRING_POOL *const pool = &(parser->m_dtd->entityValuePool);
++ if (! poolAppendString(pool, entity_start)
++ || ! poolAppendString(pool, entity->name)
++ || ! poolAppendString(pool, entity_end)) {
++ poolDiscard(pool);
++ return XML_ERROR_NO_MEMORY;
++ }
++
++ entity->textPtr = poolStart(pool);
++ entity->textLen = (int)(poolLength(pool));
++ poolFinish(pool);
++
++ return XML_ERROR_NONE;
++}
++
++#endif /* XML_GE == 0 */
++
+ static void FASTCALL
+ normalizeLines(XML_Char *s) {
+ XML_Char *p;
+--
+2.37.3.windows.1
+
+
+
diff --git a/backport-005-CVE-2023-52425.patch b/backport-005-CVE-2023-52425.patch
new file mode 100644
index 0000000000000000000000000000000000000000..506d542e5d0a0ea78225dca19311f985239ae255
--- /dev/null
+++ b/backport-005-CVE-2023-52425.patch
@@ -0,0 +1,32 @@
+From 1b728cf8376a166d21eae818dfa66c55b6209bc4 Mon Sep 17 00:00:00 2001
+From: Snild Dolkow
+Date: Thu, 24 Aug 2023 14:10:58 +0200
+Subject: [PATCH] tests: Set isFinal in test_column_number_after_parse
+
+Reference: https://github.com/libexpat/libexpat/pull/745/commits/2cee1061e2fec10633c3f02a961dabf95e85910a
+Conflict: remove basic_tests.c
+ change runtests.c
+
+Without this, parsing of the end tag may be deferred, yielding an
+unexpected column number.
+---
+ tests/runtests.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/tests/runtests.c b/tests/runtests.c
+index 8f1d11f0..9931d85e 100644
+--- a/tests/runtests.c
++++ b/tests/runtests.c
+@@ -1071,7 +1071,7 @@ START_TEST(test_column_number_after_parse) {
+ const char *text = "";
+ XML_Size colno;
+
+- if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_FALSE)
++ if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+ == XML_STATUS_ERROR)
+ xml_failure(g_parser);
+ colno = XML_GetCurrentColumnNumber(g_parser);
+--
+2.33.0
+
+
diff --git a/backport-006-CVE-2023-52425.patch b/backport-006-CVE-2023-52425.patch
new file mode 100644
index 0000000000000000000000000000000000000000..6f2107048574ec3245e961266f91401ed8f08335
--- /dev/null
+++ b/backport-006-CVE-2023-52425.patch
@@ -0,0 +1,39 @@
+From 25749ff3dad2216dfd7596498b592747a3d9305e Mon Sep 17 00:00:00 2001
+From: Snild Dolkow
+Date: Thu, 31 Aug 2023 16:14:38 +0200
+Subject: [PATCH] tests: Set isFinal=1 in line/column-number-after-error tests
+
+Reference: https://github.com/libexpat/libexpat/pull/745/commits/d4105a9080271a8d4996d2454f89be9992cb268a
+Conflict: remove basic_tests.c
+ change runtests.c
+
+---
+ tests/runtests.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/tests/runtests.c b/tests/runtests.c
+index 45ba5d59..d367271f 100644
+--- a/tests/runtests.c
++++ b/tests/runtests.c
+@@ -1139,7 +1139,7 @@ START_TEST(test_line_number_after_error) {
+ " \n"
+ " "; /* missing */
+ XML_Size lineno;
+- if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_FALSE)
++ if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+ != XML_STATUS_ERROR)
+ fail("Expected a parse error");
+
+@@ -1158,7 +1158,7 @@ START_TEST(test_column_number_after_error) {
+ " \n"
+ " "; /* missing */
+ XML_Size colno;
+- if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_FALSE)
++ if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+ != XML_STATUS_ERROR)
+ fail("Expected a parse error");
+
+--
+2.33.0
+
+
diff --git a/backport-007-CVE-2023-52425.patch b/backport-007-CVE-2023-52425.patch
new file mode 100644
index 0000000000000000000000000000000000000000..0712b746f04a1142277fc86f5e59b3cee9677500
--- /dev/null
+++ b/backport-007-CVE-2023-52425.patch
@@ -0,0 +1,368 @@
+From 1d784ef14933ee775fc20ba4435b8def6b70eae3 Mon Sep 17 00:00:00 2001
+From: caixiaomeng 00662745
+Date: Mon, 4 Mar 2024 11:00:25 +0800
+Subject: [PATCH] tests: Adapat test default current cases for 2.4.1
+
+Reference: https://github.com/libexpat/libexpat/commit/7474fe3d3f686a4d76f1df48c5db0eced295059b
+Conflict: yes
+
+---
+ tests/runtests.c | 303 +++++++++++++++++++++++++++++++----------
+ 1 file changed, 234 insertions(+), 69 deletions(-)
+
+diff --git a/tests/runtests.c b/tests/runtests.c
+index c0aa1773..e97a7c51 100644
+--- a/tests/runtests.c
++++ b/tests/runtests.c
+@@ -2536,34 +2536,75 @@ START_TEST(test_memory_allocation) {
+ }
+ END_TEST
+
++/* Handlers that record their arg and a single identifying character */
++
++struct handler_record_entry {
++ const char *name;
++ int arg;
++};
++struct handler_record_list {
++ int count;
++ struct handler_record_entry entries[50]; // arbitrary big-enough max count
++};
++
++# define handler_record_get(storage, index) \
++ _handler_record_get((storage), (index), __FILE__, __LINE__)
++
++# define assert_record_handler_called(storage, index, expected_name, \
++ expected_arg) \
++ do { \
++ const struct handler_record_entry *e \
++ = handler_record_get(storage, index); \
++ assert(strcmp(e->name, expected_name) == 0); \
++ assert(e->arg == (expected_arg)); \
++ } while (0)
++
++/* Handlers that record their function name and int arg. */
++
++static void
++record_call(struct handler_record_list *const rec, const char *funcname,
++ const int arg) {
++ const int max_entries = sizeof(rec->entries) / sizeof(rec->entries[0]);
++ assert(rec->count < max_entries);
++ struct handler_record_entry *const e = &rec->entries[rec->count++];
++ e->name = funcname;
++ e->arg = arg;
++}
++
+ static void XMLCALL
+ record_default_handler(void *userData, const XML_Char *s, int len) {
+ UNUSED_P(s);
+- UNUSED_P(len);
+- CharData_AppendXMLChars((CharData *)userData, XCS("D"), 1);
++ record_call((struct handler_record_list *)userData, __func__, len);
+ }
+
+ static void XMLCALL
+ record_cdata_handler(void *userData, const XML_Char *s, int len) {
+ UNUSED_P(s);
+- UNUSED_P(len);
+- CharData_AppendXMLChars((CharData *)userData, XCS("C"), 1);
++ record_call((struct handler_record_list *)userData, __func__, len);
+ XML_DefaultCurrent(g_parser);
+ }
+
+ static void XMLCALL
+ record_cdata_nodefault_handler(void *userData, const XML_Char *s, int len) {
+ UNUSED_P(s);
+- UNUSED_P(len);
+- CharData_AppendXMLChars((CharData *)userData, XCS("c"), 1);
++ record_call((struct handler_record_list *)userData, __func__, len);
+ }
+
+ static void XMLCALL
+ record_skip_handler(void *userData, const XML_Char *entityName,
+ int is_parameter_entity) {
+ UNUSED_P(entityName);
+- CharData_AppendXMLChars((CharData *)userData,
+- is_parameter_entity ? XCS("E") : XCS("e"), 1);
++ record_call((struct handler_record_list *)userData, __func__,
++ is_parameter_entity);
++}
++
++static const struct handler_record_entry *
++_handler_record_get(const struct handler_record_list *storage, int index,
++ const char *file, int line) {
++ if (storage->count <= index) {
++ fail("too few handler calls");
++ }
++ return &storage->entries[index];
+ }
+
+ /* Test XML_DefaultCurrent() passes handling on correctly */
+@@ -2573,78 +2614,202 @@ START_TEST(test_default_current) {
+ "\n"
+ "]>\n"
+ "&entity;";
+- CharData storage;
+
+- XML_SetDefaultHandler(g_parser, record_default_handler);
+- XML_SetCharacterDataHandler(g_parser, record_cdata_handler);
+- CharData_Init(&storage);
+- XML_SetUserData(g_parser, &storage);
+- if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+- == XML_STATUS_ERROR)
+- xml_failure(g_parser);
+- CharData_CheckXMLChars(&storage, XCS("DCDCDCDCDCDD"));
++ {
++ struct handler_record_list storage;
++ storage.count = 0;
++ XML_SetDefaultHandler(g_parser, record_default_handler);
++ XML_SetCharacterDataHandler(g_parser, record_cdata_handler);
++ XML_SetUserData(g_parser, &storage);
++ if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
++ == XML_STATUS_ERROR)
++ xml_failure(g_parser);
++ int i = 0;
++ assert_record_handler_called(&storage, i++, "record_default_handler", 5);
++ // we should have gotten one or more cdata callbacks, totaling 5 chars
++ int cdata_len_remaining = 5;
++ while (cdata_len_remaining > 0) {
++ const struct handler_record_entry *c_entry
++ = handler_record_get(&storage, i++);
++ assert(strcmp(c_entry->name, "record_cdata_handler") == 0);
++ assert(c_entry->arg > 0);
++ assert(c_entry->arg <= cdata_len_remaining);
++ cdata_len_remaining -= c_entry->arg;
++ // default handler must follow, with the exact same len argument.
++ assert_record_handler_called(&storage, i++, "record_default_handler",
++ c_entry->arg);
++ }
++ assert_record_handler_called(&storage, i++, "record_default_handler", 6);
++ assert(storage.count == i);
++ }
+
+ /* Again, without the defaulting */
+- XML_ParserReset(g_parser, NULL);
+- XML_SetDefaultHandler(g_parser, record_default_handler);
+- XML_SetCharacterDataHandler(g_parser, record_cdata_nodefault_handler);
+- CharData_Init(&storage);
+- XML_SetUserData(g_parser, &storage);
+- if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+- == XML_STATUS_ERROR)
+- xml_failure(g_parser);
+- CharData_CheckXMLChars(&storage, XCS("DcccccD"));
++ {
++ struct handler_record_list storage;
++ storage.count = 0;
++ XML_ParserReset(g_parser, NULL);
++ XML_SetDefaultHandler(g_parser, record_default_handler);
++ XML_SetCharacterDataHandler(g_parser, record_cdata_nodefault_handler);
++ XML_SetUserData(g_parser, &storage);
++ if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
++ == XML_STATUS_ERROR)
++ xml_failure(g_parser);
++ int i = 0;
++ assert_record_handler_called(&storage, i++, "record_default_handler", 5);
++ // we should have gotten one or more cdata callbacks, totaling 5 chars
++ int cdata_len_remaining = 5;
++ while (cdata_len_remaining > 0) {
++ const struct handler_record_entry *c_entry
++ = handler_record_get(&storage, i++);
++ assert(strcmp(c_entry->name, "record_cdata_nodefault_handler") == 0);
++ assert(c_entry->arg > 0);
++ assert(c_entry->arg <= cdata_len_remaining);
++ cdata_len_remaining -= c_entry->arg;
++ }
++ assert_record_handler_called(&storage, i++, "record_default_handler", 6);
++ assert(storage.count == i);
++ }
+
+ /* Now with an internal entity to complicate matters */
+- XML_ParserReset(g_parser, NULL);
+- XML_SetDefaultHandler(g_parser, record_default_handler);
+- XML_SetCharacterDataHandler(g_parser, record_cdata_handler);
+- CharData_Init(&storage);
+- XML_SetUserData(g_parser, &storage);
+- if (_XML_Parse_SINGLE_BYTES(g_parser, entity_text, (int)strlen(entity_text),
+- XML_TRUE)
+- == XML_STATUS_ERROR)
+- xml_failure(g_parser);
+- /* The default handler suppresses the entity */
+- CharData_CheckXMLChars(&storage, XCS("DDDDDDDDDDDDDDDDDDD"));
++ {
++ struct handler_record_list storage;
++ storage.count = 0;
++ XML_ParserReset(g_parser, NULL);
++ XML_SetDefaultHandler(g_parser, record_default_handler);
++ XML_SetCharacterDataHandler(g_parser, record_cdata_handler);
++ XML_SetUserData(g_parser, &storage);
++ if (_XML_Parse_SINGLE_BYTES(g_parser, entity_text, (int)strlen(entity_text),
++ XML_TRUE)
++ == XML_STATUS_ERROR)
++ xml_failure(g_parser);
++ /* The default handler suppresses the entity */
++ assert_record_handler_called(&storage, 0, "record_default_handler", 9);
++ assert_record_handler_called(&storage, 1, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 2, "record_default_handler", 3);
++ assert_record_handler_called(&storage, 3, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 4, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 5, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 6, "record_default_handler", 8);
++ assert_record_handler_called(&storage, 7, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 8, "record_default_handler", 6);
++ assert_record_handler_called(&storage, 9, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 10, "record_default_handler", 7);
++ assert_record_handler_called(&storage, 11, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 12, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 13, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 14, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 15, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 16, "record_default_handler", 5);
++ assert_record_handler_called(&storage, 17, "record_default_handler", 8);
++ assert_record_handler_called(&storage, 18, "record_default_handler", 6);
++ assert(storage.count == 19);
++ }
+
+ /* Again, with a skip handler */
+- XML_ParserReset(g_parser, NULL);
+- XML_SetDefaultHandler(g_parser, record_default_handler);
+- XML_SetCharacterDataHandler(g_parser, record_cdata_handler);
+- XML_SetSkippedEntityHandler(g_parser, record_skip_handler);
+- CharData_Init(&storage);
+- XML_SetUserData(g_parser, &storage);
+- if (_XML_Parse_SINGLE_BYTES(g_parser, entity_text, (int)strlen(entity_text),
+- XML_TRUE)
+- == XML_STATUS_ERROR)
+- xml_failure(g_parser);
+- /* The default handler suppresses the entity */
+- CharData_CheckXMLChars(&storage, XCS("DDDDDDDDDDDDDDDDDeD"));
++ {
++ struct handler_record_list storage;
++ storage.count = 0;
++ XML_ParserReset(g_parser, NULL);
++ XML_SetDefaultHandler(g_parser, record_default_handler);
++ XML_SetCharacterDataHandler(g_parser, record_cdata_handler);
++ XML_SetSkippedEntityHandler(g_parser, record_skip_handler);
++ XML_SetUserData(g_parser, &storage);
++ if (_XML_Parse_SINGLE_BYTES(g_parser, entity_text, (int)strlen(entity_text),
++ XML_TRUE)
++ == XML_STATUS_ERROR)
++ xml_failure(g_parser);
++ /* The default handler suppresses the entity */
++ assert_record_handler_called(&storage, 0, "record_default_handler", 9);
++ assert_record_handler_called(&storage, 1, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 2, "record_default_handler", 3);
++ assert_record_handler_called(&storage, 3, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 4, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 5, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 6, "record_default_handler", 8);
++ assert_record_handler_called(&storage, 7, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 8, "record_default_handler", 6);
++ assert_record_handler_called(&storage, 9, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 10, "record_default_handler", 7);
++ assert_record_handler_called(&storage, 11, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 12, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 13, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 14, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 15, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 16, "record_default_handler", 5);
++ assert_record_handler_called(&storage, 17, "record_skip_handler", 0);
++ assert_record_handler_called(&storage, 18, "record_default_handler", 6);
++ assert(storage.count == 19);
++ }
+
+ /* This time, allow the entity through */
+- XML_ParserReset(g_parser, NULL);
+- XML_SetDefaultHandlerExpand(g_parser, record_default_handler);
+- XML_SetCharacterDataHandler(g_parser, record_cdata_handler);
+- CharData_Init(&storage);
+- XML_SetUserData(g_parser, &storage);
+- if (_XML_Parse_SINGLE_BYTES(g_parser, entity_text, (int)strlen(entity_text),
+- XML_TRUE)
+- == XML_STATUS_ERROR)
+- xml_failure(g_parser);
+- CharData_CheckXMLChars(&storage, XCS("DDDDDDDDDDDDDDDDDCDD"));
++ {
++ struct handler_record_list storage;
++ storage.count = 0;
++ XML_ParserReset(g_parser, NULL);
++ XML_SetDefaultHandlerExpand(g_parser, record_default_handler);
++ XML_SetCharacterDataHandler(g_parser, record_cdata_handler);
++ XML_SetUserData(g_parser, &storage);
++ if (_XML_Parse_SINGLE_BYTES(g_parser, entity_text, (int)strlen(entity_text),
++ XML_TRUE)
++ == XML_STATUS_ERROR)
++ xml_failure(g_parser);
++ assert_record_handler_called(&storage, 0, "record_default_handler", 9);
++ assert_record_handler_called(&storage, 1, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 2, "record_default_handler", 3);
++ assert_record_handler_called(&storage, 3, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 4, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 5, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 6, "record_default_handler", 8);
++ assert_record_handler_called(&storage, 7, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 8, "record_default_handler", 6);
++ assert_record_handler_called(&storage, 9, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 10, "record_default_handler", 7);
++ assert_record_handler_called(&storage, 11, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 12, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 13, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 14, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 15, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 16, "record_default_handler", 5);
++ assert_record_handler_called(&storage, 17, "record_cdata_handler", 1);
++ assert_record_handler_called(&storage, 18, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 19, "record_default_handler", 6);
++ assert(storage.count == 20);
++ }
+
+ /* Finally, without passing the cdata to the default handler */
+- XML_ParserReset(g_parser, NULL);
+- XML_SetDefaultHandlerExpand(g_parser, record_default_handler);
+- XML_SetCharacterDataHandler(g_parser, record_cdata_nodefault_handler);
+- CharData_Init(&storage);
+- XML_SetUserData(g_parser, &storage);
+- if (_XML_Parse_SINGLE_BYTES(g_parser, entity_text, (int)strlen(entity_text),
+- XML_TRUE)
+- == XML_STATUS_ERROR)
+- xml_failure(g_parser);
+- CharData_CheckXMLChars(&storage, XCS("DDDDDDDDDDDDDDDDDcD"));
++ {
++ struct handler_record_list storage;
++ storage.count = 0;
++ XML_ParserReset(g_parser, NULL);
++ XML_SetDefaultHandlerExpand(g_parser, record_default_handler);
++ XML_SetCharacterDataHandler(g_parser, record_cdata_nodefault_handler);
++ XML_SetUserData(g_parser, &storage);
++ if (_XML_Parse_SINGLE_BYTES(g_parser, entity_text, (int)strlen(entity_text),
++ XML_TRUE)
++ == XML_STATUS_ERROR)
++ xml_failure(g_parser);
++ assert_record_handler_called(&storage, 0, "record_default_handler", 9);
++ assert_record_handler_called(&storage, 1, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 2, "record_default_handler", 3);
++ assert_record_handler_called(&storage, 3, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 4, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 5, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 6, "record_default_handler", 8);
++ assert_record_handler_called(&storage, 7, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 8, "record_default_handler", 6);
++ assert_record_handler_called(&storage, 9, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 10, "record_default_handler", 7);
++ assert_record_handler_called(&storage, 11, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 12, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 13, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 14, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 15, "record_default_handler", 1);
++ assert_record_handler_called(&storage, 16, "record_default_handler", 5);
++ assert_record_handler_called(&storage, 17, "record_cdata_nodefault_handler",
++ 1);
++ assert_record_handler_called(&storage, 18, "record_default_handler", 6);
++ assert(storage.count == 19);
++ }
+ }
+ END_TEST
+
+--
+2.33.0
+
+
diff --git a/backport-008-CVE-2023-52425.patch b/backport-008-CVE-2023-52425.patch
new file mode 100644
index 0000000000000000000000000000000000000000..0a799fc07ddd796fd1a4405fb57021c44231d8f0
--- /dev/null
+++ b/backport-008-CVE-2023-52425.patch
@@ -0,0 +1,54 @@
+From 7f54667c59c5a884beba5dce17003715d7cbaffa Mon Sep 17 00:00:00 2001
+From: Snild Dolkow
+Date: Mon, 18 Sep 2023 20:32:55 +0200
+Subject: [PATCH] tests: Run both with and without partial token heuristic
+
+If we always run with the heuristic enabled, it may hide some bugs by
+grouping up input into bigger parse attempts.
+
+CI-fighting-assistance-by: Sebastian Pipping
+---
+ lib/internal.h | 2 ++
+ lib/xmlparse.c | 5 ++++-
+ 2 files changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/lib/internal.h b/lib/internal.h
+index 444eba0f..dda42d88 100644
+--- a/lib/internal.h
++++ b/lib/internal.h
+@@ -158,6 +158,8 @@ unsigned long long testingAccountingGetCountBytesIndirect(XML_Parser parser);
+ const char *unsignedCharToPrintable(unsigned char c);
+ #endif
+
++extern XML_Bool g_reparseDeferralEnabledDefault; // written ONLY in runtests.c
++
+ #ifdef __cplusplus
+ }
+ #endif
+diff --git a/lib/xmlparse.c b/lib/xmlparse.c
+index 32df1eb9..e30e76aa 100644
+--- a/lib/xmlparse.c
++++ b/lib/xmlparse.c
+@@ -599,6 +599,8 @@ static unsigned long getDebugLevel(const char *variableName,
+ ? 0 \
+ : ((*((pool)->ptr)++ = c), 1))
+
++XML_Bool g_reparseDeferralEnabledDefault = XML_TRUE; // write ONLY in runtests.c
++
+ struct XML_ParserStruct {
+ /* The first member must be m_userData so that the XML_GetUserData
+ macro works. */
+@@ -951,7 +953,8 @@ callProcessor(XML_Parser parser, const char *start, const char *end,
+ const char **endPtr) {
+ const size_t have_now = EXPAT_SAFE_PTR_DIFF(end, start);
+
+- if (! parser->m_parsingStatus.finalBuffer) {
++ if (g_reparseDeferralEnabledDefault
++ && ! parser->m_parsingStatus.finalBuffer) {
+ // Heuristic: don't try to parse a partial token again until the amount of
+ // available data has increased significantly.
+ const size_t had_before = parser->m_partialTokenBytesBefore;
+--
+2.33.0
+
+
diff --git a/backport-009-CVE-2023-52425.patch b/backport-009-CVE-2023-52425.patch
new file mode 100644
index 0000000000000000000000000000000000000000..1583300a1b94f1fef5806757b672c4232708def5
--- /dev/null
+++ b/backport-009-CVE-2023-52425.patch
@@ -0,0 +1,122 @@
+From 3194d762dc6a80bca5d374fe5084888386fbadcd Mon Sep 17 00:00:00 2001
+From: Snild Dolkow
+Date: Mon, 11 Sep 2023 15:31:24 +0200
+Subject: [PATCH] Add app setting for enabling/disabling reparse heuristic
+
+Suggested-by: Sebastian Pipping
+CI-fighting-assistance-by: Sebastian Pipping
+---
+ doc/reference.html | 24 +++++++++++++++++++++++-
+ lib/expat.h | 4 ++++
+ lib/xmlparse.c | 13 ++++++++++++-
+ 3 files changed, 39 insertions(+), 2 deletions(-)
+
+diff --git a/doc/reference.html b/doc/reference.html
+index 309cb241..1ded3bbe 100644
+--- a/doc/reference.html
++++ b/doc/reference.html
+@@ -149,10 +149,11 @@ interface.
+
+
+
+- Billion Laughs Attack Protection
++ Attack Protection
+
+
+ Miscellaneous Functions
+@@ -2172,6 +2173,27 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold(XML_Parser p,
+
+
+
++XML_SetReparseDeferralEnabled
++
++/* Added in Expat 2.6.0. */
++XML_Bool XMLCALL
++XML_SetReparseDeferralEnabled(XML_Parser parser, XML_Bool enabled);
++
++
++
++ Large tokens may require many parse calls before enough data is available for Expat to parse it in full.
++ If Expat retried parsing the token on every parse call, parsing could take quadratic time.
++ To avoid this, Expat only retries once a significant amount of new data is available.
++ This function allows disabling this behavior.
++
++
++ The enabled
argument should be XML_TRUE
or XML_FALSE
.
++
++
++ Returns XML_TRUE
on success, and XML_FALSE
on error.
++
++
++
+
+
+ The functions in this section either obtain state information from
+diff --git a/lib/expat.h b/lib/expat.h
+index b7d6d354..a4033742 100644
+--- a/lib/expat.h
++++ b/lib/expat.h
+@@ -1036,6 +1036,10 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold(
+ XML_Parser parser, unsigned long long activationThresholdBytes);
+ #endif
+
++/* Added in Expat 2.6.0. */
++XMLPARSEAPI(XML_Bool)
++XML_SetReparseDeferralEnabled(XML_Parser parser, XML_Bool enabled);
++
+ /* Expat follows the semantic versioning convention.
+ See http://semver.org.
+ */
+diff --git a/lib/xmlparse.c b/lib/xmlparse.c
+index e30e76aa..d95b054b 100644
+--- a/lib/xmlparse.c
++++ b/lib/xmlparse.c
+@@ -617,6 +617,7 @@ struct XML_ParserStruct {
+ XML_Index m_parseEndByteIndex;
+ const char *m_parseEndPtr;
+ size_t m_partialTokenBytesBefore; /* used in heuristic to avoid O(n^2) */
++ XML_Bool m_reparseDeferralEnabled;
+ XML_Char *m_dataBuf;
+ XML_Char *m_dataBufEnd;
+ XML_StartElementHandler m_startElementHandler;
+@@ -953,7 +954,7 @@ callProcessor(XML_Parser parser, const char *start, const char *end,
+ const char **endPtr) {
+ const size_t have_now = EXPAT_SAFE_PTR_DIFF(end, start);
+
+- if (g_reparseDeferralEnabledDefault
++ if (parser->m_reparseDeferralEnabled
+ && ! parser->m_parsingStatus.finalBuffer) {
+ // Heuristic: don't try to parse a partial token again until the amount of
+ // available data has increased significantly.
+@@ -1149,6 +1150,7 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) {
+ parser->m_parseEndByteIndex = 0;
+ parser->m_parseEndPtr = NULL;
+ parser->m_partialTokenBytesBefore = 0;
++ parser->m_reparseDeferralEnabled = g_reparseDeferralEnabledDefault;
+ parser->m_declElementType = NULL;
+ parser->m_declAttributeId = NULL;
+ parser->m_declEntity = NULL;
+@@ -2568,6 +2570,15 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold(
+ }
+ #endif /* defined(XML_DTD) || XML_GE == 1 */
+
++XML_Bool XMLCALL
++XML_SetReparseDeferralEnabled(XML_Parser parser, XML_Bool enabled) {
++ if (parser != NULL && (enabled == XML_TRUE || enabled == XML_FALSE)) {
++ parser->m_reparseDeferralEnabled = enabled;
++ return XML_TRUE;
++ }
++ return XML_FALSE;
++}
++
+ /* Initially tag->rawName always points into the parse buffer;
+ for those TAG instances opened while the current parse buffer was
+ processed, and not yet closed, we need to store tag->rawName in a more
+--
+2.33.0
+
+
diff --git a/backport-CVE-2024-28757-001.patch b/backport-CVE-2024-28757-001.patch
new file mode 100644
index 0000000000000000000000000000000000000000..61fab2594974cd59baeee99191a20940e054d468
--- /dev/null
+++ b/backport-CVE-2024-28757-001.patch
@@ -0,0 +1,58 @@
+From 1d50b80cf31de87750103656f6eb693746854aa8 Mon Sep 17 00:00:00 2001
+From: Sebastian Pipping
+Date: Mon, 4 Mar 2024 23:49:06 +0100
+Subject: [PATCH] lib/xmlparse.c: Detect billion laughs attack with isolated
+ external parser
+
+When parsing DTD content with code like ..
+
+ XML_Parser parser = XML_ParserCreate(NULL);
+ XML_Parser ext_parser = XML_ExternalEntityParserCreate(parser, NULL, NULL);
+ enum XML_Status status = XML_Parse(ext_parser, doc, (int)strlen(doc), XML_TRUE);
+
+.. there are 0 bytes accounted as direct input and all input from accounted
+as indirect input. Now function accountingGetCurrentAmplification cannot calculate
+the current amplification ratio as "(direct + indirect) / direct", and it did refuse
+to divide by 0 as one would expect, but it returned 1.0 for this case to indicate
+no amplification over direct input. As a result, billion laughs attacks from
+DTD-only input were not detected with this isolated way of using an external parser.
+
+The new approach is to assume direct input of length not 0 but 22 -- derived from
+ghost input "", the shortest possible way to include an external
+DTD --, and do the usual "(direct + indirect) / direct" math with "direct := 22".
+
+GitHub issue #839 has more details on this issue and its origin in ClusterFuzz
+finding 66812.
+---
+ expat/lib/xmlparse.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/lib/xmlparse.c b/expat/lib/xmlparse.c
+index b884d82b..d44baa68 100644
+--- a/lib/xmlparse.c
++++ b/lib/xmlparse.c
+@@ -7787,6 +7787,8 @@ copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) {
+
+ static float
+ accountingGetCurrentAmplification(XML_Parser rootParser) {
++ // 1.........1.........12 => 22
++ const size_t lenOfShortestInclude = sizeof("") - 1;
+ const XmlBigCount countBytesOutput
+ = rootParser->m_accounting.countBytesDirect
+ + rootParser->m_accounting.countBytesIndirect;
+@@ -7794,7 +7796,9 @@ accountingGetCurrentAmplification(XML_Parser rootParser) {
+ = rootParser->m_accounting.countBytesDirect
+ ? (countBytesOutput
+ / (float)(rootParser->m_accounting.countBytesDirect))
+- : 1.0f;
++ : ((lenOfShortestInclude
++ + rootParser->m_accounting.countBytesIndirect)
++ / (float)lenOfShortestInclude);
+ assert(! rootParser->m_parentParser);
+ return amplificationFactor;
+ }
+--
+2.33.0
+
+
+
diff --git a/backport-CVE-2024-28757-002.patch b/backport-CVE-2024-28757-002.patch
new file mode 100644
index 0000000000000000000000000000000000000000..5d4e23fa2f444f3dc61de2c6a020c319a8f3d624
--- /dev/null
+++ b/backport-CVE-2024-28757-002.patch
@@ -0,0 +1,27 @@
+From a4c86a395ee447c59175c762af3d17f7107b2261 Mon Sep 17 00:00:00 2001
+From: Sebastian Pipping
+Date: Sun, 3 Mar 2024 02:19:58 +0100
+Subject: [PATCH] lib/xmlparse.c: Reject directly recursive parameter entities
+
+---
+ expat/lib/xmlparse.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/lib/xmlparse.c b/expat/lib/xmlparse.c
+index b884d82b..8e667fcb 100644
+--- a/lib/xmlparse.c
++++ b/lib/xmlparse.c
+@@ -6240,7 +6240,7 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc,
+ dtd->keepProcessing = dtd->standalone;
+ goto endEntityValue;
+ }
+- if (entity->open) {
++ if (entity->open || (entity == parser->m_declEntity)) {
+ if (enc == parser->m_encoding)
+ parser->m_eventPtr = entityTextPtr;
+ result = XML_ERROR_RECURSIVE_ENTITY_REF;
+--
+2.33.0
+
+
+
diff --git a/backport-CVE-2024-28757-003.patch b/backport-CVE-2024-28757-003.patch
new file mode 100644
index 0000000000000000000000000000000000000000..6aba854bb0d5ae599a5242d84cc0eadc2f27e01c
--- /dev/null
+++ b/backport-CVE-2024-28757-003.patch
@@ -0,0 +1,86 @@
+From a1c0c15892c89c69d64ca8e5d0322d3f37b08307 Mon Sep 17 00:00:00 2001
+From: caixiaomeng 00662745
+Date: Thu, 21 Mar 2024 20:29:49 +0800
+Subject: [PATCH] tests-Cover-amplification-tracking-for-isolated-exte
+
+---
+ tests/runtests.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 55 insertions(+)
+
+diff --git a/tests/runtests.c b/tests/runtests.c
+index 915fa52..1d83237 100644
+--- a/tests/runtests.c
++++ b/tests/runtests.c
+@@ -12094,6 +12094,59 @@ START_TEST(test_helper_unsigned_char_to_printable) {
+ END_TEST
+ #endif // defined(XML_DTD)
+
++START_TEST(test_amplification_isolated_external_parser) {
++ // NOTE: Length 44 is precisely twice the length of ""
++ // (22) that is used in function accountingGetCurrentAmplification in
++ // xmlparse.c.
++ // 1.........1.........1.........1.........1..4 => 44
++ const char doc[] = "";
++ const int docLen = (int)sizeof(doc) - 1;
++ const float maximumToleratedAmplification = 2.0f;
++
++ struct TestCase {
++ int offsetOfThreshold;
++ enum XML_Status expectedStatus;
++ };
++
++ struct TestCase cases[] = {
++ {-2, XML_STATUS_ERROR}, {-1, XML_STATUS_ERROR}, {0, XML_STATUS_ERROR},
++ {+1, XML_STATUS_OK}, {+2, XML_STATUS_OK},
++ };
++
++ for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
++ const int offsetOfThreshold = cases[i].offsetOfThreshold;
++ const enum XML_Status expectedStatus = cases[i].expectedStatus;
++ const unsigned long long activationThresholdBytes
++ = docLen + offsetOfThreshold;
++
++ XML_Parser parser = XML_ParserCreate(NULL);
++ assert(parser != NULL);
++
++ assert(XML_SetBillionLaughsAttackProtectionMaximumAmplification(
++ parser, maximumToleratedAmplification)
++ == XML_TRUE);
++ assert(XML_SetBillionLaughsAttackProtectionActivationThreshold(
++ parser, activationThresholdBytes)
++ == XML_TRUE);
++
++ XML_Parser ext_parser = XML_ExternalEntityParserCreate(parser, NULL, NULL);
++ assert(ext_parser != NULL);
++
++ const enum XML_Status actualStatus
++ = _XML_Parse_SINGLE_BYTES(ext_parser, doc, docLen, XML_TRUE);
++
++ assert(actualStatus == expectedStatus);
++ if (actualStatus != XML_STATUS_OK) {
++ assert(XML_GetErrorCode(ext_parser)
++ == XML_ERROR_AMPLIFICATION_LIMIT_BREACH);
++ }
++
++ XML_ParserFree(ext_parser);
++ XML_ParserFree(parser);
++ }
++}
++END_TEST
++
+ static Suite *
+ make_suite(void) {
+ Suite *s = suite_create("basic");
+@@ -12334,6 +12387,8 @@ make_suite(void) {
+ tcase_add_test(tc_basic, test_bad_notation);
+ tcase_add_test(tc_basic, test_default_doctype_handler);
+ tcase_add_test(tc_basic, test_empty_element_abort);
++ tcase_add_test__ifdef_xml_dtd(tc_basic,
++ test_amplification_isolated_external_parser);
+ tcase_add_test__ifdef_xml_dtd(tc_basic,
+ test_pool_integrity_with_unfinished_attr);
+ tcase_add_test(tc_basic, test_nested_entity_suspend);
+--
+2.33.0
+
+
diff --git a/backport-CVE-2024-28757-004.patch b/backport-CVE-2024-28757-004.patch
new file mode 100644
index 0000000000000000000000000000000000000000..c94ce8866c4834a5b0fa2449b7aa9528ba034c8b
--- /dev/null
+++ b/backport-CVE-2024-28757-004.patch
@@ -0,0 +1,75 @@
+From cce551e4e1f75b8a3e8dd087bfd6685a2c431cd0 Mon Sep 17 00:00:00 2001
+From: caixiaomeng 00662745
+Date: Thu, 21 Mar 2024 20:40:48 +0800
+Subject: [PATCH] tests-Cover-rejection-of-direct-parameter-entity-rec
+
+---
+ tests/runtests.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 44 insertions(+)
+
+diff --git a/tests/runtests.c b/tests/runtests.c
+index 1d83237..03350f4 100644
+--- a/tests/runtests.c
++++ b/tests/runtests.c
+@@ -12147,6 +12147,48 @@ START_TEST(test_amplification_isolated_external_parser) {
+ }
+ END_TEST
+
++START_TEST(test_recursive_external_parameter_entity_2) {
++ struct TestCase {
++ const char *doc;
++ enum XML_Status expectedStatus;
++ };
++
++ struct TestCase cases[] = {
++ {"", XML_STATUS_ERROR},
++ {""
++ "",
++ XML_STATUS_ERROR},
++ {""
++ "",
++ XML_STATUS_OK},
++ {"", XML_STATUS_OK},
++ };
++
++ for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
++ const char *const doc = cases[i].doc;
++ const enum XML_Status expectedStatus = cases[i].expectedStatus;
++
++ XML_Parser parser = XML_ParserCreate(NULL);
++ assert(parser != NULL);
++
++ XML_Parser ext_parser = XML_ExternalEntityParserCreate(parser, NULL, NULL);
++ assert(ext_parser != NULL);
++
++ const enum XML_Status actualStatus
++ = _XML_Parse_SINGLE_BYTES(ext_parser, doc, (int)strlen(doc), XML_TRUE);
++
++ assert(actualStatus == expectedStatus);
++ if (actualStatus != XML_STATUS_OK) {
++ assert(XML_GetErrorCode(ext_parser)
++ == XML_ERROR_RECURSIVE_ENTITY_REF);
++ }
++
++ XML_ParserFree(ext_parser);
++ XML_ParserFree(parser);
++ }
++}
++END_TEST
++
+ static Suite *
+ make_suite(void) {
+ Suite *s = suite_create("basic");
+@@ -12389,6 +12431,8 @@ make_suite(void) {
+ tcase_add_test(tc_basic, test_empty_element_abort);
+ tcase_add_test__ifdef_xml_dtd(tc_basic,
+ test_amplification_isolated_external_parser);
++ tcase_add_test__ifdef_xml_dtd(tc_basic,
++ test_recursive_external_parameter_entity_2);
+ tcase_add_test__ifdef_xml_dtd(tc_basic,
+ test_pool_integrity_with_unfinished_attr);
+ tcase_add_test(tc_basic, test_nested_entity_suspend);
+--
+2.33.0
+
+
diff --git a/backport-CVE-2024-45491.patch b/backport-CVE-2024-45491.patch
new file mode 100644
index 0000000000000000000000000000000000000000..b4b67ffd11f6cfd0678a7fa1e22616d94ab2fd75
--- /dev/null
+++ b/backport-CVE-2024-45491.patch
@@ -0,0 +1,31 @@
+From 8e439a9947e9dc80a395c0c7456545d8d9d9e421 Mon Sep 17 00:00:00 2001
+From: Sebastian Pipping
+Date: Mon, 19 Aug 2024 22:34:13 +0200
+Subject: [PATCH] lib: Detect integer overflow in dtdCopy
+
+Reported by TaiYou
+---
+ expat/lib/xmlparse.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/lib/xmlparse.c b/expat/lib/xmlparse.c
+index 91682c188..e2327bdcf 100644
+--- a/lib/xmlparse.c
++++ b/lib/xmlparse.c
+@@ -7016,6 +7016,16 @@ dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd,
+ if (! newE)
+ return 0;
+ if (oldE->nDefaultAtts) {
++ /* Detect and prevent integer overflow.
++ * The preprocessor guard addresses the "always false" warning
++ * from -Wtype-limits on platforms where
++ * sizeof(int) < sizeof(size_t), e.g. on x86_64. */
++#if UINT_MAX >= SIZE_MAX
++ if ((size_t)oldE->nDefaultAtts
++ > ((size_t)(-1) / sizeof(DEFAULT_ATTRIBUTE))) {
++ return 0;
++ }
++#endif
+ newE->defaultAtts
+ = ms->malloc_fcn(oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE));
+ if (! newE->defaultAtts) {
diff --git a/backport-CVE-2024-45492.patch b/backport-CVE-2024-45492.patch
new file mode 100644
index 0000000000000000000000000000000000000000..3f46bec68aa6bb8c876ea1587e8a7461dbae9352
--- /dev/null
+++ b/backport-CVE-2024-45492.patch
@@ -0,0 +1,30 @@
+From 9bf0f2c16ee86f644dd1432507edff94c08dc232 Mon Sep 17 00:00:00 2001
+From: Sebastian Pipping
+Date: Mon, 19 Aug 2024 22:37:16 +0200
+Subject: [PATCH] lib: Detect integer overflow in function nextScaffoldPart
+
+Reported by TaiYou
+---
+ expat/lib/xmlparse.c | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/lib/xmlparse.c b/lib/xmlparse.c
+index 91682c188..f737575ea 100644
+--- a/lib/xmlparse.c
++++ b/lib/xmlparse.c
+@@ -7558,6 +7558,15 @@ nextScaffoldPart(XML_Parser parser) {
+ int next;
+
+ if (! dtd->scaffIndex) {
++ /* Detect and prevent integer overflow.
++ * The preprocessor guard addresses the "always false" warning
++ * from -Wtype-limits on platforms where
++ * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
++#if UINT_MAX >= SIZE_MAX
++ if (parser->m_groupSize > ((size_t)(-1) / sizeof(int))) {
++ return -1;
++ }
++#endif
+ dtd->scaffIndex = (int *)MALLOC(parser, parser->m_groupSize * sizeof(int));
+ if (! dtd->scaffIndex)
+ return -1;
diff --git a/expat-2.5.0.tar.gz b/expat-2.5.0.tar.gz
new file mode 100644
index 0000000000000000000000000000000000000000..128f484475908d21d5d708d8b450aba750cb1c06
Binary files /dev/null and b/expat-2.5.0.tar.gz differ
diff --git a/expat-2.6.0.tar.gz b/expat-2.6.0.tar.gz
deleted file mode 100644
index 1982b965f3e11a4e8bb2a40ec9a5b5e96ce81f94..0000000000000000000000000000000000000000
Binary files a/expat-2.6.0.tar.gz and /dev/null differ
diff --git a/expat.spec b/expat.spec
index 2f9fc256f702de46ce85252dfc84ef573b28d87f..d60238e375ab6def6a5915fbe39fbb159e399b22 100644
--- a/expat.spec
+++ b/expat.spec
@@ -1,12 +1,35 @@
%define Rversion %(echo %{version} | sed -e 's/\\./_/g' -e 's/^/R_/')
Name: expat
-Version: 2.6.0
-Release: 1
+Version: 2.5.0
+Release: 5
Summary: An XML parser library
License: MIT
URL: https://libexpat.github.io/
Source0: https://github.com/libexpat/libexpat/releases/download/%{Rversion}/expat-%{version}.tar.gz
+Patch01: backport-CVE-2024-28757-001.patch
+Patch02: backport-CVE-2024-28757-002.patch
+Patch03: backport-CVE-2024-28757-003.patch
+Patch04: backport-CVE-2024-28757-004.patch
+Patch05: backport-001-CVE-2023-52426.patch
+Patch06: backport-002-CVE-2023-52426.patch
+Patch07: backport-003-CVE-2023-52426.patch
+Patch08: backport-004-CVE-2023-52426.patch
+Patch09: backport-001-CVE-2023-52425.patch
+Patch10: backport-002-CVE-2023-52425.patch
+Patch11: backport-003-CVE-2023-52425.patch
+Patch12: backport-004-CVE-2023-52425.patch
+Patch13: backport-005-CVE-2023-52425.patch
+Patch14: backport-006-CVE-2023-52425.patch
+Patch15: backport-007-CVE-2023-52425.patch
+Patch16: backport-008-CVE-2023-52425.patch
+Patch17: backport-009-CVE-2023-52425.patch
+Patch18: backport-001-CVE-2024-45490.patch
+Patch19: backport-002-CVE-2024-45490.patch
+Patch20: backport-003-CVE-2024-45490.patch
+Patch21: backport-CVE-2024-45491.patch
+Patch22: backport-CVE-2024-45492.patch
+
BuildRequires: sed,autoconf,automake,gcc-c++,libtool,xmlto
%description
@@ -31,36 +54,40 @@ autoreconf -fiv
%make_build
%install
-%makeinstall
+%make_install
find %{buildroot} -type f -name changelog -delete
%check
-make check
-
-%ldconfig_scriptlets
+%make_build check
%files
-%defattr(-,root,root)
%license COPYING AUTHORS
%{_bindir}/*
%{_libdir}/libexpat.so.1*
%exclude %{_docdir}/%{name}/AUTHORS
%files devel
-%defattr(-,root,root)
%{_includedir}/*
%{_libdir}/{libexpat.*a,libexpat.so}
%{_libdir}/cmake/expat-%{version}
%{_libdir}/pkgconfig/expat.pc
%files help
-%defattr(-,root,root)
%doc README.md
%{_mandir}/man1/*
%changelog
-* Wed Feb 21 2024 liweigang - 2.6.0-1
-- expat update to 2.6.0
+* Wed Sep 04 2024 Funda Wang - 2.5.0-5
+- fix CVE-2024-45491, CVE-2024-45492
+
+* Mon Sep 2 2024 caixiaomeng - 2.5.0-4
+- fix CVE-2024-45490
+
+* Wed Jun 12 2024 wangjiang - 2.5.0-3
+- fix CVE-2023-52425
+
+* Mon Apr 8 2024 caixiaomeng - 2.5.0-2
+- fix cve-2024-28757 and cve-2023-52426
* Tue Jan 31 2023 zhoupengcheng - 2.5.0-1
- expat update to 2.5.0