diff options
author | Meenali Gupta <meenali.gupta@windriver.com> | 2024-03-29 11:12:02 +0000 |
---|---|---|
committer | Steve Sakoman <steve@sakoman.com> | 2024-04-05 07:23:59 -0700 |
commit | f9653f111864598e3659108ab2692c81f677954b (patch) | |
tree | 0432eeda286e46e584a006379ad4c9fa0368c646 | |
parent | 65e2df59905f6501898a7a24659a024119712f1f (diff) | |
download | poky-f9653f111864598e3659108ab2692c81f677954b.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.
(From OE-Core rev: 1bdcd10930a2998f6bbe56b3ba4c9b6c91203b39)
Signed-off-by: Meenali Gupta <meenali.gupta@windriver.com>
Signed-off-by: Steve Sakoman <steve@sakoman.com>
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 @@ | |||
1 | From d5b02e96ab95d2a7ae0aea72d00054b9d036d76d Mon Sep 17 00:00:00 2001 | ||
2 | From: Sebastian Pipping <sebastian@pipping.org> | ||
3 | Date: Thu, 9 Nov 2023 19:28:05 +0100 | ||
4 | Subject: [PATCH] xmlwf: Document argument "-q" | ||
5 | |||
6 | Rebased-and-adapted-by: Snild Dolkow <snild@sony.com> | ||
7 | |||
8 | CVE: CVE-2023-52425 | ||
9 | |||
10 | Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/d5b02e96ab95d2a7ae0aea72d00054b9d036d76d] | ||
11 | |||
12 | Signed-off-by: Meenali Gupta <meenali.gupta@windriver.com> | ||
13 | --- | ||
14 | doc/xmlwf.xml | 10 ++++++++++ | ||
15 | 1 file changed, 10 insertions(+) | ||
16 | |||
17 | diff --git a/doc/xmlwf.xml b/doc/xmlwf.xml | ||
18 | index 9603abf..3d35393 100644 | ||
19 | --- a/doc/xmlwf.xml | ||
20 | +++ b/doc/xmlwf.xml | ||
21 | @@ -313,6 +313,16 @@ supports both. | ||
22 | </listitem> | ||
23 | </varlistentry> | ||
24 | |||
25 | + <varlistentry> | ||
26 | + <term><option>-q</option></term> | ||
27 | + <listitem> | ||
28 | + <para> | ||
29 | + Disable reparse deferral, and allow quadratic parse runtime | ||
30 | + on large tokens (default: reparse deferral enabled). | ||
31 | + </para> | ||
32 | + </listitem> | ||
33 | + </varlistentry> | ||
34 | + | ||
35 | <varlistentry> | ||
36 | <term><option>-r</option></term> | ||
37 | <listitem> | ||
38 | -- | ||
39 | 2.40.0 | ||
40 | |||
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 @@ | |||
1 | From 09fdf998e7cf3f8f9327e6602077791095aedd4d Mon Sep 17 00:00:00 2001 | ||
2 | From: Sebastian Pipping <sebastian@pipping.org> | ||
3 | Date: Thu, 9 Nov 2023 19:14:14 +0100 | ||
4 | Subject: [PATCH] xmlwf: Support disabling reparse deferral | ||
5 | |||
6 | Rebased-and-adapted-by: Snild Dolkow <snild@sony.com> | ||
7 | |||
8 | CVE: CVE-2023-52425 | ||
9 | |||
10 | Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/09fdf998e7cf3f8f9327e6602077791095aedd4d] | ||
11 | |||
12 | Signed-off-by: Meenali Gupta <meenali.gupta@windriver.com> | ||
13 | --- | ||
14 | xmlwf/xmlwf.c | 20 ++++++++++++++++++++ | ||
15 | xmlwf/xmlwf_helpgen.py | 4 ++++ | ||
16 | 2 files changed, 24 insertions(+) | ||
17 | |||
18 | diff --git a/xmlwf/xmlwf.c b/xmlwf/xmlwf.c | ||
19 | index dd023a9..9a5441c 100644 | ||
20 | --- a/xmlwf/xmlwf.c | ||
21 | +++ b/xmlwf/xmlwf.c | ||
22 | @@ -911,6 +911,9 @@ usage(const XML_Char *prog, int rc) { | ||
23 | T("billion laughs attack protection:\n") | ||
24 | T(" NOTE: If you ever need to increase these values for non-attack payload, please file a bug report.\n") | ||
25 | T("\n") | ||
26 | + T("reparse deferral:\n") | ||
27 | + T(" -q disable reparse deferral, and allow [q]uadratic parse runtime with large tokens\n") | ||
28 | + T("\n") | ||
29 | T(" -a FACTOR set maximum tolerated [a]mplification factor (default: 100.0)\n") | ||
30 | T(" -b BYTES set number of output [b]ytes needed to activate (default: 8 MiB)\n") | ||
31 | T("\n") | ||
32 | @@ -967,6 +970,8 @@ tmain(int argc, XML_Char **argv) { | ||
33 | unsigned long long attackThresholdBytes; | ||
34 | XML_Bool attackThresholdGiven = XML_FALSE; | ||
35 | |||
36 | + XML_Bool disableDeferral = XML_FALSE; | ||
37 | + | ||
38 | int exitCode = XMLWF_EXIT_SUCCESS; | ||
39 | enum XML_ParamEntityParsing paramEntityParsing | ||
40 | = XML_PARAM_ENTITY_PARSING_NEVER; | ||
41 | @@ -1091,6 +1096,11 @@ tmain(int argc, XML_Char **argv) { | ||
42 | #endif | ||
43 | break; | ||
44 | } | ||
45 | + case T('q'): { | ||
46 | + disableDeferral = XML_TRUE; | ||
47 | + j++; | ||
48 | + break; | ||
49 | + } | ||
50 | case T('\0'): | ||
51 | if (j > 1) { | ||
52 | i++; | ||
53 | @@ -1136,6 +1146,16 @@ tmain(int argc, XML_Char **argv) { | ||
54 | #endif | ||
55 | } | ||
56 | |||
57 | + if (disableDeferral) { | ||
58 | + const XML_Bool success = XML_SetReparseDeferralEnabled(parser, XML_FALSE); | ||
59 | + if (! success) { | ||
60 | + // This prevents tperror(..) from reporting misleading "[..]: Success" | ||
61 | + errno = EINVAL; | ||
62 | + tperror(T("Failed to disable reparse deferral")); | ||
63 | + exit(XMLWF_EXIT_INTERNAL_ERROR); | ||
64 | + } | ||
65 | + } | ||
66 | + | ||
67 | if (requireStandalone) | ||
68 | XML_SetNotStandaloneHandler(parser, notStandalone); | ||
69 | XML_SetParamEntityParsing(parser, paramEntityParsing); | ||
70 | diff --git a/xmlwf/xmlwf_helpgen.py b/xmlwf/xmlwf_helpgen.py | ||
71 | index c2a527f..1bd0a0a 100755 | ||
72 | --- a/xmlwf/xmlwf_helpgen.py | ||
73 | +++ b/xmlwf/xmlwf_helpgen.py | ||
74 | @@ -81,6 +81,10 @@ billion_laughs.add_argument('-a', metavar='FACTOR', | ||
75 | help='set maximum tolerated [a]mplification factor (default: 100.0)') | ||
76 | billion_laughs.add_argument('-b', metavar='BYTES', help='set number of output [b]ytes needed to activate (default: 8 MiB)') | ||
77 | |||
78 | +reparse_deferral = parser.add_argument_group('reparse deferral') | ||
79 | +reparse_deferral.add_argument('-q', metavar='FACTOR', | ||
80 | + help='disable reparse deferral, and allow [q]uadratic parse runtime with large tokens') | ||
81 | + | ||
82 | parser.add_argument('files', metavar='FILE', nargs='*', help='file to process (default: STDIN)') | ||
83 | |||
84 | info = parser.add_argument_group('info arguments') | ||
85 | -- | ||
86 | 2.40.0 | ||
87 | |||
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 @@ | |||
1 | From 9cdf9b8d77d5c2c2a27d15fb68dd3f83cafb45a1 Mon Sep 17 00:00:00 2001 | ||
2 | From: Snild Dolkow <snild@sony.com> | ||
3 | Date: Thu, 17 Aug 2023 16:25:26 +0200 | ||
4 | Subject: [PATCH] Skip parsing after repeated partials on the same token When | ||
5 | the parse buffer contains the starting bytes of a token but not all of them, | ||
6 | we cannot parse the token to completion. We call this a partial token. When | ||
7 | this happens, the parse position is reset to the start of the token, and the | ||
8 | parse() call returns. The client is then expected to provide more data and | ||
9 | call parse() again. | ||
10 | MIME-Version: 1.0 | ||
11 | Content-Type: text/plain; charset=UTF-8 | ||
12 | Content-Transfer-Encoding: 8bit | ||
13 | |||
14 | In extreme cases, this means that the bytes of a token may be parsed | ||
15 | many times: once for every buffer refill required before the full token | ||
16 | is present in the buffer. | ||
17 | |||
18 | Math: | ||
19 | Assume there's a token of T bytes | ||
20 | Assume the client fills the buffer in chunks of X bytes | ||
21 | We'll try to parse X, 2X, 3X, 4X ... until mX == T (technically >=) | ||
22 | That's (m²+m)X/2 = (T²/X+T)/2 bytes parsed (arithmetic progression) | ||
23 | While it is alleviated by larger refills, this amounts to O(T²) | ||
24 | |||
25 | Expat grows its internal buffer by doubling it when necessary, but has | ||
26 | no way to inform the client about how much space is available. Instead, | ||
27 | we add a heuristic that skips parsing when we've repeatedly stopped on | ||
28 | an incomplete token. Specifically: | ||
29 | |||
30 | * Only try to parse if we have a certain amount of data buffered | ||
31 | * Every time we stop on an incomplete token, double the threshold | ||
32 | * As soon as any token completes, the threshold is reset | ||
33 | |||
34 | This means that when we get stuck on an incomplete token, the threshold | ||
35 | grows exponentially, effectively making the client perform larger buffer | ||
36 | fills, limiting how many times we can end up re-parsing the same bytes. | ||
37 | |||
38 | Math: | ||
39 | Assume there's a token of T bytes | ||
40 | Assume the client fills the buffer in chunks of X bytes | ||
41 | We'll try to parse X, 2X, 4X, 8X ... until (2^k)X == T (or larger) | ||
42 | That's (2^(k+1)-1)X bytes parsed -- e.g. 15X if T = 8X | ||
43 | This is equal to 2T-X, which amounts to O(T) | ||
44 | |||
45 | We could've chosen a faster growth rate, e.g. 4 or 8. Those seem to | ||
46 | increase performance further, at the cost of further increasing the | ||
47 | risk of growing the buffer more than necessary. This can easily be | ||
48 | adjusted in the future, if desired. | ||
49 | |||
50 | This is all completely transparent to the client, except for: | ||
51 | 1. possible delay of some callbacks (when our heuristic overshoots) | ||
52 | 2. apps that never do isFinal=XML_TRUE could miss data at the end | ||
53 | |||
54 | For the affected testdata, this change shows a 100-400x speedup. | ||
55 | The recset.xml benchmark shows no clear change either way. | ||
56 | |||
57 | Before: | ||
58 | benchmark -n ../testdata/largefiles/recset.xml 65535 3 | ||
59 | 3 loops, with buffer size 65535. Average time per loop: 0.270223 | ||
60 | benchmark -n ../testdata/largefiles/aaaaaa_attr.xml 4096 3 | ||
61 | 3 loops, with buffer size 4096. Average time per loop: 15.033048 | ||
62 | benchmark -n ../testdata/largefiles/aaaaaa_cdata.xml 4096 3 | ||
63 | 3 loops, with buffer size 4096. Average time per loop: 0.018027 | ||
64 | benchmark -n ../testdata/largefiles/aaaaaa_comment.xml 4096 3 | ||
65 | 3 loops, with buffer size 4096. Average time per loop: 11.775362 | ||
66 | benchmark -n ../testdata/largefiles/aaaaaa_tag.xml 4096 3 | ||
67 | 3 loops, with buffer size 4096. Average time per loop: 11.711414 | ||
68 | benchmark -n ../testdata/largefiles/aaaaaa_text.xml 4096 3 | ||
69 | 3 loops, with buffer size 4096. Average time per loop: 0.019362 | ||
70 | |||
71 | After: | ||
72 | ./run.sh benchmark -n ../testdata/largefiles/recset.xml 65535 3 | ||
73 | 3 loops, with buffer size 65535. Average time per loop: 0.269030 | ||
74 | ./run.sh benchmark -n ../testdata/largefiles/aaaaaa_attr.xml 4096 3 | ||
75 | 3 loops, with buffer size 4096. Average time per loop: 0.044794 | ||
76 | ./run.sh benchmark -n ../testdata/largefiles/aaaaaa_cdata.xml 4096 3 | ||
77 | 3 loops, with buffer size 4096. Average time per loop: 0.016377 | ||
78 | ./run.sh benchmark -n ../testdata/largefiles/aaaaaa_comment.xml 4096 3 | ||
79 | 3 loops, with buffer size 4096. Average time per loop: 0.027022 | ||
80 | ./run.sh benchmark -n ../testdata/largefiles/aaaaaa_tag.xml 4096 3 | ||
81 | 3 loops, with buffer size 4096. Average time per loop: 0.099360 | ||
82 | ./run.sh benchmark -n ../testdata/largefiles/aaaaaa_text.xml 4096 3 | ||
83 | 3 loops, with buffer size 4096. Average time per loop: 0.017956 | ||
84 | |||
85 | CVE: CVE-2023-52425 | ||
86 | |||
87 | Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/9cdf9b8d77d5c2c2a27d15fb68dd3f83cafb45a1] | ||
88 | |||
89 | Signed-off-by: Meenali Gupta <meenali.gupta@windriver.com> | ||
90 | --- | ||
91 | lib/xmlparse.c | 58 +++++++++++++++++++++++++++++++++----------------- | ||
92 | 1 file changed, 39 insertions(+), 19 deletions(-) | ||
93 | |||
94 | diff --git a/lib/xmlparse.c b/lib/xmlparse.c | ||
95 | index bbffcaa..5695417 100644 | ||
96 | --- a/lib/xmlparse.c | ||
97 | +++ b/lib/xmlparse.c | ||
98 | @@ -81,6 +81,7 @@ | ||
99 | # endif | ||
100 | #endif | ||
101 | |||
102 | +#include <stdbool.h> | ||
103 | #include <stddef.h> | ||
104 | #include <string.h> /* memset(), memcpy() */ | ||
105 | #include <assert.h> | ||
106 | @@ -629,6 +630,7 @@ struct XML_ParserStruct { | ||
107 | const char *m_bufferLim; | ||
108 | XML_Index m_parseEndByteIndex; | ||
109 | const char *m_parseEndPtr; | ||
110 | + size_t m_partialTokenBytesBefore; /* used in heuristic to avoid O(n^2) */ | ||
111 | XML_Char *m_dataBuf; | ||
112 | XML_Char *m_dataBufEnd; | ||
113 | XML_StartElementHandler m_startElementHandler; | ||
114 | @@ -960,6 +962,32 @@ get_hash_secret_salt(XML_Parser parser) { | ||
115 | return parser->m_hash_secret_salt; | ||
116 | } | ||
117 | |||
118 | +static enum XML_Error | ||
119 | +callProcessor(XML_Parser parser, const char *start, const char *end, | ||
120 | + const char **endPtr) { | ||
121 | + const size_t have_now = EXPAT_SAFE_PTR_DIFF(end, start); | ||
122 | + | ||
123 | + if (! parser->m_parsingStatus.finalBuffer) { | ||
124 | + // Heuristic: don't try to parse a partial token again until the amount of | ||
125 | + // available data has increased significantly. | ||
126 | + const size_t had_before = parser->m_partialTokenBytesBefore; | ||
127 | + const bool enough = (have_now >= 2 * had_before); | ||
128 | + | ||
129 | + if (! enough) { | ||
130 | + *endPtr = start; // callers may expect this to be set | ||
131 | + return XML_ERROR_NONE; | ||
132 | + } | ||
133 | + } | ||
134 | + const enum XML_Error ret = parser->m_processor(parser, start, end, endPtr); | ||
135 | + // if we consumed nothing, remember what we had on this parse attempt. | ||
136 | + if (*endPtr == start) { | ||
137 | + parser->m_partialTokenBytesBefore = have_now; | ||
138 | + } else { | ||
139 | + parser->m_partialTokenBytesBefore = 0; | ||
140 | + } | ||
141 | + return ret; | ||
142 | +} | ||
143 | + | ||
144 | static XML_Bool /* only valid for root parser */ | ||
145 | startParsing(XML_Parser parser) { | ||
146 | /* hash functions must be initialized before setContext() is called */ | ||
147 | @@ -1141,6 +1169,7 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) { | ||
148 | parser->m_bufferEnd = parser->m_buffer; | ||
149 | parser->m_parseEndByteIndex = 0; | ||
150 | parser->m_parseEndPtr = NULL; | ||
151 | + parser->m_partialTokenBytesBefore = 0; | ||
152 | parser->m_declElementType = NULL; | ||
153 | parser->m_declAttributeId = NULL; | ||
154 | parser->m_declEntity = NULL; | ||
155 | @@ -1872,29 +1901,20 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) { | ||
156 | to detect errors based on that fact. | ||
157 | */ | ||
158 | parser->m_errorCode | ||
159 | - = parser->m_processor(parser, parser->m_bufferPtr, | ||
160 | - parser->m_parseEndPtr, &parser->m_bufferPtr); | ||
161 | + = callProcessor(parser, parser->m_bufferPtr, parser->m_parseEndPtr, | ||
162 | + &parser->m_bufferPtr); | ||
163 | |||
164 | if (parser->m_errorCode == XML_ERROR_NONE) { | ||
165 | switch (parser->m_parsingStatus.parsing) { | ||
166 | case XML_SUSPENDED: | ||
167 | - /* It is hard to be certain, but it seems that this case | ||
168 | - * cannot occur. This code is cleaning up a previous parse | ||
169 | - * with no new data (since len == 0). Changing the parsing | ||
170 | - * state requires getting to execute a handler function, and | ||
171 | - * there doesn't seem to be an opportunity for that while in | ||
172 | - * this circumstance. | ||
173 | - * | ||
174 | - * Given the uncertainty, we retain the code but exclude it | ||
175 | - * from coverage tests. | ||
176 | - * | ||
177 | - * LCOV_EXCL_START | ||
178 | - */ | ||
179 | + /* While we added no new data, the finalBuffer flag may have caused | ||
180 | + * us to parse previously-unparsed data in the internal buffer. | ||
181 | + * If that triggered a callback to the application, it would have | ||
182 | + * had an opportunity to suspend parsing. */ | ||
183 | XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr, | ||
184 | parser->m_bufferPtr, &parser->m_position); | ||
185 | parser->m_positionPtr = parser->m_bufferPtr; | ||
186 | return XML_STATUS_SUSPENDED; | ||
187 | - /* LCOV_EXCL_STOP */ | ||
188 | case XML_INITIALIZED: | ||
189 | case XML_PARSING: | ||
190 | parser->m_parsingStatus.parsing = XML_FINISHED; | ||
191 | @@ -1924,7 +1944,7 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) { | ||
192 | parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal; | ||
193 | |||
194 | parser->m_errorCode | ||
195 | - = parser->m_processor(parser, s, parser->m_parseEndPtr = s + len, &end); | ||
196 | + = callProcessor(parser, s, parser->m_parseEndPtr = s + len, &end); | ||
197 | |||
198 | if (parser->m_errorCode != XML_ERROR_NONE) { | ||
199 | parser->m_eventEndPtr = parser->m_eventPtr; | ||
200 | @@ -2027,8 +2047,8 @@ XML_ParseBuffer(XML_Parser parser, int len, int isFinal) { | ||
201 | parser->m_parseEndByteIndex += len; | ||
202 | parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal; | ||
203 | |||
204 | - parser->m_errorCode = parser->m_processor( | ||
205 | - parser, start, parser->m_parseEndPtr, &parser->m_bufferPtr); | ||
206 | + parser->m_errorCode = callProcessor(parser, start, parser->m_parseEndPtr, | ||
207 | + &parser->m_bufferPtr); | ||
208 | |||
209 | if (parser->m_errorCode != XML_ERROR_NONE) { | ||
210 | parser->m_eventEndPtr = parser->m_eventPtr; | ||
211 | @@ -2220,7 +2240,7 @@ XML_ResumeParser(XML_Parser parser) { | ||
212 | } | ||
213 | parser->m_parsingStatus.parsing = XML_PARSING; | ||
214 | |||
215 | - parser->m_errorCode = parser->m_processor( | ||
216 | + parser->m_errorCode = callProcessor( | ||
217 | parser, parser->m_bufferPtr, parser->m_parseEndPtr, &parser->m_bufferPtr); | ||
218 | |||
219 | if (parser->m_errorCode != XML_ERROR_NONE) { | ||
220 | -- | ||
221 | 2.40.0 | ||
222 | |||
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 @@ | |||
1 | From 1b9d398517befeb944cbbadadf10992b07e96fa2 Mon Sep 17 00:00:00 2001 | ||
2 | From: Snild Dolkow <snild@sony.com> | ||
3 | Date: Mon, 4 Sep 2023 17:21:14 +0200 | ||
4 | Subject: [PATCH] [PATCH] Don't update partial token heuristic on error | ||
5 | |||
6 | Suggested-by: Sebastian Pipping <sebastian@pipping.org> | ||
7 | |||
8 | CVE: CVE-2023-52425 | ||
9 | |||
10 | Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/1b9d398517befeb944cbbadadf10992b07e96fa2] | ||
11 | |||
12 | Signed-off-by: Meenali Gupta <meenali.gupta@windriver.com> | ||
13 | --- | ||
14 | lib/xmlparse.c | 12 +++++++----- | ||
15 | 1 file changed, 7 insertions(+), 5 deletions(-) | ||
16 | |||
17 | diff --git a/lib/xmlparse.c b/lib/xmlparse.c | ||
18 | index 5695417..5c66f54 100644 | ||
19 | --- a/lib/xmlparse.c | ||
20 | +++ b/lib/xmlparse.c | ||
21 | @@ -979,11 +979,13 @@ callProcessor(XML_Parser parser, const char *start, const char *end, | ||
22 | } | ||
23 | } | ||
24 | const enum XML_Error ret = parser->m_processor(parser, start, end, endPtr); | ||
25 | - // if we consumed nothing, remember what we had on this parse attempt. | ||
26 | - if (*endPtr == start) { | ||
27 | - parser->m_partialTokenBytesBefore = have_now; | ||
28 | - } else { | ||
29 | - parser->m_partialTokenBytesBefore = 0; | ||
30 | + if (ret == XML_ERROR_NONE) { | ||
31 | + // if we consumed nothing, remember what we had on this parse attempt. | ||
32 | + if (*endPtr == start) { | ||
33 | + parser->m_partialTokenBytesBefore = have_now; | ||
34 | + } else { | ||
35 | + parser->m_partialTokenBytesBefore = 0; | ||
36 | + } | ||
37 | } | ||
38 | return ret; | ||
39 | } | ||
40 | -- | ||
41 | 2.40.0 | ||
42 | |||
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 @@ | |||
1 | From 09957b8ced725b96a95acff150facda93f03afe1 Mon Sep 17 00:00:00 2001 | ||
2 | From: Snild Dolkow <snild@sony.com> | ||
3 | Date: Thu, 26 Oct 2023 10:41:00 +0200 | ||
4 | Subject: [PATCH] Allow XML_GetBuffer() with len=0 on a fresh parser | ||
5 | |||
6 | len=0 was previously OK if there had previously been a non-zero call. | ||
7 | It makes sense to allow an application to work the same way on a | ||
8 | newly-created parser, and not have to care if its incoming buffer | ||
9 | happens to be 0. | ||
10 | |||
11 | CVE: CVE-2023-52425 | ||
12 | |||
13 | Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/09957b8ced725b96a95acff150facda93f03afe1] | ||
14 | |||
15 | Signed-off-by: Meenali Gupta <meenali.gupta@windriver.com> | ||
16 | --- | ||
17 | lib/xmlparse.c | 22 +++++++++++----------- | ||
18 | 1 file changed, 11 insertions(+), 11 deletions(-) | ||
19 | |||
20 | diff --git a/lib/xmlparse.c b/lib/xmlparse.c | ||
21 | index 5c66f54..5b112c6 100644 | ||
22 | --- a/lib/xmlparse.c | ||
23 | +++ b/lib/xmlparse.c | ||
24 | @@ -2095,7 +2095,8 @@ XML_GetBuffer(XML_Parser parser, int len) { | ||
25 | default:; | ||
26 | } | ||
27 | |||
28 | - if (len > EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferEnd)) { | ||
29 | + if (len > EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferEnd) | ||
30 | + || parser->m_buffer == NULL) { | ||
31 | #ifdef XML_CONTEXT_BYTES | ||
32 | int keep; | ||
33 | #endif /* defined XML_CONTEXT_BYTES */ | ||
34 | @@ -2118,8 +2119,9 @@ XML_GetBuffer(XML_Parser parser, int len) { | ||
35 | } | ||
36 | neededSize += keep; | ||
37 | #endif /* defined XML_CONTEXT_BYTES */ | ||
38 | - if (neededSize | ||
39 | - <= EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_buffer)) { | ||
40 | + if (parser->m_buffer && parser->m_bufferPtr | ||
41 | + && neededSize | ||
42 | + <= EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_buffer)) { | ||
43 | #ifdef XML_CONTEXT_BYTES | ||
44 | if (keep < EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer)) { | ||
45 | int offset | ||
46 | @@ -2133,14 +2135,12 @@ XML_GetBuffer(XML_Parser parser, int len) { | ||
47 | parser->m_bufferPtr -= offset; | ||
48 | } | ||
49 | #else | ||
50 | - if (parser->m_buffer && parser->m_bufferPtr) { | ||
51 | - memmove(parser->m_buffer, parser->m_bufferPtr, | ||
52 | - EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr)); | ||
53 | - parser->m_bufferEnd | ||
54 | - = parser->m_buffer | ||
55 | - + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr); | ||
56 | - parser->m_bufferPtr = parser->m_buffer; | ||
57 | - } | ||
58 | + memmove(parser->m_buffer, parser->m_bufferPtr, | ||
59 | + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr)); | ||
60 | + parser->m_bufferEnd | ||
61 | + = parser->m_buffer | ||
62 | + + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr); | ||
63 | + parser->m_bufferPtr = parser->m_buffer; | ||
64 | #endif /* not defined XML_CONTEXT_BYTES */ | ||
65 | } else { | ||
66 | char *newBuf; | ||
67 | -- | ||
68 | 2.40.0 | ||
69 | |||
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 @@ | |||
1 | From 9fe3672459c1bf10926b85f013aa1b623d855545 Mon Sep 17 00:00:00 2001 | ||
2 | From: Snild Dolkow <snild@sony.com> | ||
3 | Date: Mon, 18 Sep 2023 20:32:55 +0200 | ||
4 | Subject: [PATCH] tests: Run both with and without partial token heuristic | ||
5 | |||
6 | If we always run with the heuristic enabled, it may hide some bugs by | ||
7 | grouping up input into bigger parse attempts. | ||
8 | |||
9 | CI-fighting-assistance-by: Sebastian Pipping <sebastian@pipping.org> | ||
10 | |||
11 | CVE: CVE-2023-52425 | ||
12 | |||
13 | Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/9fe3672459c1bf10926b85f013aa1b623d855545] | ||
14 | |||
15 | Signed-off-by: Meenali Gupta <meenali.gupta@windriver.com> | ||
16 | --- | ||
17 | lib/internal.h | 3 +++ | ||
18 | lib/xmlparse.c | 5 ++++- | ||
19 | 2 files changed, 7 insertions(+), 1 deletion(-) | ||
20 | |||
21 | diff --git a/lib/internal.h b/lib/internal.h | ||
22 | index 03c8fde..1df417f 100644 | ||
23 | --- a/lib/internal.h | ||
24 | +++ b/lib/internal.h | ||
25 | @@ -31,6 +31,7 @@ | ||
26 | Copyright (c) 2016-2022 Sebastian Pipping <sebastian@pipping.org> | ||
27 | Copyright (c) 2018 Yury Gribov <tetra2005@gmail.com> | ||
28 | Copyright (c) 2019 David Loffredo <loffredo@steptools.com> | ||
29 | + Copyright (c) 2023 Sony Corporation / Snild Dolkow <snild@sony.com> | ||
30 | Licensed under the MIT license: | ||
31 | |||
32 | Permission is hereby granted, free of charge, to any person obtaining | ||
33 | @@ -160,6 +161,8 @@ unsigned long long testingAccountingGetCountBytesIndirect(XML_Parser parser); | ||
34 | const char *unsignedCharToPrintable(unsigned char c); | ||
35 | #endif | ||
36 | |||
37 | +extern XML_Bool g_reparseDeferralEnabledDefault; // written ONLY in runtests.c | ||
38 | + // | ||
39 | #ifdef __cplusplus | ||
40 | } | ||
41 | #endif | ||
42 | diff --git a/lib/xmlparse.c b/lib/xmlparse.c | ||
43 | index 5b112c6..be6dd92 100644 | ||
44 | --- a/lib/xmlparse.c | ||
45 | +++ b/lib/xmlparse.c | ||
46 | @@ -615,6 +615,8 @@ static unsigned long getDebugLevel(const char *variableName, | ||
47 | ? 0 \ | ||
48 | : ((*((pool)->ptr)++ = c), 1)) | ||
49 | |||
50 | +XML_Bool g_reparseDeferralEnabledDefault = XML_TRUE; // write ONLY in runtests.c | ||
51 | + // | ||
52 | struct XML_ParserStruct { | ||
53 | /* The first member must be m_userData so that the XML_GetUserData | ||
54 | macro works. */ | ||
55 | @@ -967,7 +969,8 @@ callProcessor(XML_Parser parser, const char *start, const char *end, | ||
56 | const char **endPtr) { | ||
57 | const size_t have_now = EXPAT_SAFE_PTR_DIFF(end, start); | ||
58 | |||
59 | - if (! parser->m_parsingStatus.finalBuffer) { | ||
60 | + if (g_reparseDeferralEnabledDefault | ||
61 | + && ! parser->m_parsingStatus.finalBuffer) { | ||
62 | // Heuristic: don't try to parse a partial token again until the amount of | ||
63 | // available data has increased significantly. | ||
64 | const size_t had_before = parser->m_partialTokenBytesBefore; | ||
65 | -- | ||
66 | 2.40.0 | ||
67 | |||
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 @@ | |||
1 | From 1d3162da8a85a398ab451aadd6c2ad19587e5a68 Mon Sep 17 00:00:00 2001 | ||
2 | From: Snild Dolkow <snild@sony.com> | ||
3 | Date: Mon, 11 Sep 2023 15:31:24 +0200 | ||
4 | Subject: [PATCH] Add app setting for enabling/disabling reparse heuristic | ||
5 | |||
6 | Suggested-by: Sebastian Pipping <sebastian@pipping.org> | ||
7 | CI-fighting-assistance-by: Sebastian Pipping <sebastian@pipping.org> | ||
8 | |||
9 | CVE: CVE-2023-52425 | ||
10 | |||
11 | Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/1d3162da8a85a398ab451aadd6c2ad19587e5a68] | ||
12 | |||
13 | Signed-off-by: Meenali Gupta <meenali.gupta@windriver.com> | ||
14 | --- | ||
15 | doc/reference.html | 30 ++++++++++++++++++++++++------ | ||
16 | lib/expat.h | 5 +++++ | ||
17 | lib/libexpat.def.cmake | 2 ++ | ||
18 | lib/xmlparse.c | 13 ++++++++++++- | ||
19 | 4 files changed, 43 insertions(+), 7 deletions(-) | ||
20 | |||
21 | diff --git a/doc/reference.html b/doc/reference.html | ||
22 | index 9953aa7..7dd9370 100644 | ||
23 | --- a/doc/reference.html | ||
24 | +++ b/doc/reference.html | ||
25 | @@ -151,10 +151,11 @@ interface.</p> | ||
26 | </ul> | ||
27 | </li> | ||
28 | <li> | ||
29 | - <a href="#billion-laughs">Billion Laughs Attack Protection</a> | ||
30 | + <a href="#attack-protection">Attack Protection</a> | ||
31 | <ul> | ||
32 | <li><a href="#XML_SetBillionLaughsAttackProtectionMaximumAmplification">XML_SetBillionLaughsAttackProtectionMaximumAmplification</a></li> | ||
33 | <li><a href="#XML_SetBillionLaughsAttackProtectionActivationThreshold">XML_SetBillionLaughsAttackProtectionActivationThreshold</a></li> | ||
34 | + <li><a href="#XML_SetReparseDeferralEnabled">XML_SetReparseDeferralEnabled</a></li> | ||
35 | </ul> | ||
36 | </li> | ||
37 | <li><a href="#miscellaneous">Miscellaneous Functions</a> | ||
38 | @@ -2123,11 +2124,7 @@ parse position may be before the beginning of the buffer.</p> | ||
39 | return NULL.</p> | ||
40 | </div> | ||
41 | |||
42 | -<h3><a name="billion-laughs">Billion Laughs Attack Protection</a></h3> | ||
43 | - | ||
44 | -<p>The functions in this section configure the built-in | ||
45 | - protection against various forms of | ||
46 | - <a href="https://en.wikipedia.org/wiki/Billion_laughs_attack">billion laughs attacks</a>.</p> | ||
47 | +<h3><a name="attack-protection">Attack Protection</a><a name="billion-laughs"></a></h3> | ||
48 | |||
49 | <h4 id="XML_SetBillionLaughsAttackProtectionMaximumAmplification">XML_SetBillionLaughsAttackProtectionMaximumAmplification</h4> | ||
50 | <pre class="fcndec"> | ||
51 | @@ -2215,6 +2212,27 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold(XML_Parser p, | ||
52 | </p> | ||
53 | </div> | ||
54 | |||
55 | +<h4 id="XML_SetReparseDeferralEnabled">XML_SetReparseDeferralEnabled</h4> | ||
56 | +<pre class="fcndec"> | ||
57 | +/* Added in Expat 2.6.0. */ | ||
58 | +XML_Bool XMLCALL | ||
59 | +XML_SetReparseDeferralEnabled(XML_Parser parser, XML_Bool enabled); | ||
60 | +</pre> | ||
61 | +<div class="fcndef"> | ||
62 | + <p> | ||
63 | + Large tokens may require many parse calls before enough data is available for Expat to parse it in full. | ||
64 | + If Expat retried parsing the token on every parse call, parsing could take quadratic time. | ||
65 | + To avoid this, Expat only retries once a significant amount of new data is available. | ||
66 | + This function allows disabling this behavior. | ||
67 | + </p> | ||
68 | + <p> | ||
69 | + The <code>enabled</code> argument should be <code>XML_TRUE</code> or <code>XML_FALSE</code>. | ||
70 | + </p> | ||
71 | + <p> | ||
72 | + Returns <code>XML_TRUE</code> on success, and <code>XML_FALSE</code> on error. | ||
73 | + </p> | ||
74 | +</div> | ||
75 | + | ||
76 | <h3><a name="miscellaneous">Miscellaneous functions</a></h3> | ||
77 | |||
78 | <p>The functions in this section either obtain state information from | ||
79 | diff --git a/lib/expat.h b/lib/expat.h | ||
80 | index 9e64174..73dda6d 100644 | ||
81 | --- a/lib/expat.h | ||
82 | +++ b/lib/expat.h | ||
83 | @@ -16,6 +16,7 @@ | ||
84 | Copyright (c) 2016 Thomas Beutlich <tc@tbeu.de> | ||
85 | Copyright (c) 2017 Rhodri James <rhodri@wildebeest.org.uk> | ||
86 | Copyright (c) 2022 Thijs Schreijer <thijs@thijsschreijer.nl> | ||
87 | + Copyright (c) 2023 Sony Corporation / Snild Dolkow <snild@sony.com> | ||
88 | Licensed under the MIT license: | ||
89 | |||
90 | Permission is hereby granted, free of charge, to any person obtaining | ||
91 | @@ -1054,6 +1055,10 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold( | ||
92 | XML_Parser parser, unsigned long long activationThresholdBytes); | ||
93 | #endif | ||
94 | |||
95 | +/* Added in Expat 2.6.0. */ | ||
96 | +XMLPARSEAPI(XML_Bool) | ||
97 | +XML_SetReparseDeferralEnabled(XML_Parser parser, XML_Bool enabled); | ||
98 | + | ||
99 | /* Expat follows the semantic versioning convention. | ||
100 | See http://semver.org. | ||
101 | */ | ||
102 | diff --git a/lib/libexpat.def.cmake b/lib/libexpat.def.cmake | ||
103 | index 61a4f00..10ee9cd 100644 | ||
104 | --- a/lib/libexpat.def.cmake | ||
105 | +++ b/lib/libexpat.def.cmake | ||
106 | @@ -77,3 +77,5 @@ EXPORTS | ||
107 | ; added with version 2.4.0 | ||
108 | @_EXPAT_COMMENT_DTD_OR_GE@ XML_SetBillionLaughsAttackProtectionActivationThreshold @69 | ||
109 | @_EXPAT_COMMENT_DTD_OR_GE@ XML_SetBillionLaughsAttackProtectionMaximumAmplification @70 | ||
110 | +; added with version 2.6.0 | ||
111 | + XML_SetReparseDeferralEnabled @71 | ||
112 | diff --git a/lib/xmlparse.c b/lib/xmlparse.c | ||
113 | index be6dd92..8cf32e0 100644 | ||
114 | --- a/lib/xmlparse.c | ||
115 | +++ b/lib/xmlparse.c | ||
116 | @@ -633,6 +633,7 @@ struct XML_ParserStruct { | ||
117 | XML_Index m_parseEndByteIndex; | ||
118 | const char *m_parseEndPtr; | ||
119 | size_t m_partialTokenBytesBefore; /* used in heuristic to avoid O(n^2) */ | ||
120 | + XML_Bool m_reparseDeferralEnabled; | ||
121 | XML_Char *m_dataBuf; | ||
122 | XML_Char *m_dataBufEnd; | ||
123 | XML_StartElementHandler m_startElementHandler; | ||
124 | @@ -969,7 +970,7 @@ callProcessor(XML_Parser parser, const char *start, const char *end, | ||
125 | const char **endPtr) { | ||
126 | const size_t have_now = EXPAT_SAFE_PTR_DIFF(end, start); | ||
127 | |||
128 | - if (g_reparseDeferralEnabledDefault | ||
129 | + if (parser->m_reparseDeferralEnabled | ||
130 | && ! parser->m_parsingStatus.finalBuffer) { | ||
131 | // Heuristic: don't try to parse a partial token again until the amount of | ||
132 | // available data has increased significantly. | ||
133 | @@ -1175,6 +1176,7 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) { | ||
134 | parser->m_parseEndByteIndex = 0; | ||
135 | parser->m_parseEndPtr = NULL; | ||
136 | parser->m_partialTokenBytesBefore = 0; | ||
137 | + parser->m_reparseDeferralEnabled = g_reparseDeferralEnabledDefault; | ||
138 | parser->m_declElementType = NULL; | ||
139 | parser->m_declAttributeId = NULL; | ||
140 | parser->m_declEntity = NULL; | ||
141 | @@ -2601,6 +2603,15 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold( | ||
142 | } | ||
143 | #endif /* XML_GE == 1 */ | ||
144 | |||
145 | +XML_Bool XMLCALL | ||
146 | +XML_SetReparseDeferralEnabled(XML_Parser parser, XML_Bool enabled) { | ||
147 | + if (parser != NULL && (enabled == XML_TRUE || enabled == XML_FALSE)) { | ||
148 | + parser->m_reparseDeferralEnabled = enabled; | ||
149 | + return XML_TRUE; | ||
150 | + } | ||
151 | + return XML_FALSE; | ||
152 | +} | ||
153 | + | ||
154 | /* Initially tag->rawName always points into the parse buffer; | ||
155 | for those TAG instances opened while the current parse buffer was | ||
156 | processed, and not yet closed, we need to store tag->rawName in a more | ||
157 | -- | ||
158 | 2.40.0 | ||
159 | |||
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 @@ | |||
1 | From 8ddd8e86aa446d02eb8d398972d3b10d4cad908a Mon Sep 17 00:00:00 2001 | ||
2 | From: Snild Dolkow <snild@sony.com> | ||
3 | Date: Fri, 29 Sep 2023 10:14:59 +0200 | ||
4 | Subject: [PATCH] Try to parse even when incoming len is zero | ||
5 | |||
6 | If the reparse deferral setting has changed, it may be possible to | ||
7 | finish a token. | ||
8 | |||
9 | CVE: CVE-2023-52425 | ||
10 | |||
11 | Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/8ddd8e86aa446d02eb8d398972d3b10d4cad908a] | ||
12 | |||
13 | Signed-off-by: Meenali Gupta <meenali.gupta@windriver.com> | ||
14 | --- | ||
15 | lib/xmlparse.c | 55 ++++++++------------------------------------------ | ||
16 | 1 file changed, 8 insertions(+), 47 deletions(-) | ||
17 | |||
18 | diff --git a/lib/xmlparse.c b/lib/xmlparse.c | ||
19 | index 8cf32e0..f4ff66e 100644 | ||
20 | --- a/lib/xmlparse.c | ||
21 | +++ b/lib/xmlparse.c | ||
22 | @@ -1896,46 +1896,8 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) { | ||
23 | parser->m_parsingStatus.parsing = XML_PARSING; | ||
24 | } | ||
25 | |||
26 | - if (len == 0) { | ||
27 | - parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal; | ||
28 | - if (! isFinal) | ||
29 | - return XML_STATUS_OK; | ||
30 | - parser->m_positionPtr = parser->m_bufferPtr; | ||
31 | - parser->m_parseEndPtr = parser->m_bufferEnd; | ||
32 | - | ||
33 | - /* If data are left over from last buffer, and we now know that these | ||
34 | - data are the final chunk of input, then we have to check them again | ||
35 | - to detect errors based on that fact. | ||
36 | - */ | ||
37 | - parser->m_errorCode | ||
38 | - = callProcessor(parser, parser->m_bufferPtr, parser->m_parseEndPtr, | ||
39 | - &parser->m_bufferPtr); | ||
40 | - | ||
41 | - if (parser->m_errorCode == XML_ERROR_NONE) { | ||
42 | - switch (parser->m_parsingStatus.parsing) { | ||
43 | - case XML_SUSPENDED: | ||
44 | - /* While we added no new data, the finalBuffer flag may have caused | ||
45 | - * us to parse previously-unparsed data in the internal buffer. | ||
46 | - * If that triggered a callback to the application, it would have | ||
47 | - * had an opportunity to suspend parsing. */ | ||
48 | - XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr, | ||
49 | - parser->m_bufferPtr, &parser->m_position); | ||
50 | - parser->m_positionPtr = parser->m_bufferPtr; | ||
51 | - return XML_STATUS_SUSPENDED; | ||
52 | - case XML_INITIALIZED: | ||
53 | - case XML_PARSING: | ||
54 | - parser->m_parsingStatus.parsing = XML_FINISHED; | ||
55 | - /* fall through */ | ||
56 | - default: | ||
57 | - return XML_STATUS_OK; | ||
58 | - } | ||
59 | - } | ||
60 | - parser->m_eventEndPtr = parser->m_eventPtr; | ||
61 | - parser->m_processor = errorProcessor; | ||
62 | - return XML_STATUS_ERROR; | ||
63 | - } | ||
64 | #ifndef XML_CONTEXT_BYTES | ||
65 | - else if (parser->m_bufferPtr == parser->m_bufferEnd) { | ||
66 | + if (parser->m_bufferPtr == parser->m_bufferEnd) { | ||
67 | const char *end; | ||
68 | int nLeftOver; | ||
69 | enum XML_Status result; | ||
70 | @@ -2006,15 +1968,14 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) { | ||
71 | return result; | ||
72 | } | ||
73 | #endif /* not defined XML_CONTEXT_BYTES */ | ||
74 | - else { | ||
75 | - void *buff = XML_GetBuffer(parser, len); | ||
76 | - if (buff == NULL) | ||
77 | - return XML_STATUS_ERROR; | ||
78 | - else { | ||
79 | - memcpy(buff, s, len); | ||
80 | - return XML_ParseBuffer(parser, len, isFinal); | ||
81 | - } | ||
82 | + void *buff = XML_GetBuffer(parser, len); | ||
83 | + if (buff == NULL) | ||
84 | + return XML_STATUS_ERROR; | ||
85 | + if (len > 0) { | ||
86 | + assert(s != NULL); // make sure s==NULL && len!=0 was rejected above | ||
87 | + memcpy(buff, s, len); | ||
88 | } | ||
89 | + return XML_ParseBuffer(parser, len, isFinal); | ||
90 | } | ||
91 | |||
92 | enum XML_Status XMLCALL | ||
93 | -- | ||
94 | 2.40.0 | ||
95 | |||
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 @@ | |||
1 | From ad9c01be8ee5d3d5cac2bfd3949ad764541d35e7 Mon Sep 17 00:00:00 2001 | ||
2 | From: Snild Dolkow <snild@sony.com> | ||
3 | Date: Thu, 26 Oct 2023 13:55:02 +0200 | ||
4 | Subject: [PATCH] Make external entity parser inherit partial token heuristic | ||
5 | setting | ||
6 | |||
7 | The test is essentially a copy of the existing test for the setter, | ||
8 | adapted to run on the external parser instead of the original one. | ||
9 | |||
10 | Suggested-by: Sebastian Pipping <sebastian@pipping.org> | ||
11 | CI-fighting-assistance-by: Sebastian Pipping <sebastian@pipping.org> | ||
12 | |||
13 | CVE: CVE-2023-52425 | ||
14 | |||
15 | Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/ad9c01be8ee5d3d5cac2bfd3949ad764541d35e7] | ||
16 | |||
17 | Signed-off-by: Meenali Gupta <meenali.gupta@windriver.com> | ||
18 | --- | ||
19 | lib/xmlparse.c | 3 +++ | ||
20 | 1 file changed, 3 insertions(+) | ||
21 | |||
22 | diff --git a/lib/xmlparse.c b/lib/xmlparse.c | ||
23 | index f4ff66e..6746d70 100644 | ||
24 | --- a/lib/xmlparse.c | ||
25 | +++ b/lib/xmlparse.c | ||
26 | @@ -1346,6 +1346,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context, | ||
27 | to worry which hash secrets each table has. | ||
28 | */ | ||
29 | unsigned long oldhash_secret_salt; | ||
30 | + XML_Bool oldReparseDeferralEnabled; | ||
31 | |||
32 | /* Validate the oldParser parameter before we pull everything out of it */ | ||
33 | if (oldParser == NULL) | ||
34 | @@ -1390,6 +1391,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context, | ||
35 | to worry which hash secrets each table has. | ||
36 | */ | ||
37 | oldhash_secret_salt = parser->m_hash_secret_salt; | ||
38 | + oldReparseDeferralEnabled = parser->m_reparseDeferralEnabled; | ||
39 | |||
40 | #ifdef XML_DTD | ||
41 | if (! context) | ||
42 | @@ -1442,6 +1444,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context, | ||
43 | parser->m_defaultExpandInternalEntities = oldDefaultExpandInternalEntities; | ||
44 | parser->m_ns_triplets = oldns_triplets; | ||
45 | parser->m_hash_secret_salt = oldhash_secret_salt; | ||
46 | + parser->m_reparseDeferralEnabled = oldReparseDeferralEnabled; | ||
47 | parser->m_parentParser = oldParser; | ||
48 | #ifdef XML_DTD | ||
49 | parser->m_paramEntityParsing = oldParamEntityParsing; | ||
50 | -- | ||
51 | 2.40.0 | ||
52 | |||
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 @@ | |||
1 | From 60b74209899a67d426d208662674b55a5eed918c Mon Sep 17 00:00:00 2001 | ||
2 | From: Snild Dolkow <snild@sony.com> | ||
3 | Date: Wed, 4 Oct 2023 16:00:14 +0200 | ||
4 | Subject: [PATCH] Bypass partial token heuristic when close to maximum buffer | ||
5 | size | ||
6 | |||
7 | For huge tokens, we may end up in a situation where the partial token | ||
8 | parse deferral heuristic demands more bytes than Expat's maximum buffer | ||
9 | size (currently ~half of INT_MAX) could fit. | ||
10 | |||
11 | INT_MAX/2 is 1024 MiB on most systems. Clearly, a token of 950 MiB could | ||
12 | fit in that buffer, but the reparse threshold might be such that | ||
13 | callProcessor() will defer it, allowing the app to keep filling the | ||
14 | buffer until XML_GetBuffer() eventually returns a memory error. | ||
15 | |||
16 | By bypassing the heuristic when we're getting close to the maximum | ||
17 | buffer size, it will once again be possible to parse tokens in the size | ||
18 | range INT_MAX/2/ratio < size < INT_MAX/2 reliably. | ||
19 | |||
20 | We subtract the last buffer fill size as a way to detect that the next | ||
21 | XML_GetBuffer() call has a risk of returning a memory error -- assuming | ||
22 | that the application is likely to keep using the same (or smaller) fill. | ||
23 | |||
24 | We subtract XML_CONTEXT_BYTES because that's the maximum amount of bytes | ||
25 | that could remain at the start of the buffer, preceding the partial | ||
26 | token. Technically, it could be fewer bytes, but XML_CONTEXT_BYTES is | ||
27 | normally small relative to INT_MAX, and is much simpler to use. | ||
28 | |||
29 | Co-authored-by: Sebastian Pipping <sebastian@pipping.org> | ||
30 | |||
31 | CVE: CVE-2023-52425 | ||
32 | |||
33 | Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/60b74209899a67d426d208662674b55a5eed918c] | ||
34 | |||
35 | Signed-off-by: Meenali Gupta <meenali.gupta@windriver.com> | ||
36 | --- | ||
37 | lib/xmlparse.c | 23 ++++++++++++++++++++++- | ||
38 | 1 file changed, 22 insertions(+), 1 deletion(-) | ||
39 | |||
40 | diff --git a/lib/xmlparse.c b/lib/xmlparse.c | ||
41 | index 6746d70..32c57f6 100644 | ||
42 | --- a/lib/xmlparse.c | ||
43 | +++ b/lib/xmlparse.c | ||
44 | @@ -205,6 +205,8 @@ typedef char ICHAR; | ||
45 | /* Do safe (NULL-aware) pointer arithmetic */ | ||
46 | #define EXPAT_SAFE_PTR_DIFF(p, q) (((p) && (q)) ? ((p) - (q)) : 0) | ||
47 | |||
48 | +#define EXPAT_MIN(a, b) (((a) < (b)) ? (a) : (b)) | ||
49 | + | ||
50 | #include "internal.h" | ||
51 | #include "xmltok.h" | ||
52 | #include "xmlrole.h" | ||
53 | @@ -634,6 +636,7 @@ struct XML_ParserStruct { | ||
54 | const char *m_parseEndPtr; | ||
55 | size_t m_partialTokenBytesBefore; /* used in heuristic to avoid O(n^2) */ | ||
56 | XML_Bool m_reparseDeferralEnabled; | ||
57 | + int m_lastBufferRequestSize; | ||
58 | XML_Char *m_dataBuf; | ||
59 | XML_Char *m_dataBufEnd; | ||
60 | XML_StartElementHandler m_startElementHandler; | ||
61 | @@ -975,7 +978,18 @@ callProcessor(XML_Parser parser, const char *start, const char *end, | ||
62 | // Heuristic: don't try to parse a partial token again until the amount of | ||
63 | // available data has increased significantly. | ||
64 | const size_t had_before = parser->m_partialTokenBytesBefore; | ||
65 | - const bool enough = (have_now >= 2 * had_before); | ||
66 | + // ...but *do* try anyway if we're close to reaching the max buffer size. | ||
67 | + size_t close_to_maxbuf = INT_MAX / 2 + (INT_MAX & 1); // round up | ||
68 | +#if XML_CONTEXT_BYTES > 0 | ||
69 | + // subtract XML_CONTEXT_BYTES, but don't go below zero | ||
70 | + close_to_maxbuf -= EXPAT_MIN(close_to_maxbuf, XML_CONTEXT_BYTES); | ||
71 | +#endif | ||
72 | + // subtract the last buffer fill size, but don't go below zero | ||
73 | + // m_lastBufferRequestSize is never assigned a value < 0, so the cast is ok | ||
74 | + close_to_maxbuf | ||
75 | + -= EXPAT_MIN(close_to_maxbuf, (size_t)parser->m_lastBufferRequestSize); | ||
76 | + const bool enough | ||
77 | + = (have_now >= 2 * had_before) || (have_now > close_to_maxbuf); | ||
78 | |||
79 | if (! enough) { | ||
80 | *endPtr = start; // callers may expect this to be set | ||
81 | @@ -1177,6 +1191,7 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) { | ||
82 | parser->m_parseEndPtr = NULL; | ||
83 | parser->m_partialTokenBytesBefore = 0; | ||
84 | parser->m_reparseDeferralEnabled = g_reparseDeferralEnabledDefault; | ||
85 | + parser->m_lastBufferRequestSize = 0; | ||
86 | parser->m_declElementType = NULL; | ||
87 | parser->m_declAttributeId = NULL; | ||
88 | parser->m_declEntity = NULL; | ||
89 | @@ -1911,6 +1926,9 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) { | ||
90 | parser->m_processor = errorProcessor; | ||
91 | return XML_STATUS_ERROR; | ||
92 | } | ||
93 | + // though this isn't a buffer request, we assume that `len` is the app's | ||
94 | + // preferred buffer fill size, and therefore save it here. | ||
95 | + parser->m_lastBufferRequestSize = len; | ||
96 | parser->m_parseEndByteIndex += len; | ||
97 | parser->m_positionPtr = s; | ||
98 | parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal; | ||
99 | @@ -2064,6 +2082,9 @@ XML_GetBuffer(XML_Parser parser, int len) { | ||
100 | default:; | ||
101 | } | ||
102 | |||
103 | + // whether or not the request succeeds, `len` seems to be the app's preferred | ||
104 | + // buffer fill size; remember it. | ||
105 | + parser->m_lastBufferRequestSize = len; | ||
106 | if (len > EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferEnd) | ||
107 | || parser->m_buffer == NULL) { | ||
108 | #ifdef XML_CONTEXT_BYTES | ||
109 | -- | ||
110 | 2.40.0 | ||
111 | |||
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 @@ | |||
1 | From 3d8141d26a3b01ff948e00956cb0723a89dadf7f Mon Sep 17 00:00:00 2001 | ||
2 | From: Snild Dolkow <snild@sony.com> | ||
3 | Date: Mon, 20 Nov 2023 16:11:24 +0100 | ||
4 | Subject: [PATCH] Bypass partial token heuristic when nearing full buffer | ||
5 | |||
6 | ...instead of only when approaching the maximum buffer size INT/2+1. | ||
7 | |||
8 | We'd like to give applications a chance to finish parsing a large token | ||
9 | before buffer reallocation, in case the reallocation fails. | ||
10 | |||
11 | By bypassing the reparse deferral heuristic when getting close to the | ||
12 | filling the buffer, we give them this chance -- if the whole token is | ||
13 | present in the buffer, it will be parsed at that time. | ||
14 | |||
15 | This may come at the cost of some extra reparse attempts. For a token | ||
16 | of n bytes, these extra parses cause us to scan over a maximum of | ||
17 | 2n bytes (... + n/8 + n/4 + n/2 + n). Therefore, parsing of big tokens | ||
18 | remains O(n) in regard how many bytes we scan in attempts to parse. The | ||
19 | cost in reality is lower than that, since the reparses that happen due | ||
20 | to the bypass will affect m_partialTokenBytesBefore, delaying the next | ||
21 | ratio-based reparse. Furthermore, only the first token that "breaks | ||
22 | through" a buffer ceiling takes that extra reparse attempt; subsequent | ||
23 | large tokens will only bypass the heuristic if they manage to hit the | ||
24 | new buffer ceiling. | ||
25 | |||
26 | Note that this cost analysis depends on the assumption that Expat grows | ||
27 | its buffer by doubling it (or, more generally, grows it exponentially). | ||
28 | If this changes, the cost of this bypass may increase. Hopefully, this | ||
29 | would be caught by test_big_tokens_take_linear_time or the new test. | ||
30 | |||
31 | The bypass logic assumes that the application uses a consistent fill. | ||
32 | If the app increases its fill size, it may miss the bypass (and the | ||
33 | normal heuristic will apply). If the app decreases its fill size, the | ||
34 | bypass may be hit multiple times for the same buffer size. The very | ||
35 | worst case would be to always fill half of the remaining buffer space, | ||
36 | in which case parsing of a large n-byte token becomes O(n log n). | ||
37 | |||
38 | As an added bonus, the new test case should be faster than the old one, | ||
39 | since it doesn't have to go all the way to 1GiB to check the behavior. | ||
40 | |||
41 | Finally, this change necessitated a small modification to two existing | ||
42 | tests related to reparse deferral. These tests are testing the deferral | ||
43 | enabled setting, and assume that reparsing will not happen for any other | ||
44 | reason. By pre-growing the buffer, we make sure that this new deferral | ||
45 | does not affect those test cases. | ||
46 | |||
47 | CVE: CVE-2023-52425 | ||
48 | |||
49 | Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/3d8141d26a3b01ff948e00956cb0723a89dadf7f] | ||
50 | |||
51 | Signed-off-by: Meenali Gupta <meenali.gupta@windriver.com> | ||
52 | --- | ||
53 | lib/xmlparse.c | 16 ++++++++-------- | ||
54 | 1 file changed, 8 insertions(+), 8 deletions(-) | ||
55 | |||
56 | diff --git a/lib/xmlparse.c b/lib/xmlparse.c | ||
57 | index 32c57f6..2830c1e 100644 | ||
58 | --- a/lib/xmlparse.c | ||
59 | +++ b/lib/xmlparse.c | ||
60 | @@ -978,18 +978,18 @@ callProcessor(XML_Parser parser, const char *start, const char *end, | ||
61 | // Heuristic: don't try to parse a partial token again until the amount of | ||
62 | // available data has increased significantly. | ||
63 | const size_t had_before = parser->m_partialTokenBytesBefore; | ||
64 | - // ...but *do* try anyway if we're close to reaching the max buffer size. | ||
65 | - size_t close_to_maxbuf = INT_MAX / 2 + (INT_MAX & 1); // round up | ||
66 | + // ...but *do* try anyway if we're close to causing a reallocation. | ||
67 | + size_t available_buffer | ||
68 | + = EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer); | ||
69 | #if XML_CONTEXT_BYTES > 0 | ||
70 | - // subtract XML_CONTEXT_BYTES, but don't go below zero | ||
71 | - close_to_maxbuf -= EXPAT_MIN(close_to_maxbuf, XML_CONTEXT_BYTES); | ||
72 | + available_buffer -= EXPAT_MIN(available_buffer, XML_CONTEXT_BYTES); | ||
73 | #endif | ||
74 | - // subtract the last buffer fill size, but don't go below zero | ||
75 | + available_buffer | ||
76 | + += EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferEnd); | ||
77 | // m_lastBufferRequestSize is never assigned a value < 0, so the cast is ok | ||
78 | - close_to_maxbuf | ||
79 | - -= EXPAT_MIN(close_to_maxbuf, (size_t)parser->m_lastBufferRequestSize); | ||
80 | const bool enough | ||
81 | - = (have_now >= 2 * had_before) || (have_now > close_to_maxbuf); | ||
82 | + = (have_now >= 2 * had_before) | ||
83 | + || ((size_t)parser->m_lastBufferRequestSize > available_buffer); | ||
84 | |||
85 | if (! enough) { | ||
86 | *endPtr = start; // callers may expect this to be set | ||
87 | -- | ||
88 | 2.40.0 | ||
89 | |||
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 @@ | |||
1 | From 119ae277abaabd4d17b2e64300fec712ef403b28 Mon Sep 17 00:00:00 2001 | ||
2 | From: Snild Dolkow <snild@sony.com> | ||
3 | Date: Thu, 28 Sep 2023 18:26:19 +0200 | ||
4 | Subject: [PATCH] Grow buffer based on current size Until now, the buffer size | ||
5 | to grow to has been calculated based on the distance from the current parse | ||
6 | position to the end of the buffer. This means that the size of any | ||
7 | already-parsed data was not considered, leading to inconsistent buffer | ||
8 | growth. | ||
9 | |||
10 | There was also a special case in XML_Parse() when XML_CONTEXT_BYTES was | ||
11 | zero, where the buffer size would be set to twice the incoming string | ||
12 | length. This patch replaces this with an XML_GetBuffer() call. | ||
13 | |||
14 | Growing the buffer based on its total size makes its growth consistent. | ||
15 | |||
16 | The commit includes a test that checks that we can reach the max buffer | ||
17 | size (usually INT_MAX/2 + 1) regardless of previously parsed content. | ||
18 | |||
19 | GitHub CI couldn't allocate the full 1GiB with MinGW/wine32, though it | ||
20 | works locally with the same compiler and wine version. As a workaround, | ||
21 | the test tries to malloc 1GiB, and reduces `maxbuf` to 512MiB in case | ||
22 | of failure. | ||
23 | |||
24 | CVE: CVE-2023-52425 | ||
25 | |||
26 | Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/119ae277abaabd4d17b2e64300fec712ef403b28] | ||
27 | |||
28 | Signed-off-by: Meenali Gupta <meenali.gupta@windriver.com> | ||
29 | --- | ||
30 | lib/xmlparse.c | 33 ++++++++++++++++----------------- | ||
31 | 1 file changed, 16 insertions(+), 17 deletions(-) | ||
32 | |||
33 | diff --git a/lib/xmlparse.c b/lib/xmlparse.c | ||
34 | index 2830c1e..81f9bb3 100644 | ||
35 | --- a/lib/xmlparse.c | ||
36 | +++ b/lib/xmlparse.c | ||
37 | @@ -1961,23 +1961,22 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) { | ||
38 | &parser->m_position); | ||
39 | nLeftOver = s + len - end; | ||
40 | if (nLeftOver) { | ||
41 | - if (parser->m_buffer == NULL | ||
42 | - || nLeftOver > parser->m_bufferLim - parser->m_buffer) { | ||
43 | - /* avoid _signed_ integer overflow */ | ||
44 | - char *temp = NULL; | ||
45 | - const int bytesToAllocate = (int)((unsigned)len * 2U); | ||
46 | - if (bytesToAllocate > 0) { | ||
47 | - temp = (char *)REALLOC(parser, parser->m_buffer, bytesToAllocate); | ||
48 | - } | ||
49 | - if (temp == NULL) { | ||
50 | - parser->m_errorCode = XML_ERROR_NO_MEMORY; | ||
51 | - parser->m_eventPtr = parser->m_eventEndPtr = NULL; | ||
52 | - parser->m_processor = errorProcessor; | ||
53 | - return XML_STATUS_ERROR; | ||
54 | - } | ||
55 | - parser->m_buffer = temp; | ||
56 | - parser->m_bufferLim = parser->m_buffer + bytesToAllocate; | ||
57 | + // Back up and restore the parsing status to avoid XML_ERROR_SUSPENDED | ||
58 | + // (and XML_ERROR_FINISHED) from XML_GetBuffer. | ||
59 | + const enum XML_Parsing originalStatus = parser->m_parsingStatus.parsing; | ||
60 | + parser->m_parsingStatus.parsing = XML_PARSING; | ||
61 | + void *const temp = XML_GetBuffer(parser, nLeftOver); | ||
62 | + parser->m_parsingStatus.parsing = originalStatus; | ||
63 | + if (temp == NULL) { | ||
64 | + // NOTE: parser->m_errorCode has already been set by XML_GetBuffer(). | ||
65 | + parser->m_eventPtr = parser->m_eventEndPtr = NULL; | ||
66 | + parser->m_processor = errorProcessor; | ||
67 | + return XML_STATUS_ERROR; | ||
68 | } | ||
69 | + // Since we know that the buffer was empty and XML_CONTEXT_BYTES is 0, we | ||
70 | + // don't have any data to preserve, and can copy straight into the start | ||
71 | + // of the buffer rather than the GetBuffer return pointer (which may be | ||
72 | + // pointing further into the allocated buffer). | ||
73 | memcpy(parser->m_buffer, end, nLeftOver); | ||
74 | } | ||
75 | parser->m_bufferPtr = parser->m_buffer; | ||
76 | @@ -2135,7 +2134,7 @@ XML_GetBuffer(XML_Parser parser, int len) { | ||
77 | } else { | ||
78 | char *newBuf; | ||
79 | int bufferSize | ||
80 | - = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferPtr); | ||
81 | + = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_buffer); | ||
82 | if (bufferSize == 0) | ||
83 | bufferSize = INIT_BUFFER_SIZE; | ||
84 | do { | ||
85 | -- | ||
86 | 2.40.0 | ||
87 | |||
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 | |||
22 | file://CVE-2023-52426-009.patch \ | 22 | file://CVE-2023-52426-009.patch \ |
23 | file://CVE-2023-52426-010.patch \ | 23 | file://CVE-2023-52426-010.patch \ |
24 | file://CVE-2023-52426-011.patch \ | 24 | file://CVE-2023-52426-011.patch \ |
25 | file://CVE-2023-52425-0001.patch \ | ||
26 | file://CVE-2023-52425-0002.patch \ | ||
27 | file://CVE-2023-52425-0003.patch \ | ||
28 | file://CVE-2023-52425-0004.patch \ | ||
29 | file://CVE-2023-52425-0005.patch \ | ||
30 | file://CVE-2023-52425-0006.patch \ | ||
31 | file://CVE-2023-52425-0007.patch \ | ||
32 | file://CVE-2023-52425-0008.patch \ | ||
33 | file://CVE-2023-52425-0009.patch \ | ||
34 | file://CVE-2023-52425-0010.patch \ | ||
35 | file://CVE-2023-52425-0011.patch \ | ||
36 | file://CVE-2023-52425-0012.patch \ | ||
25 | " | 37 | " |
26 | 38 | ||
27 | UPSTREAM_CHECK_URI = "https://github.com/libexpat/libexpat/releases/" | 39 | UPSTREAM_CHECK_URI = "https://github.com/libexpat/libexpat/releases/" |