summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMeenali Gupta <meenali.gupta@windriver.com>2024-03-29 11:12:02 +0000
committerSteve Sakoman <steve@sakoman.com>2024-04-02 08:02:41 -1000
commit1bdcd10930a2998f6bbe56b3ba4c9b6c91203b39 (patch)
tree7584fbd380338a91264fd9e5c710a0839dad0d0e
parente8feba36e09aefffcafcebc85ec75abb5b97b3eb (diff)
downloadopenembedded-core-1bdcd10930a2998f6bbe56b3ba4c9b6c91203b39.tar.gz
expat: fix CVE-2023-52425
libexpat through 2.5.0 allows a denial of service (resource consumption) because many full reparsings are required in the case of a large token for which multiple buffer fills are needed. References: https://nvd.nist.gov/vuln/detail/CVE-2023-52425 Changes related to test directory are not included as most of the files are not present and are introduced in the later version. Signed-off-by: Meenali Gupta <meenali.gupta@windriver.com> Signed-off-by: Steve Sakoman <steve@sakoman.com>
-rw-r--r--meta/recipes-core/expat/expat/CVE-2023-52425-0001.patch40
-rw-r--r--meta/recipes-core/expat/expat/CVE-2023-52425-0002.patch87
-rw-r--r--meta/recipes-core/expat/expat/CVE-2023-52425-0003.patch222
-rw-r--r--meta/recipes-core/expat/expat/CVE-2023-52425-0004.patch42
-rw-r--r--meta/recipes-core/expat/expat/CVE-2023-52425-0005.patch69
-rw-r--r--meta/recipes-core/expat/expat/CVE-2023-52425-0006.patch67
-rw-r--r--meta/recipes-core/expat/expat/CVE-2023-52425-0007.patch159
-rw-r--r--meta/recipes-core/expat/expat/CVE-2023-52425-0008.patch95
-rw-r--r--meta/recipes-core/expat/expat/CVE-2023-52425-0009.patch52
-rw-r--r--meta/recipes-core/expat/expat/CVE-2023-52425-0010.patch111
-rw-r--r--meta/recipes-core/expat/expat/CVE-2023-52425-0011.patch89
-rw-r--r--meta/recipes-core/expat/expat/CVE-2023-52425-0012.patch87
-rw-r--r--meta/recipes-core/expat/expat_2.5.0.bb12
13 files changed, 1132 insertions, 0 deletions
diff --git a/meta/recipes-core/expat/expat/CVE-2023-52425-0001.patch b/meta/recipes-core/expat/expat/CVE-2023-52425-0001.patch
new file mode 100644
index 0000000000..4e21ade018
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2023-52425-0001.patch
@@ -0,0 +1,40 @@
+From d5b02e96ab95d2a7ae0aea72d00054b9d036d76d Mon Sep 17 00:00:00 2001
+From: Sebastian Pipping <sebastian@pipping.org>
+Date: Thu, 9 Nov 2023 19:28:05 +0100
+Subject: [PATCH] xmlwf: Document argument "-q"
+
+Rebased-and-adapted-by: Snild Dolkow <snild@sony.com>
+
+CVE: CVE-2023-52425
+
+Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/d5b02e96ab95d2a7ae0aea72d00054b9d036d76d]
+
+Signed-off-by: Meenali Gupta <meenali.gupta@windriver.com>
+---
+ doc/xmlwf.xml | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/doc/xmlwf.xml b/doc/xmlwf.xml
+index 9603abf..3d35393 100644
+--- a/doc/xmlwf.xml
++++ b/doc/xmlwf.xml
+@@ -313,6 +313,16 @@ supports both.
+ </listitem>
+ </varlistentry>
+
++ <varlistentry>
++ <term><option>-q</option></term>
++ <listitem>
++ <para>
++ Disable reparse deferral, and allow quadratic parse runtime
++ on large tokens (default: reparse deferral enabled).
++ </para>
++ </listitem>
++ </varlistentry>
++
+ <varlistentry>
+ <term><option>-r</option></term>
+ <listitem>
+--
+2.40.0
+
diff --git a/meta/recipes-core/expat/expat/CVE-2023-52425-0002.patch b/meta/recipes-core/expat/expat/CVE-2023-52425-0002.patch
new file mode 100644
index 0000000000..8376727778
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2023-52425-0002.patch
@@ -0,0 +1,87 @@
+From 09fdf998e7cf3f8f9327e6602077791095aedd4d Mon Sep 17 00:00:00 2001
+From: Sebastian Pipping <sebastian@pipping.org>
+Date: Thu, 9 Nov 2023 19:14:14 +0100
+Subject: [PATCH] xmlwf: Support disabling reparse deferral
+
+Rebased-and-adapted-by: Snild Dolkow <snild@sony.com>
+
+CVE: CVE-2023-52425
+
+Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/09fdf998e7cf3f8f9327e6602077791095aedd4d]
+
+Signed-off-by: Meenali Gupta <meenali.gupta@windriver.com>
+---
+ xmlwf/xmlwf.c | 20 ++++++++++++++++++++
+ xmlwf/xmlwf_helpgen.py | 4 ++++
+ 2 files changed, 24 insertions(+)
+
+diff --git a/xmlwf/xmlwf.c b/xmlwf/xmlwf.c
+index dd023a9..9a5441c 100644
+--- a/xmlwf/xmlwf.c
++++ b/xmlwf/xmlwf.c
+@@ -911,6 +911,9 @@ usage(const XML_Char *prog, int rc) {
+ T("billion laughs attack protection:\n")
+ T(" NOTE: If you ever need to increase these values for non-attack payload, please file a bug report.\n")
+ T("\n")
++ T("reparse deferral:\n")
++ T(" -q disable reparse deferral, and allow [q]uadratic parse runtime with large tokens\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("\n")
+@@ -967,6 +970,8 @@ tmain(int argc, XML_Char **argv) {
+ unsigned long long attackThresholdBytes;
+ XML_Bool attackThresholdGiven = XML_FALSE;
+
++ XML_Bool disableDeferral = XML_FALSE;
++
+ int exitCode = XMLWF_EXIT_SUCCESS;
+ enum XML_ParamEntityParsing paramEntityParsing
+ = XML_PARAM_ENTITY_PARSING_NEVER;
+@@ -1091,6 +1096,11 @@ tmain(int argc, XML_Char **argv) {
+ #endif
+ break;
+ }
++ case T('q'): {
++ disableDeferral = XML_TRUE;
++ j++;
++ break;
++ }
+ case T('\0'):
+ if (j > 1) {
+ i++;
+@@ -1136,6 +1146,16 @@ tmain(int argc, XML_Char **argv) {
+ #endif
+ }
+
++ if (disableDeferral) {
++ const XML_Bool success = XML_SetReparseDeferralEnabled(parser, XML_FALSE);
++ if (! success) {
++ // This prevents tperror(..) from reporting misleading "[..]: Success"
++ errno = EINVAL;
++ tperror(T("Failed to disable reparse deferral"));
++ exit(XMLWF_EXIT_INTERNAL_ERROR);
++ }
++ }
++
+ if (requireStandalone)
+ XML_SetNotStandaloneHandler(parser, notStandalone);
+ XML_SetParamEntityParsing(parser, paramEntityParsing);
+diff --git a/xmlwf/xmlwf_helpgen.py b/xmlwf/xmlwf_helpgen.py
+index c2a527f..1bd0a0a 100755
+--- a/xmlwf/xmlwf_helpgen.py
++++ b/xmlwf/xmlwf_helpgen.py
+@@ -81,6 +81,10 @@ 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)')
+
++reparse_deferral = parser.add_argument_group('reparse deferral')
++reparse_deferral.add_argument('-q', metavar='FACTOR',
++ help='disable reparse deferral, and allow [q]uadratic parse runtime with large tokens')
++
+ parser.add_argument('files', metavar='FILE', nargs='*', help='file to process (default: STDIN)')
+
+ info = parser.add_argument_group('info arguments')
+--
+2.40.0
+
diff --git a/meta/recipes-core/expat/expat/CVE-2023-52425-0003.patch b/meta/recipes-core/expat/expat/CVE-2023-52425-0003.patch
new file mode 100644
index 0000000000..e5c3606e19
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2023-52425-0003.patch
@@ -0,0 +1,222 @@
+From 9cdf9b8d77d5c2c2a27d15fb68dd3f83cafb45a1 Mon Sep 17 00:00:00 2001
+From: Snild Dolkow <snild@sony.com>
+Date: Thu, 17 Aug 2023 16:25:26 +0200
+Subject: [PATCH] Skip parsing after repeated partials on the same token 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.
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+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
+
+CVE: CVE-2023-52425
+
+Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/9cdf9b8d77d5c2c2a27d15fb68dd3f83cafb45a1]
+
+Signed-off-by: Meenali Gupta <meenali.gupta@windriver.com>
+---
+ lib/xmlparse.c | 58 +++++++++++++++++++++++++++++++++-----------------
+ 1 file changed, 39 insertions(+), 19 deletions(-)
+
+diff --git a/lib/xmlparse.c b/lib/xmlparse.c
+index bbffcaa..5695417 100644
+--- a/lib/xmlparse.c
++++ b/lib/xmlparse.c
+@@ -81,6 +81,7 @@
+ # endif
+ #endif
+
++#include <stdbool.h>
+ #include <stddef.h>
+ #include <string.h> /* memset(), memcpy() */
+ #include <assert.h>
+@@ -629,6 +630,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;
+@@ -960,6 +962,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 */
+@@ -1141,6 +1169,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;
+@@ -1872,29 +1901,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;
+@@ -1924,7 +1944,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;
+@@ -2027,8 +2047,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;
+@@ -2220,7 +2240,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.40.0
+
diff --git a/meta/recipes-core/expat/expat/CVE-2023-52425-0004.patch b/meta/recipes-core/expat/expat/CVE-2023-52425-0004.patch
new file mode 100644
index 0000000000..35e8e0b1e5
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2023-52425-0004.patch
@@ -0,0 +1,42 @@
+From 1b9d398517befeb944cbbadadf10992b07e96fa2 Mon Sep 17 00:00:00 2001
+From: Snild Dolkow <snild@sony.com>
+Date: Mon, 4 Sep 2023 17:21:14 +0200
+Subject: [PATCH] [PATCH] Don't update partial token heuristic on error
+
+Suggested-by: Sebastian Pipping <sebastian@pipping.org>
+
+CVE: CVE-2023-52425
+
+Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/1b9d398517befeb944cbbadadf10992b07e96fa2]
+
+Signed-off-by: Meenali Gupta <meenali.gupta@windriver.com>
+---
+ lib/xmlparse.c | 12 +++++++-----
+ 1 file changed, 7 insertions(+), 5 deletions(-)
+
+diff --git a/lib/xmlparse.c b/lib/xmlparse.c
+index 5695417..5c66f54 100644
+--- a/lib/xmlparse.c
++++ b/lib/xmlparse.c
+@@ -979,11 +979,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.40.0
+
diff --git a/meta/recipes-core/expat/expat/CVE-2023-52425-0005.patch b/meta/recipes-core/expat/expat/CVE-2023-52425-0005.patch
new file mode 100644
index 0000000000..d4e112db58
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2023-52425-0005.patch
@@ -0,0 +1,69 @@
+From 09957b8ced725b96a95acff150facda93f03afe1 Mon Sep 17 00:00:00 2001
+From: Snild Dolkow <snild@sony.com>
+Date: Thu, 26 Oct 2023 10:41:00 +0200
+Subject: [PATCH] Allow XML_GetBuffer() with len=0 on a fresh parser
+
+len=0 was previously OK if there had previously been a non-zero call.
+It makes sense to allow an application to work the same way on a
+newly-created parser, and not have to care if its incoming buffer
+happens to be 0.
+
+CVE: CVE-2023-52425
+
+Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/09957b8ced725b96a95acff150facda93f03afe1]
+
+Signed-off-by: Meenali Gupta <meenali.gupta@windriver.com>
+---
+ lib/xmlparse.c | 22 +++++++++++-----------
+ 1 file changed, 11 insertions(+), 11 deletions(-)
+
+diff --git a/lib/xmlparse.c b/lib/xmlparse.c
+index 5c66f54..5b112c6 100644
+--- a/lib/xmlparse.c
++++ b/lib/xmlparse.c
+@@ -2095,7 +2095,8 @@ XML_GetBuffer(XML_Parser parser, int len) {
+ default:;
+ }
+
+- if (len > EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferEnd)) {
++ if (len > EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferEnd)
++ || parser->m_buffer == NULL) {
+ #ifdef XML_CONTEXT_BYTES
+ int keep;
+ #endif /* defined XML_CONTEXT_BYTES */
+@@ -2118,8 +2119,9 @@ XML_GetBuffer(XML_Parser parser, int len) {
+ }
+ neededSize += keep;
+ #endif /* defined XML_CONTEXT_BYTES */
+- if (neededSize
+- <= EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_buffer)) {
++ if (parser->m_buffer && parser->m_bufferPtr
++ && neededSize
++ <= EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_buffer)) {
+ #ifdef XML_CONTEXT_BYTES
+ if (keep < EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer)) {
+ int offset
+@@ -2133,14 +2135,12 @@ XML_GetBuffer(XML_Parser parser, int len) {
+ parser->m_bufferPtr -= offset;
+ }
+ #else
+- if (parser->m_buffer && parser->m_bufferPtr) {
+- memmove(parser->m_buffer, parser->m_bufferPtr,
+- EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr));
+- parser->m_bufferEnd
+- = parser->m_buffer
+- + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr);
+- parser->m_bufferPtr = parser->m_buffer;
+- }
++ memmove(parser->m_buffer, parser->m_bufferPtr,
++ EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr));
++ parser->m_bufferEnd
++ = parser->m_buffer
++ + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr);
++ parser->m_bufferPtr = parser->m_buffer;
+ #endif /* not defined XML_CONTEXT_BYTES */
+ } else {
+ char *newBuf;
+--
+2.40.0
+
diff --git a/meta/recipes-core/expat/expat/CVE-2023-52425-0006.patch b/meta/recipes-core/expat/expat/CVE-2023-52425-0006.patch
new file mode 100644
index 0000000000..c1fb4893ed
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2023-52425-0006.patch
@@ -0,0 +1,67 @@
+From 9fe3672459c1bf10926b85f013aa1b623d855545 Mon Sep 17 00:00:00 2001
+From: Snild Dolkow <snild@sony.com>
+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 <sebastian@pipping.org>
+
+CVE: CVE-2023-52425
+
+Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/9fe3672459c1bf10926b85f013aa1b623d855545]
+
+Signed-off-by: Meenali Gupta <meenali.gupta@windriver.com>
+---
+ lib/internal.h | 3 +++
+ lib/xmlparse.c | 5 ++++-
+ 2 files changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/lib/internal.h b/lib/internal.h
+index 03c8fde..1df417f 100644
+--- a/lib/internal.h
++++ b/lib/internal.h
+@@ -31,6 +31,7 @@
+ Copyright (c) 2016-2022 Sebastian Pipping <sebastian@pipping.org>
+ Copyright (c) 2018 Yury Gribov <tetra2005@gmail.com>
+ Copyright (c) 2019 David Loffredo <loffredo@steptools.com>
++ Copyright (c) 2023 Sony Corporation / Snild Dolkow <snild@sony.com>
+ Licensed under the MIT license:
+
+ Permission is hereby granted, free of charge, to any person obtaining
+@@ -160,6 +161,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 5b112c6..be6dd92 100644
+--- a/lib/xmlparse.c
++++ b/lib/xmlparse.c
+@@ -615,6 +615,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. */
+@@ -967,7 +969,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.40.0
+
diff --git a/meta/recipes-core/expat/expat/CVE-2023-52425-0007.patch b/meta/recipes-core/expat/expat/CVE-2023-52425-0007.patch
new file mode 100644
index 0000000000..e2fb35eae6
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2023-52425-0007.patch
@@ -0,0 +1,159 @@
+From 1d3162da8a85a398ab451aadd6c2ad19587e5a68 Mon Sep 17 00:00:00 2001
+From: Snild Dolkow <snild@sony.com>
+Date: Mon, 11 Sep 2023 15:31:24 +0200
+Subject: [PATCH] Add app setting for enabling/disabling reparse heuristic
+
+Suggested-by: Sebastian Pipping <sebastian@pipping.org>
+CI-fighting-assistance-by: Sebastian Pipping <sebastian@pipping.org>
+
+CVE: CVE-2023-52425
+
+Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/1d3162da8a85a398ab451aadd6c2ad19587e5a68]
+
+Signed-off-by: Meenali Gupta <meenali.gupta@windriver.com>
+---
+ doc/reference.html | 30 ++++++++++++++++++++++++------
+ lib/expat.h | 5 +++++
+ lib/libexpat.def.cmake | 2 ++
+ lib/xmlparse.c | 13 ++++++++++++-
+ 4 files changed, 43 insertions(+), 7 deletions(-)
+
+diff --git a/doc/reference.html b/doc/reference.html
+index 9953aa7..7dd9370 100644
+--- a/doc/reference.html
++++ b/doc/reference.html
+@@ -151,10 +151,11 @@ interface.</p>
+ </ul>
+ </li>
+ <li>
+- <a href="#billion-laughs">Billion Laughs Attack Protection</a>
++ <a href="#attack-protection">Attack Protection</a>
+ <ul>
+ <li><a href="#XML_SetBillionLaughsAttackProtectionMaximumAmplification">XML_SetBillionLaughsAttackProtectionMaximumAmplification</a></li>
+ <li><a href="#XML_SetBillionLaughsAttackProtectionActivationThreshold">XML_SetBillionLaughsAttackProtectionActivationThreshold</a></li>
++ <li><a href="#XML_SetReparseDeferralEnabled">XML_SetReparseDeferralEnabled</a></li>
+ </ul>
+ </li>
+ <li><a href="#miscellaneous">Miscellaneous Functions</a>
+@@ -2123,11 +2124,7 @@ parse position may be before the beginning of the buffer.</p>
+ return NULL.</p>
+ </div>
+
+-<h3><a name="billion-laughs">Billion Laughs Attack Protection</a></h3>
+-
+-<p>The functions in this section configure the built-in
+- protection against various forms of
+- <a href="https://en.wikipedia.org/wiki/Billion_laughs_attack">billion laughs attacks</a>.</p>
++<h3><a name="attack-protection">Attack Protection</a><a name="billion-laughs"></a></h3>
+
+ <h4 id="XML_SetBillionLaughsAttackProtectionMaximumAmplification">XML_SetBillionLaughsAttackProtectionMaximumAmplification</h4>
+ <pre class="fcndec">
+@@ -2215,6 +2212,27 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold(XML_Parser p,
+ </p>
+ </div>
+
++<h4 id="XML_SetReparseDeferralEnabled">XML_SetReparseDeferralEnabled</h4>
++<pre class="fcndec">
++/* Added in Expat 2.6.0. */
++XML_Bool XMLCALL
++XML_SetReparseDeferralEnabled(XML_Parser parser, XML_Bool enabled);
++</pre>
++<div class="fcndef">
++ <p>
++ 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.
++ </p>
++ <p>
++ The <code>enabled</code> argument should be <code>XML_TRUE</code> or <code>XML_FALSE</code>.
++ </p>
++ <p>
++ Returns <code>XML_TRUE</code> on success, and <code>XML_FALSE</code> on error.
++ </p>
++</div>
++
+ <h3><a name="miscellaneous">Miscellaneous functions</a></h3>
+
+ <p>The functions in this section either obtain state information from
+diff --git a/lib/expat.h b/lib/expat.h
+index 9e64174..73dda6d 100644
+--- a/lib/expat.h
++++ b/lib/expat.h
+@@ -16,6 +16,7 @@
+ Copyright (c) 2016 Thomas Beutlich <tc@tbeu.de>
+ Copyright (c) 2017 Rhodri James <rhodri@wildebeest.org.uk>
+ Copyright (c) 2022 Thijs Schreijer <thijs@thijsschreijer.nl>
++ Copyright (c) 2023 Sony Corporation / Snild Dolkow <snild@sony.com>
+ Licensed under the MIT license:
+
+ Permission is hereby granted, free of charge, to any person obtaining
+@@ -1054,6 +1055,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/libexpat.def.cmake b/lib/libexpat.def.cmake
+index 61a4f00..10ee9cd 100644
+--- a/lib/libexpat.def.cmake
++++ b/lib/libexpat.def.cmake
+@@ -77,3 +77,5 @@ EXPORTS
+ ; added with version 2.4.0
+ @_EXPAT_COMMENT_DTD_OR_GE@ XML_SetBillionLaughsAttackProtectionActivationThreshold @69
+ @_EXPAT_COMMENT_DTD_OR_GE@ XML_SetBillionLaughsAttackProtectionMaximumAmplification @70
++; added with version 2.6.0
++ XML_SetReparseDeferralEnabled @71
+diff --git a/lib/xmlparse.c b/lib/xmlparse.c
+index be6dd92..8cf32e0 100644
+--- a/lib/xmlparse.c
++++ b/lib/xmlparse.c
+@@ -633,6 +633,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;
+@@ -969,7 +970,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.
+@@ -1175,6 +1176,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;
+@@ -2601,6 +2603,15 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold(
+ }
+ #endif /* 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.40.0
+
diff --git a/meta/recipes-core/expat/expat/CVE-2023-52425-0008.patch b/meta/recipes-core/expat/expat/CVE-2023-52425-0008.patch
new file mode 100644
index 0000000000..fa25fcd2db
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2023-52425-0008.patch
@@ -0,0 +1,95 @@
+From 8ddd8e86aa446d02eb8d398972d3b10d4cad908a Mon Sep 17 00:00:00 2001
+From: Snild Dolkow <snild@sony.com>
+Date: Fri, 29 Sep 2023 10:14:59 +0200
+Subject: [PATCH] Try to parse even when incoming len is zero
+
+If the reparse deferral setting has changed, it may be possible to
+finish a token.
+
+CVE: CVE-2023-52425
+
+Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/8ddd8e86aa446d02eb8d398972d3b10d4cad908a]
+
+Signed-off-by: Meenali Gupta <meenali.gupta@windriver.com>
+---
+ lib/xmlparse.c | 55 ++++++++------------------------------------------
+ 1 file changed, 8 insertions(+), 47 deletions(-)
+
+diff --git a/lib/xmlparse.c b/lib/xmlparse.c
+index 8cf32e0..f4ff66e 100644
+--- a/lib/xmlparse.c
++++ b/lib/xmlparse.c
+@@ -1896,46 +1896,8 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) {
+ parser->m_parsingStatus.parsing = XML_PARSING;
+ }
+
+- if (len == 0) {
+- parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal;
+- if (! isFinal)
+- return XML_STATUS_OK;
+- parser->m_positionPtr = parser->m_bufferPtr;
+- parser->m_parseEndPtr = parser->m_bufferEnd;
+-
+- /* If data are left over from last buffer, and we now know that these
+- data are the final chunk of input, then we have to check them again
+- to detect errors based on that fact.
+- */
+- parser->m_errorCode
+- = 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:
+- /* 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;
+- case XML_INITIALIZED:
+- case XML_PARSING:
+- parser->m_parsingStatus.parsing = XML_FINISHED;
+- /* fall through */
+- default:
+- return XML_STATUS_OK;
+- }
+- }
+- parser->m_eventEndPtr = parser->m_eventPtr;
+- parser->m_processor = errorProcessor;
+- return XML_STATUS_ERROR;
+- }
+ #ifndef XML_CONTEXT_BYTES
+- else if (parser->m_bufferPtr == parser->m_bufferEnd) {
++ if (parser->m_bufferPtr == parser->m_bufferEnd) {
+ const char *end;
+ int nLeftOver;
+ enum XML_Status result;
+@@ -2006,15 +1968,14 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) {
+ return result;
+ }
+ #endif /* not defined XML_CONTEXT_BYTES */
+- else {
+- void *buff = XML_GetBuffer(parser, len);
+- if (buff == NULL)
+- return XML_STATUS_ERROR;
+- else {
+- memcpy(buff, s, len);
+- return XML_ParseBuffer(parser, len, isFinal);
+- }
++ void *buff = XML_GetBuffer(parser, len);
++ if (buff == NULL)
++ return XML_STATUS_ERROR;
++ if (len > 0) {
++ assert(s != NULL); // make sure s==NULL && len!=0 was rejected above
++ memcpy(buff, s, len);
+ }
++ return XML_ParseBuffer(parser, len, isFinal);
+ }
+
+ enum XML_Status XMLCALL
+--
+2.40.0
+
diff --git a/meta/recipes-core/expat/expat/CVE-2023-52425-0009.patch b/meta/recipes-core/expat/expat/CVE-2023-52425-0009.patch
new file mode 100644
index 0000000000..9c1157faac
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2023-52425-0009.patch
@@ -0,0 +1,52 @@
+From ad9c01be8ee5d3d5cac2bfd3949ad764541d35e7 Mon Sep 17 00:00:00 2001
+From: Snild Dolkow <snild@sony.com>
+Date: Thu, 26 Oct 2023 13:55:02 +0200
+Subject: [PATCH] Make external entity parser inherit partial token heuristic
+ setting
+
+The test is essentially a copy of the existing test for the setter,
+adapted to run on the external parser instead of the original one.
+
+Suggested-by: Sebastian Pipping <sebastian@pipping.org>
+CI-fighting-assistance-by: Sebastian Pipping <sebastian@pipping.org>
+
+CVE: CVE-2023-52425
+
+Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/ad9c01be8ee5d3d5cac2bfd3949ad764541d35e7]
+
+Signed-off-by: Meenali Gupta <meenali.gupta@windriver.com>
+---
+ lib/xmlparse.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/lib/xmlparse.c b/lib/xmlparse.c
+index f4ff66e..6746d70 100644
+--- a/lib/xmlparse.c
++++ b/lib/xmlparse.c
+@@ -1346,6 +1346,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context,
+ to worry which hash secrets each table has.
+ */
+ unsigned long oldhash_secret_salt;
++ XML_Bool oldReparseDeferralEnabled;
+
+ /* Validate the oldParser parameter before we pull everything out of it */
+ if (oldParser == NULL)
+@@ -1390,6 +1391,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context,
+ to worry which hash secrets each table has.
+ */
+ oldhash_secret_salt = parser->m_hash_secret_salt;
++ oldReparseDeferralEnabled = parser->m_reparseDeferralEnabled;
+
+ #ifdef XML_DTD
+ if (! context)
+@@ -1442,6 +1444,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context,
+ parser->m_defaultExpandInternalEntities = oldDefaultExpandInternalEntities;
+ parser->m_ns_triplets = oldns_triplets;
+ parser->m_hash_secret_salt = oldhash_secret_salt;
++ parser->m_reparseDeferralEnabled = oldReparseDeferralEnabled;
+ parser->m_parentParser = oldParser;
+ #ifdef XML_DTD
+ parser->m_paramEntityParsing = oldParamEntityParsing;
+--
+2.40.0
+
diff --git a/meta/recipes-core/expat/expat/CVE-2023-52425-0010.patch b/meta/recipes-core/expat/expat/CVE-2023-52425-0010.patch
new file mode 100644
index 0000000000..3fbf69de08
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2023-52425-0010.patch
@@ -0,0 +1,111 @@
+From 60b74209899a67d426d208662674b55a5eed918c Mon Sep 17 00:00:00 2001
+From: Snild Dolkow <snild@sony.com>
+Date: Wed, 4 Oct 2023 16:00:14 +0200
+Subject: [PATCH] Bypass partial token heuristic when close to maximum buffer
+ size
+
+For huge tokens, we may end up in a situation where the partial token
+parse deferral heuristic demands more bytes than Expat's maximum buffer
+size (currently ~half of INT_MAX) could fit.
+
+INT_MAX/2 is 1024 MiB on most systems. Clearly, a token of 950 MiB could
+fit in that buffer, but the reparse threshold might be such that
+callProcessor() will defer it, allowing the app to keep filling the
+buffer until XML_GetBuffer() eventually returns a memory error.
+
+By bypassing the heuristic when we're getting close to the maximum
+buffer size, it will once again be possible to parse tokens in the size
+range INT_MAX/2/ratio < size < INT_MAX/2 reliably.
+
+We subtract the last buffer fill size as a way to detect that the next
+XML_GetBuffer() call has a risk of returning a memory error -- assuming
+that the application is likely to keep using the same (or smaller) fill.
+
+We subtract XML_CONTEXT_BYTES because that's the maximum amount of bytes
+that could remain at the start of the buffer, preceding the partial
+token. Technically, it could be fewer bytes, but XML_CONTEXT_BYTES is
+normally small relative to INT_MAX, and is much simpler to use.
+
+Co-authored-by: Sebastian Pipping <sebastian@pipping.org>
+
+CVE: CVE-2023-52425
+
+Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/60b74209899a67d426d208662674b55a5eed918c]
+
+Signed-off-by: Meenali Gupta <meenali.gupta@windriver.com>
+---
+ lib/xmlparse.c | 23 ++++++++++++++++++++++-
+ 1 file changed, 22 insertions(+), 1 deletion(-)
+
+diff --git a/lib/xmlparse.c b/lib/xmlparse.c
+index 6746d70..32c57f6 100644
+--- a/lib/xmlparse.c
++++ b/lib/xmlparse.c
+@@ -205,6 +205,8 @@ typedef char ICHAR;
+ /* Do safe (NULL-aware) pointer arithmetic */
+ #define EXPAT_SAFE_PTR_DIFF(p, q) (((p) && (q)) ? ((p) - (q)) : 0)
+
++#define EXPAT_MIN(a, b) (((a) < (b)) ? (a) : (b))
++
+ #include "internal.h"
+ #include "xmltok.h"
+ #include "xmlrole.h"
+@@ -634,6 +636,7 @@ struct XML_ParserStruct {
+ const char *m_parseEndPtr;
+ size_t m_partialTokenBytesBefore; /* used in heuristic to avoid O(n^2) */
+ XML_Bool m_reparseDeferralEnabled;
++ int m_lastBufferRequestSize;
+ XML_Char *m_dataBuf;
+ XML_Char *m_dataBufEnd;
+ XML_StartElementHandler m_startElementHandler;
+@@ -975,7 +978,18 @@ callProcessor(XML_Parser parser, const char *start, const char *end,
+ // 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);
++ // ...but *do* try anyway if we're close to reaching the max buffer size.
++ size_t close_to_maxbuf = INT_MAX / 2 + (INT_MAX & 1); // round up
++#if XML_CONTEXT_BYTES > 0
++ // subtract XML_CONTEXT_BYTES, but don't go below zero
++ close_to_maxbuf -= EXPAT_MIN(close_to_maxbuf, XML_CONTEXT_BYTES);
++#endif
++ // subtract the last buffer fill size, but don't go below zero
++ // m_lastBufferRequestSize is never assigned a value < 0, so the cast is ok
++ close_to_maxbuf
++ -= EXPAT_MIN(close_to_maxbuf, (size_t)parser->m_lastBufferRequestSize);
++ const bool enough
++ = (have_now >= 2 * had_before) || (have_now > close_to_maxbuf);
+
+ if (! enough) {
+ *endPtr = start; // callers may expect this to be set
+@@ -1177,6 +1191,7 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) {
+ parser->m_parseEndPtr = NULL;
+ parser->m_partialTokenBytesBefore = 0;
+ parser->m_reparseDeferralEnabled = g_reparseDeferralEnabledDefault;
++ parser->m_lastBufferRequestSize = 0;
+ parser->m_declElementType = NULL;
+ parser->m_declAttributeId = NULL;
+ parser->m_declEntity = NULL;
+@@ -1911,6 +1926,9 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) {
+ parser->m_processor = errorProcessor;
+ return XML_STATUS_ERROR;
+ }
++ // though this isn't a buffer request, we assume that `len` is the app's
++ // preferred buffer fill size, and therefore save it here.
++ parser->m_lastBufferRequestSize = len;
+ parser->m_parseEndByteIndex += len;
+ parser->m_positionPtr = s;
+ parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal;
+@@ -2064,6 +2082,9 @@ XML_GetBuffer(XML_Parser parser, int len) {
+ default:;
+ }
+
++ // whether or not the request succeeds, `len` seems to be the app's preferred
++ // buffer fill size; remember it.
++ parser->m_lastBufferRequestSize = len;
+ if (len > EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferEnd)
+ || parser->m_buffer == NULL) {
+ #ifdef XML_CONTEXT_BYTES
+--
+2.40.0
+
diff --git a/meta/recipes-core/expat/expat/CVE-2023-52425-0011.patch b/meta/recipes-core/expat/expat/CVE-2023-52425-0011.patch
new file mode 100644
index 0000000000..800aaff544
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2023-52425-0011.patch
@@ -0,0 +1,89 @@
+From 3d8141d26a3b01ff948e00956cb0723a89dadf7f Mon Sep 17 00:00:00 2001
+From: Snild Dolkow <snild@sony.com>
+Date: Mon, 20 Nov 2023 16:11:24 +0100
+Subject: [PATCH] Bypass partial token heuristic when nearing full buffer
+
+...instead of only when approaching the maximum buffer size INT/2+1.
+
+We'd like to give applications a chance to finish parsing a large token
+before buffer reallocation, in case the reallocation fails.
+
+By bypassing the reparse deferral heuristic when getting close to the
+filling the buffer, we give them this chance -- if the whole token is
+present in the buffer, it will be parsed at that time.
+
+This may come at the cost of some extra reparse attempts. For a token
+of n bytes, these extra parses cause us to scan over a maximum of
+2n bytes (... + n/8 + n/4 + n/2 + n). Therefore, parsing of big tokens
+remains O(n) in regard how many bytes we scan in attempts to parse. The
+cost in reality is lower than that, since the reparses that happen due
+to the bypass will affect m_partialTokenBytesBefore, delaying the next
+ratio-based reparse. Furthermore, only the first token that "breaks
+through" a buffer ceiling takes that extra reparse attempt; subsequent
+large tokens will only bypass the heuristic if they manage to hit the
+new buffer ceiling.
+
+Note that this cost analysis depends on the assumption that Expat grows
+its buffer by doubling it (or, more generally, grows it exponentially).
+If this changes, the cost of this bypass may increase. Hopefully, this
+would be caught by test_big_tokens_take_linear_time or the new test.
+
+The bypass logic assumes that the application uses a consistent fill.
+If the app increases its fill size, it may miss the bypass (and the
+normal heuristic will apply). If the app decreases its fill size, the
+bypass may be hit multiple times for the same buffer size. The very
+worst case would be to always fill half of the remaining buffer space,
+in which case parsing of a large n-byte token becomes O(n log n).
+
+As an added bonus, the new test case should be faster than the old one,
+since it doesn't have to go all the way to 1GiB to check the behavior.
+
+Finally, this change necessitated a small modification to two existing
+tests related to reparse deferral. These tests are testing the deferral
+enabled setting, and assume that reparsing will not happen for any other
+reason. By pre-growing the buffer, we make sure that this new deferral
+does not affect those test cases.
+
+CVE: CVE-2023-52425
+
+Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/3d8141d26a3b01ff948e00956cb0723a89dadf7f]
+
+Signed-off-by: Meenali Gupta <meenali.gupta@windriver.com>
+---
+ lib/xmlparse.c | 16 ++++++++--------
+ 1 file changed, 8 insertions(+), 8 deletions(-)
+
+diff --git a/lib/xmlparse.c b/lib/xmlparse.c
+index 32c57f6..2830c1e 100644
+--- a/lib/xmlparse.c
++++ b/lib/xmlparse.c
+@@ -978,18 +978,18 @@ callProcessor(XML_Parser parser, const char *start, const char *end,
+ // 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;
+- // ...but *do* try anyway if we're close to reaching the max buffer size.
+- size_t close_to_maxbuf = INT_MAX / 2 + (INT_MAX & 1); // round up
++ // ...but *do* try anyway if we're close to causing a reallocation.
++ size_t available_buffer
++ = EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer);
+ #if XML_CONTEXT_BYTES > 0
+- // subtract XML_CONTEXT_BYTES, but don't go below zero
+- close_to_maxbuf -= EXPAT_MIN(close_to_maxbuf, XML_CONTEXT_BYTES);
++ available_buffer -= EXPAT_MIN(available_buffer, XML_CONTEXT_BYTES);
+ #endif
+- // subtract the last buffer fill size, but don't go below zero
++ available_buffer
++ += EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferEnd);
+ // m_lastBufferRequestSize is never assigned a value < 0, so the cast is ok
+- close_to_maxbuf
+- -= EXPAT_MIN(close_to_maxbuf, (size_t)parser->m_lastBufferRequestSize);
+ const bool enough
+- = (have_now >= 2 * had_before) || (have_now > close_to_maxbuf);
++ = (have_now >= 2 * had_before)
++ || ((size_t)parser->m_lastBufferRequestSize > available_buffer);
+
+ if (! enough) {
+ *endPtr = start; // callers may expect this to be set
+--
+2.40.0
+
diff --git a/meta/recipes-core/expat/expat/CVE-2023-52425-0012.patch b/meta/recipes-core/expat/expat/CVE-2023-52425-0012.patch
new file mode 100644
index 0000000000..8693e9449e
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2023-52425-0012.patch
@@ -0,0 +1,87 @@
+From 119ae277abaabd4d17b2e64300fec712ef403b28 Mon Sep 17 00:00:00 2001
+From: Snild Dolkow <snild@sony.com>
+Date: Thu, 28 Sep 2023 18:26:19 +0200
+Subject: [PATCH] Grow buffer based on current size Until now, the buffer size
+ to grow to has been calculated based on the distance from the current parse
+ position to the end of the buffer. This means that the size of any
+ already-parsed data was not considered, leading to inconsistent buffer
+ growth.
+
+There was also a special case in XML_Parse() when XML_CONTEXT_BYTES was
+zero, where the buffer size would be set to twice the incoming string
+length. This patch replaces this with an XML_GetBuffer() call.
+
+Growing the buffer based on its total size makes its growth consistent.
+
+The commit includes a test that checks that we can reach the max buffer
+size (usually INT_MAX/2 + 1) regardless of previously parsed content.
+
+GitHub CI couldn't allocate the full 1GiB with MinGW/wine32, though it
+works locally with the same compiler and wine version. As a workaround,
+the test tries to malloc 1GiB, and reduces `maxbuf` to 512MiB in case
+of failure.
+
+CVE: CVE-2023-52425
+
+Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/119ae277abaabd4d17b2e64300fec712ef403b28]
+
+Signed-off-by: Meenali Gupta <meenali.gupta@windriver.com>
+---
+ lib/xmlparse.c | 33 ++++++++++++++++-----------------
+ 1 file changed, 16 insertions(+), 17 deletions(-)
+
+diff --git a/lib/xmlparse.c b/lib/xmlparse.c
+index 2830c1e..81f9bb3 100644
+--- a/lib/xmlparse.c
++++ b/lib/xmlparse.c
+@@ -1961,23 +1961,22 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) {
+ &parser->m_position);
+ nLeftOver = s + len - end;
+ if (nLeftOver) {
+- if (parser->m_buffer == NULL
+- || nLeftOver > parser->m_bufferLim - parser->m_buffer) {
+- /* avoid _signed_ integer overflow */
+- char *temp = NULL;
+- const int bytesToAllocate = (int)((unsigned)len * 2U);
+- if (bytesToAllocate > 0) {
+- temp = (char *)REALLOC(parser, parser->m_buffer, bytesToAllocate);
+- }
+- if (temp == NULL) {
+- parser->m_errorCode = XML_ERROR_NO_MEMORY;
+- parser->m_eventPtr = parser->m_eventEndPtr = NULL;
+- parser->m_processor = errorProcessor;
+- return XML_STATUS_ERROR;
+- }
+- parser->m_buffer = temp;
+- parser->m_bufferLim = parser->m_buffer + bytesToAllocate;
++ // Back up and restore the parsing status to avoid XML_ERROR_SUSPENDED
++ // (and XML_ERROR_FINISHED) from XML_GetBuffer.
++ const enum XML_Parsing originalStatus = parser->m_parsingStatus.parsing;
++ parser->m_parsingStatus.parsing = XML_PARSING;
++ void *const temp = XML_GetBuffer(parser, nLeftOver);
++ parser->m_parsingStatus.parsing = originalStatus;
++ if (temp == NULL) {
++ // NOTE: parser->m_errorCode has already been set by XML_GetBuffer().
++ parser->m_eventPtr = parser->m_eventEndPtr = NULL;
++ parser->m_processor = errorProcessor;
++ return XML_STATUS_ERROR;
+ }
++ // Since we know that the buffer was empty and XML_CONTEXT_BYTES is 0, we
++ // don't have any data to preserve, and can copy straight into the start
++ // of the buffer rather than the GetBuffer return pointer (which may be
++ // pointing further into the allocated buffer).
+ memcpy(parser->m_buffer, end, nLeftOver);
+ }
+ parser->m_bufferPtr = parser->m_buffer;
+@@ -2135,7 +2134,7 @@ XML_GetBuffer(XML_Parser parser, int len) {
+ } else {
+ char *newBuf;
+ int bufferSize
+- = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferPtr);
++ = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_buffer);
+ if (bufferSize == 0)
+ bufferSize = INIT_BUFFER_SIZE;
+ do {
+--
+2.40.0
+
diff --git a/meta/recipes-core/expat/expat_2.5.0.bb b/meta/recipes-core/expat/expat_2.5.0.bb
index 31e989cfe2..b7b5cce925 100644
--- a/meta/recipes-core/expat/expat_2.5.0.bb
+++ b/meta/recipes-core/expat/expat_2.5.0.bb
@@ -22,6 +22,18 @@ SRC_URI = "https://github.com/libexpat/libexpat/releases/download/R_${VERSION_TA
file://CVE-2023-52426-009.patch \
file://CVE-2023-52426-010.patch \
file://CVE-2023-52426-011.patch \
+ file://CVE-2023-52425-0001.patch \
+ file://CVE-2023-52425-0002.patch \
+ file://CVE-2023-52425-0003.patch \
+ file://CVE-2023-52425-0004.patch \
+ file://CVE-2023-52425-0005.patch \
+ file://CVE-2023-52425-0006.patch \
+ file://CVE-2023-52425-0007.patch \
+ file://CVE-2023-52425-0008.patch \
+ file://CVE-2023-52425-0009.patch \
+ file://CVE-2023-52425-0010.patch \
+ file://CVE-2023-52425-0011.patch \
+ file://CVE-2023-52425-0012.patch \
"
UPSTREAM_CHECK_URI = "https://github.com/libexpat/libexpat/releases/"