Compare commits
10 Commits
6ad0537066
...
77dd4a0e20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
77dd4a0e20 | ||
|
|
741a73e2d5 | ||
|
|
bd13e657ad | ||
|
|
0f4b9d1705 | ||
|
|
b6a11ec365 | ||
|
|
b1ec6c4caa | ||
|
|
a39fbb7c4a | ||
|
|
cbc39e564c | ||
|
|
0eaf306133 | ||
|
|
756d8acea5 |
223
backport-001-CVE-2023-52425.patch
Normal file
223
backport-001-CVE-2023-52425.patch
Normal file
@ -0,0 +1,223 @@
|
||||
From 6cc9677838ce4e68680f7877d71032ca6481ee56 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
|
||||
|
||||
Reference: https://github.com/libexpat/libexpat/pull/789/commits/9cdf9b8d77d5c2c2a27d15fb68dd3f83cafb45a1
|
||||
Conflict: remove basic_test.c
|
||||
change xmlparse.c
|
||||
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
When the parse buffer contains the starting bytes of a token but not
|
||||
all of them, we cannot parse the token to completion. We call this a
|
||||
partial token. When this happens, the parse position is reset to the
|
||||
start of the token, and the parse() call returns. The client is then
|
||||
expected to provide more data and call parse() again.
|
||||
|
||||
In extreme cases, this means that the bytes of a token may be parsed
|
||||
many times: once for every buffer refill required before the full token
|
||||
is present in the buffer.
|
||||
|
||||
Math:
|
||||
Assume there's a token of T bytes
|
||||
Assume the client fills the buffer in chunks of X bytes
|
||||
We'll try to parse X, 2X, 3X, 4X ... until mX == T (technically >=)
|
||||
That's (m²+m)X/2 = (T²/X+T)/2 bytes parsed (arithmetic progression)
|
||||
While it is alleviated by larger refills, this amounts to O(T²)
|
||||
|
||||
Expat grows its internal buffer by doubling it when necessary, but has
|
||||
no way to inform the client about how much space is available. Instead,
|
||||
we add a heuristic that skips parsing when we've repeatedly stopped on
|
||||
an incomplete token. Specifically:
|
||||
|
||||
* Only try to parse if we have a certain amount of data buffered
|
||||
* Every time we stop on an incomplete token, double the threshold
|
||||
* As soon as any token completes, the threshold is reset
|
||||
|
||||
This means that when we get stuck on an incomplete token, the threshold
|
||||
grows exponentially, effectively making the client perform larger buffer
|
||||
fills, limiting how many times we can end up re-parsing the same bytes.
|
||||
|
||||
Math:
|
||||
Assume there's a token of T bytes
|
||||
Assume the client fills the buffer in chunks of X bytes
|
||||
We'll try to parse X, 2X, 4X, 8X ... until (2^k)X == T (or larger)
|
||||
That's (2^(k+1)-1)X bytes parsed -- e.g. 15X if T = 8X
|
||||
This is equal to 2T-X, which amounts to O(T)
|
||||
|
||||
We could've chosen a faster growth rate, e.g. 4 or 8. Those seem to
|
||||
increase performance further, at the cost of further increasing the
|
||||
risk of growing the buffer more than necessary. This can easily be
|
||||
adjusted in the future, if desired.
|
||||
|
||||
This is all completely transparent to the client, except for:
|
||||
1. possible delay of some callbacks (when our heuristic overshoots)
|
||||
2. apps that never do isFinal=XML_TRUE could miss data at the end
|
||||
|
||||
For the affected testdata, this change shows a 100-400x speedup.
|
||||
The recset.xml benchmark shows no clear change either way.
|
||||
|
||||
Before:
|
||||
benchmark -n ../testdata/largefiles/recset.xml 65535 3
|
||||
3 loops, with buffer size 65535. Average time per loop: 0.270223
|
||||
benchmark -n ../testdata/largefiles/aaaaaa_attr.xml 4096 3
|
||||
3 loops, with buffer size 4096. Average time per loop: 15.033048
|
||||
benchmark -n ../testdata/largefiles/aaaaaa_cdata.xml 4096 3
|
||||
3 loops, with buffer size 4096. Average time per loop: 0.018027
|
||||
benchmark -n ../testdata/largefiles/aaaaaa_comment.xml 4096 3
|
||||
3 loops, with buffer size 4096. Average time per loop: 11.775362
|
||||
benchmark -n ../testdata/largefiles/aaaaaa_tag.xml 4096 3
|
||||
3 loops, with buffer size 4096. Average time per loop: 11.711414
|
||||
benchmark -n ../testdata/largefiles/aaaaaa_text.xml 4096 3
|
||||
3 loops, with buffer size 4096. Average time per loop: 0.019362
|
||||
|
||||
After:
|
||||
./run.sh benchmark -n ../testdata/largefiles/recset.xml 65535 3
|
||||
3 loops, with buffer size 65535. Average time per loop: 0.269030
|
||||
./run.sh benchmark -n ../testdata/largefiles/aaaaaa_attr.xml 4096 3
|
||||
3 loops, with buffer size 4096. Average time per loop: 0.044794
|
||||
./run.sh benchmark -n ../testdata/largefiles/aaaaaa_cdata.xml 4096 3
|
||||
3 loops, with buffer size 4096. Average time per loop: 0.016377
|
||||
./run.sh benchmark -n ../testdata/largefiles/aaaaaa_comment.xml 4096 3
|
||||
3 loops, with buffer size 4096. Average time per loop: 0.027022
|
||||
./run.sh benchmark -n ../testdata/largefiles/aaaaaa_tag.xml 4096 3
|
||||
3 loops, with buffer size 4096. Average time per loop: 0.099360
|
||||
./run.sh benchmark -n ../testdata/largefiles/aaaaaa_text.xml 4096 3
|
||||
3 loops, with buffer size 4096. Average time per loop: 0.017956
|
||||
---
|
||||
lib/xmlparse.c | 58 +++++++++++++++++++++++++++++---------------
|
||||
1 file changed, 39 insertions(+), 19 deletions(-)
|
||||
|
||||
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
|
||||
index 5ba56eae..32df1eb9 100644
|
||||
--- a/lib/xmlparse.c
|
||||
+++ b/lib/xmlparse.c
|
||||
@@ -65,6 +65,7 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
+#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h> /* memset(), memcpy() */
|
||||
#include <assert.h>
|
||||
@@ -613,6 +614,7 @@ struct XML_ParserStruct {
|
||||
const char *m_bufferLim;
|
||||
XML_Index m_parseEndByteIndex;
|
||||
const char *m_parseEndPtr;
|
||||
+ size_t m_partialTokenBytesBefore; /* used in heuristic to avoid O(n^2) */
|
||||
XML_Char *m_dataBuf;
|
||||
XML_Char *m_dataBufEnd;
|
||||
XML_StartElementHandler m_startElementHandler;
|
||||
@@ -944,6 +946,32 @@ get_hash_secret_salt(XML_Parser parser) {
|
||||
return parser->m_hash_secret_salt;
|
||||
}
|
||||
|
||||
+static enum XML_Error
|
||||
+callProcessor(XML_Parser parser, const char *start, const char *end,
|
||||
+ const char **endPtr) {
|
||||
+ const size_t have_now = EXPAT_SAFE_PTR_DIFF(end, start);
|
||||
+
|
||||
+ if (! parser->m_parsingStatus.finalBuffer) {
|
||||
+ // Heuristic: don't try to parse a partial token again until the amount of
|
||||
+ // available data has increased significantly.
|
||||
+ const size_t had_before = parser->m_partialTokenBytesBefore;
|
||||
+ const bool enough = (have_now >= 2 * had_before);
|
||||
+
|
||||
+ if (! enough) {
|
||||
+ *endPtr = start; // callers may expect this to be set
|
||||
+ return XML_ERROR_NONE;
|
||||
+ }
|
||||
+ }
|
||||
+ const enum XML_Error ret = parser->m_processor(parser, start, end, endPtr);
|
||||
+ // if we consumed nothing, remember what we had on this parse attempt.
|
||||
+ if (*endPtr == start) {
|
||||
+ parser->m_partialTokenBytesBefore = have_now;
|
||||
+ } else {
|
||||
+ parser->m_partialTokenBytesBefore = 0;
|
||||
+ }
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
static XML_Bool /* only valid for root parser */
|
||||
startParsing(XML_Parser parser) {
|
||||
/* hash functions must be initialized before setContext() is called */
|
||||
@@ -1117,6 +1145,7 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) {
|
||||
parser->m_bufferEnd = parser->m_buffer;
|
||||
parser->m_parseEndByteIndex = 0;
|
||||
parser->m_parseEndPtr = NULL;
|
||||
+ parser->m_partialTokenBytesBefore = 0;
|
||||
parser->m_declElementType = NULL;
|
||||
parser->m_declAttributeId = NULL;
|
||||
parser->m_declEntity = NULL;
|
||||
@@ -1849,29 +1878,20 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) {
|
||||
to detect errors based on that fact.
|
||||
*/
|
||||
parser->m_errorCode
|
||||
- = parser->m_processor(parser, parser->m_bufferPtr,
|
||||
- parser->m_parseEndPtr, &parser->m_bufferPtr);
|
||||
+ = callProcessor(parser, parser->m_bufferPtr, parser->m_parseEndPtr,
|
||||
+ &parser->m_bufferPtr);
|
||||
|
||||
if (parser->m_errorCode == XML_ERROR_NONE) {
|
||||
switch (parser->m_parsingStatus.parsing) {
|
||||
case XML_SUSPENDED:
|
||||
- /* It is hard to be certain, but it seems that this case
|
||||
- * cannot occur. This code is cleaning up a previous parse
|
||||
- * with no new data (since len == 0). Changing the parsing
|
||||
- * state requires getting to execute a handler function, and
|
||||
- * there doesn't seem to be an opportunity for that while in
|
||||
- * this circumstance.
|
||||
- *
|
||||
- * Given the uncertainty, we retain the code but exclude it
|
||||
- * from coverage tests.
|
||||
- *
|
||||
- * LCOV_EXCL_START
|
||||
- */
|
||||
+ /* While we added no new data, the finalBuffer flag may have caused
|
||||
+ * us to parse previously-unparsed data in the internal buffer.
|
||||
+ * If that triggered a callback to the application, it would have
|
||||
+ * had an opportunity to suspend parsing. */
|
||||
XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr,
|
||||
parser->m_bufferPtr, &parser->m_position);
|
||||
parser->m_positionPtr = parser->m_bufferPtr;
|
||||
return XML_STATUS_SUSPENDED;
|
||||
- /* LCOV_EXCL_STOP */
|
||||
case XML_INITIALIZED:
|
||||
case XML_PARSING:
|
||||
parser->m_parsingStatus.parsing = XML_FINISHED;
|
||||
@@ -1901,7 +1921,7 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) {
|
||||
parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal;
|
||||
|
||||
parser->m_errorCode
|
||||
- = parser->m_processor(parser, s, parser->m_parseEndPtr = s + len, &end);
|
||||
+ = callProcessor(parser, s, parser->m_parseEndPtr = s + len, &end);
|
||||
|
||||
if (parser->m_errorCode != XML_ERROR_NONE) {
|
||||
parser->m_eventEndPtr = parser->m_eventPtr;
|
||||
@@ -2004,8 +2024,8 @@ XML_ParseBuffer(XML_Parser parser, int len, int isFinal) {
|
||||
parser->m_parseEndByteIndex += len;
|
||||
parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal;
|
||||
|
||||
- parser->m_errorCode = parser->m_processor(
|
||||
- parser, start, parser->m_parseEndPtr, &parser->m_bufferPtr);
|
||||
+ parser->m_errorCode = callProcessor(parser, start, parser->m_parseEndPtr,
|
||||
+ &parser->m_bufferPtr);
|
||||
|
||||
if (parser->m_errorCode != XML_ERROR_NONE) {
|
||||
parser->m_eventEndPtr = parser->m_eventPtr;
|
||||
@@ -2192,7 +2212,7 @@ XML_ResumeParser(XML_Parser parser) {
|
||||
}
|
||||
parser->m_parsingStatus.parsing = XML_PARSING;
|
||||
|
||||
- parser->m_errorCode = parser->m_processor(
|
||||
+ parser->m_errorCode = callProcessor(
|
||||
parser, parser->m_bufferPtr, parser->m_parseEndPtr, &parser->m_bufferPtr);
|
||||
|
||||
if (parser->m_errorCode != XML_ERROR_NONE) {
|
||||
--
|
||||
2.33.0
|
||||
|
||||
|
||||
73
backport-001-CVE-2023-52426.patch
Normal file
73
backport-001-CVE-2023-52426.patch
Normal file
@ -0,0 +1,73 @@
|
||||
From daa89e42c005cc7f4f7af9eee271ae0723d30300 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Thu, 26 Oct 2023 00:59:52 +0200
|
||||
Subject: [PATCH 01/17] cmake: Introduce option EXPAT_GE to control macro
|
||||
XML_GE
|
||||
|
||||
Reference: https://github.com//libexpat/libexpat/commit/daa89e42c005cc7f4f7af9eee271ae0723d30300
|
||||
Conflict: remove expat_config_h_cmake__expected.txt
|
||||
change expat_shy_set to option in CMakeLists.txt
|
||||
---
|
||||
CMakeLists.txt | 9 +++++++++
|
||||
expat_config.h.cmake | 3 +++
|
||||
2 files changed, 12 insertions(+)
|
||||
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index cd12a99..697d4d7 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -126,6 +126,8 @@ set(EXPAT_CONTEXT_BYTES 1024 CACHE STRING "Define to specify how much context to
|
||||
mark_as_advanced(EXPAT_CONTEXT_BYTES)
|
||||
option(EXPAT_DTD "Define to make parameter entity parsing functionality available" ON)
|
||||
mark_as_advanced(EXPAT_DTD)
|
||||
+option(EXPAT_GE "Define to make general entity parsing functionality available" ON)
|
||||
+mark_as_advanced(EXPAT_GE)
|
||||
option(EXPAT_NS "Define to make XML Namespaces functionality available" ON)
|
||||
mark_as_advanced(EXPAT_NS)
|
||||
option(EXPAT_WARNINGS_AS_ERRORS "Treat all compiler warnings as errors" OFF)
|
||||
@@ -156,6 +158,11 @@ endif()
|
||||
#
|
||||
# Environment checks
|
||||
#
|
||||
+if(EXPAT_DTD AND NOT EXPAT_GE)
|
||||
+ message(SEND_ERROR "Option EXPAT_DTD requires that EXPAT_GE is also enabled.")
|
||||
+ message(SEND_ERROR "Please either enable option EXPAT_GE (recommended) or disable EXPAT_DTD also.")
|
||||
+endif()
|
||||
+
|
||||
if(EXPAT_WITH_LIBBSD)
|
||||
find_library(LIB_BSD NAMES bsd)
|
||||
if(NOT LIB_BSD)
|
||||
@@ -258,6 +265,7 @@ endif()
|
||||
|
||||
_expat_copy_bool_int(EXPAT_ATTR_INFO XML_ATTR_INFO)
|
||||
_expat_copy_bool_int(EXPAT_DTD XML_DTD)
|
||||
+_expat_copy_bool_int(EXPAT_GE XML_GE)
|
||||
_expat_copy_bool_int(EXPAT_LARGE_SIZE XML_LARGE_SIZE)
|
||||
_expat_copy_bool_int(EXPAT_MIN_SIZE XML_MIN_SIZE)
|
||||
_expat_copy_bool_int(EXPAT_NS XML_NS)
|
||||
@@ -758,6 +766,7 @@ message(STATUS " // Advanced options, changes not advised")
|
||||
message(STATUS " Attributes info .......... ${EXPAT_ATTR_INFO}")
|
||||
message(STATUS " Context bytes ............ ${EXPAT_CONTEXT_BYTES}")
|
||||
message(STATUS " DTD support .............. ${EXPAT_DTD}")
|
||||
+message(STATUS " General entities ......... ${EXPAT_GE}")
|
||||
message(STATUS " Large size ............... ${EXPAT_LARGE_SIZE}")
|
||||
message(STATUS " Minimum size ............. ${EXPAT_MIN_SIZE}")
|
||||
message(STATUS " Namespace support ........ ${EXPAT_NS}")
|
||||
diff --git a/expat_config.h.cmake b/expat_config.h.cmake
|
||||
index 173fed1..18d979a 100644
|
||||
--- a/expat_config.h.cmake
|
||||
+++ b/expat_config.h.cmake
|
||||
@@ -100,6 +100,9 @@
|
||||
/* Define to make parameter entity parsing functionality available. */
|
||||
#cmakedefine XML_DTD
|
||||
|
||||
+/* Define as 1/0 to enable/disable support for general entities. */
|
||||
+#define XML_GE @XML_GE@
|
||||
+
|
||||
/* Define to make XML Namespaces functionality available. */
|
||||
#cmakedefine XML_NS
|
||||
|
||||
--
|
||||
2.33.0
|
||||
|
||||
|
||||
84
backport-001-CVE-2024-45490.patch
Normal file
84
backport-001-CVE-2024-45490.patch
Normal file
@ -0,0 +1,84 @@
|
||||
From a882e725dd057db98907f6b03b733f0f6889aee7 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Tue, 20 Aug 2024 22:57:12 +0200
|
||||
Subject: [PATCH] tests: Cover "len < 0" for both XML_Parse and XML_ParseBuffer
|
||||
|
||||
---
|
||||
tests/runtests.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
1 file changed, 53 insertions(+)
|
||||
|
||||
diff --git a/tests/runtests.c b/tests/runtests.c
|
||||
index 02c8c85..4649359 100644
|
||||
--- a/tests/runtests.c
|
||||
+++ b/tests/runtests.c
|
||||
@@ -3978,6 +3978,57 @@ START_TEST(test_empty_parse) {
|
||||
}
|
||||
END_TEST
|
||||
|
||||
+/* Test XML_Parse for len < 0 */
|
||||
+START_TEST(test_negative_len_parse) {
|
||||
+ const char *const doc = "<root/>";
|
||||
+ for (int isFinal = 0; isFinal < 2; isFinal++) {
|
||||
+ XML_Parser parser = XML_ParserCreate(NULL);
|
||||
+
|
||||
+ if (XML_GetErrorCode(parser) != XML_ERROR_NONE)
|
||||
+ fail("There was not supposed to be any initial parse error.");
|
||||
+
|
||||
+ const enum XML_Status status = XML_Parse(parser, doc, -1, isFinal);
|
||||
+
|
||||
+ if (status != XML_STATUS_ERROR)
|
||||
+ fail("Negative len was expected to fail the parse but did not.");
|
||||
+
|
||||
+ if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_ARGUMENT)
|
||||
+ fail("Parse error does not match XML_ERROR_INVALID_ARGUMENT.");
|
||||
+
|
||||
+ XML_ParserFree(parser);
|
||||
+ }
|
||||
+}
|
||||
+END_TEST
|
||||
+
|
||||
+/* Test XML_ParseBuffer for len < 0 */
|
||||
+START_TEST(test_negative_len_parse_buffer) {
|
||||
+ const char *const doc = "<root/>";
|
||||
+ for (int isFinal = 0; isFinal < 2; isFinal++) {
|
||||
+ XML_Parser parser = XML_ParserCreate(NULL);
|
||||
+
|
||||
+ if (XML_GetErrorCode(parser) != XML_ERROR_NONE)
|
||||
+ fail("There was not supposed to be any initial parse error.");
|
||||
+
|
||||
+ void *const buffer = XML_GetBuffer(parser, (int)strlen(doc));
|
||||
+
|
||||
+ if (buffer == NULL)
|
||||
+ fail("XML_GetBuffer failed.");
|
||||
+
|
||||
+ memcpy(buffer, doc, strlen(doc));
|
||||
+
|
||||
+ const enum XML_Status status = XML_ParseBuffer(parser, -1, isFinal);
|
||||
+
|
||||
+ if (status != XML_STATUS_ERROR)
|
||||
+ fail("Negative len was expected to fail the parse but did not.");
|
||||
+
|
||||
+ if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_ARGUMENT)
|
||||
+ fail("Parse error does not match XML_ERROR_INVALID_ARGUMENT.");
|
||||
+
|
||||
+ XML_ParserFree(parser);
|
||||
+ }
|
||||
+}
|
||||
+END_TEST
|
||||
+
|
||||
/* Test odd corners of the XML_GetBuffer interface */
|
||||
static enum XML_Status
|
||||
get_feature(enum XML_FeatureEnum feature_id, long *presult) {
|
||||
@@ -12474,6 +12525,8 @@ make_suite(void) {
|
||||
tcase_add_test__ifdef_xml_dtd(tc_basic, test_user_parameters);
|
||||
tcase_add_test__ifdef_xml_dtd(tc_basic, test_ext_entity_ref_parameter);
|
||||
tcase_add_test(tc_basic, test_empty_parse);
|
||||
+ tcase_add_test(tc_basic, test_negative_len_parse);
|
||||
+ tcase_add_test(tc_basic, test_negative_len_parse_buffer);
|
||||
tcase_add_test(tc_basic, test_get_buffer_1);
|
||||
tcase_add_test(tc_basic, test_get_buffer_2);
|
||||
#if defined(XML_CONTEXT_BYTES)
|
||||
--
|
||||
2.33.0
|
||||
|
||||
|
||||
40
backport-002-CVE-2023-52425.patch
Normal file
40
backport-002-CVE-2023-52425.patch
Normal file
@ -0,0 +1,40 @@
|
||||
From c3a4816e175ede7da1a692a50d6251efdfe41a45 Mon Sep 17 00:00:00 2001
|
||||
From: Snild Dolkow <snild@sony.com>
|
||||
Date: Mon, 4 Sep 2023 17:21:14 +0200
|
||||
Subject: [PATCH] Don't update partial token heuristic on error
|
||||
|
||||
Reference: https://github.com/libexpat/libexpat/pull/789/commits/1b9d398517befeb944cbbadadf10992b07e96fa2
|
||||
Conflict: no
|
||||
|
||||
Suggested-by: Sebastian Pipping <sebastian@pipping.org>
|
||||
---
|
||||
lib/xmlparse.c | 12 +++++++-----
|
||||
1 file changed, 7 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
|
||||
index 32df1eb9..a8414dd7 100644
|
||||
--- a/lib/xmlparse.c
|
||||
+++ b/lib/xmlparse.c
|
||||
@@ -963,11 +963,13 @@ callProcessor(XML_Parser parser, const char *start, const char *end,
|
||||
}
|
||||
}
|
||||
const enum XML_Error ret = parser->m_processor(parser, start, end, endPtr);
|
||||
- // if we consumed nothing, remember what we had on this parse attempt.
|
||||
- if (*endPtr == start) {
|
||||
- parser->m_partialTokenBytesBefore = have_now;
|
||||
- } else {
|
||||
- parser->m_partialTokenBytesBefore = 0;
|
||||
+ if (ret == XML_ERROR_NONE) {
|
||||
+ // if we consumed nothing, remember what we had on this parse attempt.
|
||||
+ if (*endPtr == start) {
|
||||
+ parser->m_partialTokenBytesBefore = have_now;
|
||||
+ } else {
|
||||
+ parser->m_partialTokenBytesBefore = 0;
|
||||
+ }
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
--
|
||||
2.33.0
|
||||
|
||||
|
||||
29
backport-002-CVE-2023-52426.patch
Normal file
29
backport-002-CVE-2023-52426.patch
Normal file
@ -0,0 +1,29 @@
|
||||
From ed87a4793404e91c0cc0c81435fcfcc64a8be9f4 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Thu, 26 Oct 2023 00:45:23 +0200
|
||||
Subject: [PATCH 02/17] configure.ac: Define macro XML_GE as 1
|
||||
|
||||
Reference: https://github.com//libexpat/libexpat/commit/ed87a4793404e91c0cc0c81435fcfcc64a8be9f4
|
||||
Conflict: remove expat_config_h_in__expected.txt
|
||||
|
||||
---
|
||||
configure.ac | 2 ++
|
||||
2 files changed, 3 insertions(+)
|
||||
|
||||
diff --git a/expat/configure.ac b/expat/configure.ac
|
||||
index c9f95bca..fec4ecd0 100644
|
||||
--- a/configure.ac
|
||||
+++ b/configure.ac
|
||||
@@ -303,6 +303,8 @@ AC_SUBST(FILEMAP)
|
||||
dnl Some basic configuration:
|
||||
AC_DEFINE([XML_NS], 1,
|
||||
[Define to make XML Namespaces functionality available.])
|
||||
+AC_DEFINE([XML_GE], 1,
|
||||
+ [Define as 1/0 to enable/disable support for general entities.])
|
||||
AC_DEFINE([XML_DTD], 1,
|
||||
[Define to make parameter entity parsing functionality available.])
|
||||
AC_DEFINE([XML_DEV_URANDOM], 1,
|
||||
--
|
||||
2.37.3.windows.1
|
||||
|
||||
|
||||
31
backport-002-CVE-2024-45490.patch
Normal file
31
backport-002-CVE-2024-45490.patch
Normal file
@ -0,0 +1,31 @@
|
||||
From a5d580af424bde0c83ad64fcc8bd3beff1db317d Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Mon, 19 Aug 2024 22:26:07 +0200
|
||||
Subject: [PATCH] lib: Reject negative len for XML_ParseBuffer
|
||||
|
||||
Reported by TaiYou
|
||||
---
|
||||
lib/xmlparse.c | 6 ++++++
|
||||
1 file changed, 6 insertions(+)
|
||||
|
||||
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
|
||||
index bd6aa72..8b9046e 100644
|
||||
--- a/lib/xmlparse.c
|
||||
+++ b/lib/xmlparse.c
|
||||
@@ -2016,6 +2016,12 @@ XML_ParseBuffer(XML_Parser parser, int len, int isFinal) {
|
||||
|
||||
if (parser == NULL)
|
||||
return XML_STATUS_ERROR;
|
||||
+
|
||||
+ if (len < 0) {
|
||||
+ parser->m_errorCode = XML_ERROR_INVALID_ARGUMENT;
|
||||
+ return XML_STATUS_ERROR;
|
||||
+ }
|
||||
+
|
||||
switch (parser->m_parsingStatus.parsing) {
|
||||
case XML_SUSPENDED:
|
||||
parser->m_errorCode = XML_ERROR_SUSPENDED;
|
||||
--
|
||||
2.33.0
|
||||
|
||||
|
||||
62
backport-003-CVE-2023-52425.patch
Normal file
62
backport-003-CVE-2023-52425.patch
Normal file
@ -0,0 +1,62 @@
|
||||
From af7d2acf60b2d42506c7fb7e61ed3dbc7989dd01 Mon Sep 17 00:00:00 2001
|
||||
From: Snild Dolkow <snild@sony.com>
|
||||
Date: Thu, 31 Aug 2023 12:36:43 +0200
|
||||
Subject: [PATCH] Always consume BOM bytes when found in prolog
|
||||
|
||||
Reference: https://github.com/libexpat/libexpat/commit/b1e955449cea6bb5862cd249e659c2123bd95a9e
|
||||
Conflict: change xmlparse.c
|
||||
|
||||
The byte order mark is not correctly consumed when followed by an
|
||||
incomplete token in a non-final parse. This results in the BOM staying
|
||||
in the buffer, causing an invalid token error later.
|
||||
|
||||
This was not detected by existing tests because they either parse
|
||||
everything in one call, or add a single byte at a time.
|
||||
|
||||
By moving forward when we find a BOM, we make sure that the BOM
|
||||
bytes are properly consumed in all cases.
|
||||
---
|
||||
lib/xmlparse.c | 18 +++++++++---------
|
||||
1 file changed, 9 insertions(+), 9 deletions(-)
|
||||
|
||||
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
|
||||
index daceacf..184997d 100644
|
||||
--- a/lib/xmlparse.c
|
||||
+++ b/lib/xmlparse.c
|
||||
@@ -4502,15 +4502,15 @@ entityValueInitProcessor(XML_Parser parser, const char *s, const char *end,
|
||||
parser->m_processor = entityValueProcessor;
|
||||
return entityValueProcessor(parser, next, end, nextPtr);
|
||||
}
|
||||
- /* If we are at the end of the buffer, this would cause XmlPrologTok to
|
||||
- return XML_TOK_NONE on the next call, which would then cause the
|
||||
- function to exit with *nextPtr set to s - that is what we want for other
|
||||
- tokens, but not for the BOM - we would rather like to skip it;
|
||||
- then, when this routine is entered the next time, XmlPrologTok will
|
||||
- return XML_TOK_INVALID, since the BOM is still in the buffer
|
||||
+ /* XmlPrologTok has now set the encoding based on the BOM it found, and we
|
||||
+ must move s and nextPtr forward to consume the BOM.
|
||||
+
|
||||
+ If we didn't, and got XML_TOK_NONE from the next XmlPrologTok call, we
|
||||
+ would leave the BOM in the buffer and return. On the next call to this
|
||||
+ function, our XmlPrologTok call would return XML_TOK_INVALID, since it
|
||||
+ is not valid to have multiple BOMs.
|
||||
*/
|
||||
- else if (tok == XML_TOK_BOM && next == end
|
||||
- && ! parser->m_parsingStatus.finalBuffer) {
|
||||
+ else if (tok == XML_TOK_BOM) {
|
||||
# if defined(XML_DTD) || XML_GE == 1
|
||||
if (! accountingDiffTolerated(parser, tok, s, next, __LINE__,
|
||||
XML_ACCOUNT_DIRECT)) {
|
||||
@@ -4520,7 +4520,7 @@ entityValueInitProcessor(XML_Parser parser, const char *s, const char *end,
|
||||
# endif
|
||||
|
||||
*nextPtr = next;
|
||||
- return XML_ERROR_NONE;
|
||||
+ s = next;
|
||||
}
|
||||
/* If we get this token, we have the start of what might be a
|
||||
normal tag, but not a declaration (i.e. it doesn't begin with
|
||||
--
|
||||
2.33.0
|
||||
|
||||
|
||||
402
backport-003-CVE-2023-52426.patch
Normal file
402
backport-003-CVE-2023-52426.patch
Normal file
@ -0,0 +1,402 @@
|
||||
From ff958ebde28854845b28167695a678cc49a50c4b Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Thu, 26 Oct 2023 00:43:22 +0200
|
||||
Subject: [PATCH] lib|xmlwf|cmake: Extend scope of billion laughs attack
|
||||
protection
|
||||
|
||||
.. from "defined(XML_DTD)" to "defined(XML_DTD) || XML_GE==1".
|
||||
|
||||
Reference: https://github.com//libexpat/libexpat/commit/ff958ebde28854845b28167695a678cc49a50c4b
|
||||
Conflict: remove _expat_def_file_toggle in CMakeLists.txt of win32
|
||||
remove _EXPAT_COMMENT_DTD_OR_GE in libexpat.def.cmake of MS VC++
|
||||
adapt entityValueInitProcessor
|
||||
adapt internalEntityProcessor
|
||||
|
||||
|
||||
---
|
||||
lib/expat.h | 8 +++---
|
||||
lib/internal.h | 2 +-
|
||||
lib/xmlparse.c | 73 ++++++++++++++++++++++++++------------------------
|
||||
xmlwf/xmlwf.c | 18 +++++++------
|
||||
4 files changed, 54 insertions(+), 47 deletions(-)
|
||||
|
||||
diff --git a/lib/expat.h b/lib/expat.h
|
||||
index 6990c25..02d4728 100644
|
||||
--- a/lib/expat.h
|
||||
+++ b/lib/expat.h
|
||||
@@ -1015,13 +1015,15 @@ typedef struct {
|
||||
XMLPARSEAPI(const XML_Feature *)
|
||||
XML_GetFeatureList(void);
|
||||
|
||||
-#ifdef XML_DTD
|
||||
-/* Added in Expat 2.2.9. */
|
||||
+#if defined(XML_DTD) || XML_GE == 1
|
||||
+/* Added in Expat 2.2.9 for XML_DTD defined and
|
||||
+ * added in Expat 2.6.0 for XML_GE == 1. */
|
||||
XMLPARSEAPI(XML_Bool)
|
||||
XML_SetBillionLaughsAttackProtectionMaximumAmplification(
|
||||
XML_Parser parser, float maximumAmplificationFactor);
|
||||
|
||||
-/* Added in Expat 2.2.9. */
|
||||
+/* Added in Expat 2.2.9 for XML_DTD defined and
|
||||
+ * added in Expat 2.6.0 for XML_GE == 1. */
|
||||
XMLPARSEAPI(XML_Bool)
|
||||
XML_SetBillionLaughsAttackProtectionActivationThreshold(
|
||||
XML_Parser parser, unsigned long long activationThresholdBytes);
|
||||
diff --git a/lib/internal.h b/lib/internal.h
|
||||
index fcfeec6..79fd319 100644
|
||||
--- a/lib/internal.h
|
||||
+++ b/lib/internal.h
|
||||
@@ -154,7 +154,7 @@ void
|
||||
_INTERNAL_trim_to_complete_utf8_characters(const char *from,
|
||||
const char **fromLimRef);
|
||||
|
||||
-#if defined(XML_DTD)
|
||||
+#if defined(XML_DTD) || XML_GE == 1
|
||||
unsigned long long testingAccountingGetCountBytesDirect(XML_Parser parser);
|
||||
unsigned long long testingAccountingGetCountBytesIndirect(XML_Parser parser);
|
||||
const char *unsignedCharToPrintable(unsigned char c);
|
||||
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
|
||||
index 134765f..2609fc1 100644
|
||||
--- a/lib/xmlparse.c
|
||||
+++ b/lib/xmlparse.c
|
||||
@@ -390,7 +390,7 @@ enum XML_Account {
|
||||
XML_ACCOUNT_NONE /* i.e. do not account, was accounted already */
|
||||
};
|
||||
|
||||
-#ifdef XML_DTD
|
||||
+#if defined(XML_DTD) || XML_GE == 1
|
||||
typedef unsigned long long XmlBigCount;
|
||||
typedef struct accounting {
|
||||
XmlBigCount countBytesDirect;
|
||||
@@ -406,7 +406,7 @@ typedef struct entity_stats {
|
||||
unsigned int maximumDepthSeen;
|
||||
int debugLevel;
|
||||
} ENTITY_STATS;
|
||||
-#endif /* XML_DTD */
|
||||
+#endif /* defined(XML_DTD) || XML_GE == 1 */
|
||||
|
||||
typedef enum XML_Error PTRCALL Processor(XML_Parser parser, const char *start,
|
||||
const char *end, const char **endPtr);
|
||||
@@ -544,7 +544,7 @@ static XML_Parser parserCreate(const XML_Char *encodingName,
|
||||
|
||||
static void parserInit(XML_Parser parser, const XML_Char *encodingName);
|
||||
|
||||
-#ifdef XML_DTD
|
||||
+#if defined(XML_DTD) || XML_GE == 1
|
||||
static float accountingGetCurrentAmplification(XML_Parser rootParser);
|
||||
static void accountingReportStats(XML_Parser originParser, const char *epilog);
|
||||
static void accountingOnAbort(XML_Parser originParser);
|
||||
@@ -567,7 +567,7 @@ static void entityTrackingOnClose(XML_Parser parser, ENTITY *entity,
|
||||
|
||||
static XML_Parser getRootParserOf(XML_Parser parser,
|
||||
unsigned int *outLevelDiff);
|
||||
-#endif /* XML_DTD */
|
||||
+#endif /* defined(XML_DTD) || XML_GE == 1 */
|
||||
|
||||
static unsigned long getDebugLevel(const char *variableName,
|
||||
unsigned long defaultDebugLevel);
|
||||
@@ -685,7 +685,7 @@ struct XML_ParserStruct {
|
||||
enum XML_ParamEntityParsing m_paramEntityParsing;
|
||||
#endif
|
||||
unsigned long m_hash_secret_salt;
|
||||
-#ifdef XML_DTD
|
||||
+#if defined(XML_DTD) || XML_GE == 1
|
||||
ACCOUNTING m_accounting;
|
||||
ENTITY_STATS m_entity_stats;
|
||||
#endif
|
||||
@@ -1135,7 +1135,7 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) {
|
||||
#endif
|
||||
parser->m_hash_secret_salt = 0;
|
||||
|
||||
-#ifdef XML_DTD
|
||||
+#if defined(XML_DTD) || XML_GE == 1
|
||||
memset(&parser->m_accounting, 0, sizeof(ACCOUNTING));
|
||||
parser->m_accounting.debugLevel = getDebugLevel("EXPAT_ACCOUNTING_DEBUG", 0u);
|
||||
parser->m_accounting.maximumAmplificationFactor
|
||||
@@ -2483,8 +2483,9 @@ XML_GetFeatureList(void) {
|
||||
#ifdef XML_ATTR_INFO
|
||||
{XML_FEATURE_ATTR_INFO, XML_L("XML_ATTR_INFO"), 0},
|
||||
#endif
|
||||
-#ifdef XML_DTD
|
||||
- /* Added in Expat 2.2.9. */
|
||||
+#if defined(XML_DTD) || XML_GE == 1
|
||||
+ /* Added in Expat 2.2.9 for XML_DTD defined and
|
||||
+ * added in Expat 2.6.0 for XML_GE == 1. */
|
||||
{XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT,
|
||||
XML_L("XML_BLAP_MAX_AMP"),
|
||||
(long int)
|
||||
@@ -2498,7 +2499,7 @@ XML_GetFeatureList(void) {
|
||||
return features;
|
||||
}
|
||||
|
||||
-#ifdef XML_DTD
|
||||
+#if defined(XML_DTD) || XML_GE == 1
|
||||
XML_Bool XMLCALL
|
||||
XML_SetBillionLaughsAttackProtectionMaximumAmplification(
|
||||
XML_Parser parser, float maximumAmplificationFactor) {
|
||||
@@ -2520,7 +2521,7 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold(
|
||||
parser->m_accounting.activationThresholdBytes = activationThresholdBytes;
|
||||
return XML_TRUE;
|
||||
}
|
||||
-#endif /* XML_DTD */
|
||||
+#endif /* defined(XML_DTD) || XML_GE == 1 */
|
||||
|
||||
/* Initially tag->rawName always points into the parse buffer;
|
||||
for those TAG instances opened while the current parse buffer was
|
||||
@@ -2606,13 +2607,13 @@ externalEntityInitProcessor2(XML_Parser parser, const char *start,
|
||||
int tok = XmlContentTok(parser->m_encoding, start, end, &next);
|
||||
switch (tok) {
|
||||
case XML_TOK_BOM:
|
||||
-#ifdef XML_DTD
|
||||
+#if defined(XML_DTD) || XML_GE == 1
|
||||
if (! accountingDiffTolerated(parser, tok, start, next, __LINE__,
|
||||
XML_ACCOUNT_DIRECT)) {
|
||||
accountingOnAbort(parser);
|
||||
return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
|
||||
}
|
||||
-#endif /* XML_DTD */
|
||||
+#endif /* defined(XML_DTD) || XML_GE == 1 */
|
||||
|
||||
/* If we are at the end of the buffer, this would cause the next stage,
|
||||
i.e. externalEntityInitProcessor3, to pass control directly to
|
||||
@@ -2726,7 +2727,7 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
|
||||
for (;;) {
|
||||
const char *next = s; /* XmlContentTok doesn't always set the last arg */
|
||||
int tok = XmlContentTok(enc, s, end, &next);
|
||||
-#ifdef XML_DTD
|
||||
+#if defined(XML_DTD) || XML_GE == 1
|
||||
const char *accountAfter
|
||||
= ((tok == XML_TOK_TRAILING_RSQB) || (tok == XML_TOK_TRAILING_CR))
|
||||
? (haveMore ? s /* i.e. 0 bytes */ : end)
|
||||
@@ -2792,14 +2793,14 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
|
||||
XML_Char ch = (XML_Char)XmlPredefinedEntityName(
|
||||
enc, s + enc->minBytesPerChar, next - enc->minBytesPerChar);
|
||||
if (ch) {
|
||||
-#ifdef XML_DTD
|
||||
+#if defined(XML_DTD) || XML_GE == 1
|
||||
/* NOTE: We are replacing 4-6 characters original input for 1 character
|
||||
* so there is no amplification and hence recording without
|
||||
* protection. */
|
||||
accountingDiffTolerated(parser, tok, (char *)&ch,
|
||||
((char *)&ch) + sizeof(XML_Char), __LINE__,
|
||||
XML_ACCOUNT_ENTITY_EXPANSION);
|
||||
-#endif /* XML_DTD */
|
||||
+#endif /* defined(XML_DTD) || XML_GE == 1 */
|
||||
if (parser->m_characterDataHandler)
|
||||
parser->m_characterDataHandler(parser->m_handlerArg, &ch, 1);
|
||||
else if (parser->m_defaultHandler)
|
||||
@@ -3999,7 +4000,7 @@ doCdataSection(XML_Parser parser, const ENCODING *enc, const char **startPtr,
|
||||
for (;;) {
|
||||
const char *next = s; /* in case of XML_TOK_NONE or XML_TOK_PARTIAL */
|
||||
int tok = XmlCdataSectionTok(enc, s, end, &next);
|
||||
-#ifdef XML_DTD
|
||||
+#if defined(XML_DTD) || XML_GE == 1
|
||||
if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, account)) {
|
||||
accountingOnAbort(parser);
|
||||
return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
|
||||
@@ -4151,7 +4152,7 @@ doIgnoreSection(XML_Parser parser, const ENCODING *enc, const char **startPtr,
|
||||
*eventPP = s;
|
||||
*startPtr = NULL;
|
||||
tok = XmlIgnoreSectionTok(enc, s, end, &next);
|
||||
-# ifdef XML_DTD
|
||||
+# if defined(XML_DTD) || XML_GE == 1
|
||||
if (! accountingDiffTolerated(parser, tok, s, next, __LINE__,
|
||||
XML_ACCOUNT_DIRECT)) {
|
||||
accountingOnAbort(parser);
|
||||
@@ -4243,7 +4244,7 @@ processXmlDecl(XML_Parser parser, int isGeneralTextEntity, const char *s,
|
||||
const XML_Char *storedversion = NULL;
|
||||
int standalone = -1;
|
||||
|
||||
-#ifdef XML_DTD
|
||||
+#if defined(XML_DTD) || XML_GE == 1
|
||||
if (! accountingDiffTolerated(parser, XML_TOK_XML_DECL, s, next, __LINE__,
|
||||
XML_ACCOUNT_DIRECT)) {
|
||||
accountingOnAbort(parser);
|
||||
@@ -4450,7 +4451,7 @@ entityValueInitProcessor(XML_Parser parser, const char *s, const char *end,
|
||||
*/
|
||||
else if (tok == XML_TOK_BOM && next == end
|
||||
&& ! parser->m_parsingStatus.finalBuffer) {
|
||||
-# ifdef XML_DTD
|
||||
+# if defined(XML_DTD) || XML_GE == 1
|
||||
if (! accountingDiffTolerated(parser, tok, s, next, __LINE__,
|
||||
XML_ACCOUNT_DIRECT)) {
|
||||
accountingOnAbort(parser);
|
||||
@@ -4666,11 +4667,13 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
|
||||
}
|
||||
}
|
||||
role = XmlTokenRole(&parser->m_prologState, tok, s, next, enc);
|
||||
-#ifdef XML_DTD
|
||||
+#if defined(XML_DTD) || XML_GE == 1
|
||||
switch (role) {
|
||||
case XML_ROLE_INSTANCE_START: // bytes accounted in contentProcessor
|
||||
case XML_ROLE_XML_DECL: // bytes accounted in processXmlDecl
|
||||
- case XML_ROLE_TEXT_DECL: // bytes accounted in processXmlDecl
|
||||
+# ifdef XML_DTD
|
||||
+ case XML_ROLE_TEXT_DECL: // bytes accounted in processXmlDecl
|
||||
+# endif
|
||||
break;
|
||||
default:
|
||||
if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, account)) {
|
||||
@@ -5607,7 +5610,7 @@ epilogProcessor(XML_Parser parser, const char *s, const char *end,
|
||||
for (;;) {
|
||||
const char *next = NULL;
|
||||
int tok = XmlPrologTok(parser->m_encoding, s, end, &next);
|
||||
-#ifdef XML_DTD
|
||||
+#if defined(XML_DTD) || XML_GE == 1
|
||||
if (! accountingDiffTolerated(parser, tok, s, next, __LINE__,
|
||||
XML_ACCOUNT_DIRECT)) {
|
||||
accountingOnAbort(parser);
|
||||
@@ -5687,7 +5690,7 @@ processInternalEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl) {
|
||||
return XML_ERROR_NO_MEMORY;
|
||||
}
|
||||
entity->open = XML_TRUE;
|
||||
-#ifdef XML_DTD
|
||||
+#if defined(XML_DTD) || XML_GE == 1
|
||||
entityTrackingOnOpen(parser, entity, __LINE__);
|
||||
#endif
|
||||
entity->processed = 0;
|
||||
@@ -5721,9 +5724,9 @@ processInternalEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl) {
|
||||
entity->processed = (int)(next - textStart);
|
||||
parser->m_processor = internalEntityProcessor;
|
||||
} else {
|
||||
-#ifdef XML_DTD
|
||||
+#if defined(XML_DTD) || XML_GE == 1
|
||||
entityTrackingOnClose(parser, entity, __LINE__);
|
||||
-#endif /* XML_DTD */
|
||||
+#endif /* defined(XML_DTD) || XML_GE == 1 */
|
||||
entity->open = XML_FALSE;
|
||||
parser->m_openInternalEntities = openEntity->next;
|
||||
/* put openEntity back in list of free instances */
|
||||
@@ -5771,7 +5774,7 @@ internalEntityProcessor(XML_Parser parser, const char *s, const char *end,
|
||||
entity->processed = (int)(next - (char *)entity->textPtr);
|
||||
return result;
|
||||
} else {
|
||||
-#ifdef XML_DTD
|
||||
+#if defined(XML_DTD) || XML_GE == 1
|
||||
entityTrackingOnClose(parser, entity, __LINE__);
|
||||
#endif
|
||||
entity->open = XML_FALSE;
|
||||
@@ -5843,7 +5846,7 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
|
||||
const char *next
|
||||
= ptr; /* XmlAttributeValueTok doesn't always set the last arg */
|
||||
int tok = XmlAttributeValueTok(enc, ptr, end, &next);
|
||||
-#ifdef XML_DTD
|
||||
+#if defined(XML_DTD) || XML_GE == 1
|
||||
if (! accountingDiffTolerated(parser, tok, ptr, next, __LINE__, account)) {
|
||||
accountingOnAbort(parser);
|
||||
return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
|
||||
@@ -5908,14 +5911,14 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
|
||||
XML_Char ch = (XML_Char)XmlPredefinedEntityName(
|
||||
enc, ptr + enc->minBytesPerChar, next - enc->minBytesPerChar);
|
||||
if (ch) {
|
||||
-#ifdef XML_DTD
|
||||
+#if defined(XML_DTD) || XML_GE == 1
|
||||
/* NOTE: We are replacing 4-6 characters original input for 1 character
|
||||
* so there is no amplification and hence recording without
|
||||
* protection. */
|
||||
accountingDiffTolerated(parser, tok, (char *)&ch,
|
||||
((char *)&ch) + sizeof(XML_Char), __LINE__,
|
||||
XML_ACCOUNT_ENTITY_EXPANSION);
|
||||
-#endif /* XML_DTD */
|
||||
+#endif /* defined(XML_DTD) || XML_GE == 1 */
|
||||
if (! poolAppendChar(pool, ch))
|
||||
return XML_ERROR_NO_MEMORY;
|
||||
break;
|
||||
@@ -5993,14 +5996,14 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
|
||||
enum XML_Error result;
|
||||
const XML_Char *textEnd = entity->textPtr + entity->textLen;
|
||||
entity->open = XML_TRUE;
|
||||
-#ifdef XML_DTDded to activate (
|
||||
- entityTrackingOnOpen(parser, entity, __LINE__);ded to activate (
|
||||
+#if defined(XML_DTD) || XML_GE == 1
|
||||
+ entityTrackingOnOpen(parser, entity, __LINE__);
|
||||
#endif
|
||||
result = appendAttributeValue(parser, parser->m_internalEncoding,
|
||||
isCdata, (char *)entity->textPtr,
|
||||
(char *)textEnd, pool,
|
||||
XML_ACCOUNT_ENTITY_EXPANSION);
|
||||
-#ifdef XML_DTD
|
||||
+#if defined(XML_DTD) || XML_GE == 1
|
||||
entityTrackingOnClose(parser, entity, __LINE__);
|
||||
#endif
|
||||
entity->open = XML_FALSE;
|
||||
@@ -6056,7 +6059,7 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc,
|
||||
= entityTextPtr; /* XmlEntityValueTok doesn't always set the last arg */
|
||||
int tok = XmlEntityValueTok(enc, entityTextPtr, entityTextEnd, &next);
|
||||
|
||||
-#ifdef XML_DTD
|
||||
+#if defined(XML_DTD) || XML_GE == 1
|
||||
if (! accountingDiffTolerated(parser, tok, entityTextPtr, next, __LINE__,
|
||||
account)) {
|
||||
accountingOnAbort(parser);
|
||||
@@ -7602,7 +7605,7 @@ copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) {
|
||||
return result;
|
||||
}
|
||||
|
||||
-#ifdef XML_DTD
|
||||
+#if defined(XML_DTD) || XML_GE == 1
|
||||
|
||||
static float
|
||||
accountingGetCurrentAmplification(XML_Parser rootParser) {
|
||||
@@ -8333,7 +8336,7 @@ unsignedCharToPrintable(unsigned char c) {
|
||||
assert(0); /* never gets here */
|
||||
}
|
||||
|
||||
-#endif /* XML_DTD */
|
||||
+#endif /* defined(XML_DTD) || XML_GE == 1 */
|
||||
|
||||
static unsigned long
|
||||
getDebugLevel(const char *variableName, unsigned long defaultDebugLevel) {
|
||||
diff --git a/xmlwf/xmlwf.c b/xmlwf/xmlwf.c
|
||||
index 365cd11..50006bd 100644
|
||||
--- a/xmlwf/xmlwf.c
|
||||
+++ b/xmlwf/xmlwf.c
|
||||
@@ -1020,9 +1020,10 @@ tmain(int argc, XML_Char **argv) {
|
||||
" (needs a floating point number greater or equal than 1.0)"));
|
||||
exit(2);
|
||||
}
|
||||
-#ifndef XML_DTD
|
||||
- ftprintf(stderr, T("Warning: Given amplification limit ignored") T(
|
||||
- ", xmlwf has been compiled without DTD support.\n"));
|
||||
+#if ! defined(XML_DTD) && XML_GE == 0
|
||||
+ ftprintf(stderr,
|
||||
+ T("Warning: Given amplification limit ignored")
|
||||
+ T(", xmlwf has been compiled without DTD/GE support.\n"));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
@@ -1041,9 +1042,10 @@ tmain(int argc, XML_Char **argv) {
|
||||
exit(2);
|
||||
}
|
||||
attackThresholdGiven = XML_TRUE;
|
||||
-#ifndef XML_DTD
|
||||
- ftprintf(stderr, T("Warning: Given attack threshold ignored") T(
|
||||
- ", xmlwf has been compiled without DTD support.\n"));
|
||||
+#if ! defined(XML_DTD) && XML_GE == 0
|
||||
+ ftprintf(stderr,
|
||||
+ T("Warning: Given attack threshold ignored")
|
||||
+ T(", xmlwf has been compiled without DTD/GE support.\n"));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
@@ -1078,13 +1080,13 @@ tmain(int argc, XML_Char **argv) {
|
||||
}
|
||||
|
||||
if (attackMaximumAmplification != -1.0f) {
|
||||
-#ifdef XML_DTD
|
||||
+#if defined(XML_DTD) || XML_GE == 1
|
||||
XML_SetBillionLaughsAttackProtectionMaximumAmplification(
|
||||
parser, attackMaximumAmplification);
|
||||
#endif
|
||||
}
|
||||
if (attackThresholdGiven) {
|
||||
-#ifdef XML_DTD
|
||||
+#if defined(XML_DTD) || XML_GE == 1
|
||||
XML_SetBillionLaughsAttackProtectionActivationThreshold(
|
||||
parser, attackThresholdBytes);
|
||||
#endif
|
||||
--
|
||||
2.33.0
|
||||
|
||||
|
||||
41
backport-004-CVE-2023-52425.patch
Normal file
41
backport-004-CVE-2023-52425.patch
Normal file
@ -0,0 +1,41 @@
|
||||
From a7b9a07cd50a4422194f64eb50181fcaec3ef0cf Mon Sep 17 00:00:00 2001
|
||||
From: Snild Dolkow <snild@sony.com>
|
||||
Date: Thu, 24 Aug 2023 09:31:31 +0200
|
||||
Subject: [PATCH] tests: Move triplet_start_checker flag check after isFinal=1
|
||||
call
|
||||
|
||||
Reference: https://github.com/libexpat/libexpat/pull/745/commits/d52b4141496bd26bd716d88c67af8f2250bd0da6
|
||||
Conflict: remove ns_tests.c
|
||||
change runtests.c
|
||||
|
||||
There is no guarantee that the callback will happen before the parse
|
||||
call with isFinal=XML_TRUE. Let's move the assertion to a location
|
||||
where we know it must have happened.
|
||||
---
|
||||
tests/runtests.c | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/tests/runtests.c b/tests/runtests.c
|
||||
index 45ba5d59..8f1d11f0 100644
|
||||
--- a/tests/runtests.c
|
||||
+++ b/tests/runtests.c
|
||||
@@ -6527,13 +6527,13 @@ START_TEST(test_return_ns_triplet) {
|
||||
if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_FALSE)
|
||||
== XML_STATUS_ERROR)
|
||||
xml_failure(g_parser);
|
||||
- if (! triplet_start_flag)
|
||||
- fail("triplet_start_checker not invoked");
|
||||
/* Check that unsetting "return triplets" fails while still parsing */
|
||||
XML_SetReturnNSTriplet(g_parser, XML_FALSE);
|
||||
if (_XML_Parse_SINGLE_BYTES(g_parser, epilog, (int)strlen(epilog), XML_TRUE)
|
||||
== XML_STATUS_ERROR)
|
||||
xml_failure(g_parser);
|
||||
+ if (! triplet_start_flag)
|
||||
+ fail("triplet_start_checker not invoked");
|
||||
if (! triplet_end_flag)
|
||||
fail("triplet_end_checker not invoked");
|
||||
if (dummy_handler_flags
|
||||
--
|
||||
2.33.0
|
||||
|
||||
|
||||
184
backport-004-CVE-2023-52426.patch
Normal file
184
backport-004-CVE-2023-52426.patch
Normal file
@ -0,0 +1,184 @@
|
||||
From 2b127c20b220b673cf52c6be8bef725bf04cbeaf Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Thu, 26 Oct 2023 18:32:11 +0200
|
||||
Subject: [PATCH 07/17] lib: Make XML_GE==0 use self-references as entity
|
||||
replacement text
|
||||
|
||||
Reference: https://github.com//libexpat/libexpat/commit/2b127c20b220b673cf52c6be8bef725bf04cbeaf
|
||||
Conflict: NA
|
||||
|
||||
---
|
||||
lib/xmlparse.c | 81 +++++++++++++++++++++++++++++++++++++++-----
|
||||
1 file changed, 72 insertions(+), 9 deletions(-)
|
||||
|
||||
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
|
||||
index db148b21..6a38dbe2 100644
|
||||
--- a/lib/xmlparse.c
|
||||
+++ b/lib/xmlparse.c
|
||||
@@ -512,9 +512,13 @@ static enum XML_Error appendAttributeValue(XML_Parser parser, const ENCODING *,
|
||||
static ATTRIBUTE_ID *getAttributeId(XML_Parser parser, const ENCODING *enc,
|
||||
const char *start, const char *end);
|
||||
static int setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *);
|
||||
+#if XML_GE == 1
|
||||
static enum XML_Error storeEntityValue(XML_Parser parser, const ENCODING *enc,
|
||||
const char *start, const char *end,
|
||||
enum XML_Account account);
|
||||
+#else
|
||||
+static enum XML_Error storeSelfEntityValue(XML_Parser parser, ENTITY *entity);
|
||||
+#endif
|
||||
static int reportProcessingInstruction(XML_Parser parser, const ENCODING *enc,
|
||||
const char *start, const char *end);
|
||||
static int reportComment(XML_Parser parser, const ENCODING *enc,
|
||||
@@ -5053,6 +5057,9 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
|
||||
break;
|
||||
case XML_ROLE_ENTITY_VALUE:
|
||||
if (dtd->keepProcessing) {
|
||||
+#if defined(XML_DTD) || XML_GE == 1
|
||||
+ // This will store the given replacement text in
|
||||
+ // parser->m_declEntity->textPtr.
|
||||
enum XML_Error result
|
||||
= storeEntityValue(parser, enc, s + enc->minBytesPerChar,
|
||||
next - enc->minBytesPerChar, XML_ACCOUNT_NONE);
|
||||
@@ -5073,6 +5080,25 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
|
||||
poolDiscard(&dtd->entityValuePool);
|
||||
if (result != XML_ERROR_NONE)
|
||||
return result;
|
||||
+#else
|
||||
+ // This will store "&entity123;" in parser->m_declEntity->textPtr
|
||||
+ // to end up as "&entity123;" in the handler.
|
||||
+ if (parser->m_declEntity != NULL) {
|
||||
+ const enum XML_Error result
|
||||
+ = storeSelfEntityValue(parser, parser->m_declEntity);
|
||||
+ if (result != XML_ERROR_NONE)
|
||||
+ return result;
|
||||
+
|
||||
+ if (parser->m_entityDeclHandler) {
|
||||
+ *eventEndPP = s;
|
||||
+ parser->m_entityDeclHandler(
|
||||
+ parser->m_handlerArg, parser->m_declEntity->name,
|
||||
+ parser->m_declEntity->is_param, parser->m_declEntity->textPtr,
|
||||
+ parser->m_declEntity->textLen, parser->m_curBase, 0, 0, 0);
|
||||
+ handleDefault = XML_FALSE;
|
||||
+ }
|
||||
+ }
|
||||
+#endif
|
||||
}
|
||||
break;
|
||||
case XML_ROLE_DOCTYPE_SYSTEM_ID:
|
||||
@@ -5131,6 +5157,16 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
|
||||
}
|
||||
break;
|
||||
case XML_ROLE_ENTITY_COMPLETE:
|
||||
+#if XML_GE == 0
|
||||
+ // This will store "&entity123;" in entity->textPtr
|
||||
+ // to end up as "&entity123;" in the handler.
|
||||
+ if (parser->m_declEntity != NULL) {
|
||||
+ const enum XML_Error result
|
||||
+ = storeSelfEntityValue(parser, parser->m_declEntity);
|
||||
+ if (result != XML_ERROR_NONE)
|
||||
+ return result;
|
||||
+ }
|
||||
+#endif
|
||||
if (dtd->keepProcessing && parser->m_declEntity
|
||||
&& parser->m_entityDeclHandler) {
|
||||
*eventEndPP = s;
|
||||
@@ -6103,6 +6139,7 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
|
||||
/* not reached */
|
||||
}
|
||||
|
||||
+#if XML_GE == 1
|
||||
static enum XML_Error
|
||||
storeEntityValue(XML_Parser parser, const ENCODING *enc,
|
||||
const char *entityTextPtr, const char *entityTextEnd,
|
||||
@@ -6110,12 +6147,12 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc,
|
||||
DTD *const dtd = parser->m_dtd; /* save one level of indirection */
|
||||
STRING_POOL *pool = &(dtd->entityValuePool);
|
||||
enum XML_Error result = XML_ERROR_NONE;
|
||||
-#ifdef XML_DTD
|
||||
+# ifdef XML_DTD
|
||||
int oldInEntityValue = parser->m_prologState.inEntityValue;
|
||||
parser->m_prologState.inEntityValue = 1;
|
||||
-#else
|
||||
+# else
|
||||
UNUSED_P(account);
|
||||
-#endif /* XML_DTD */
|
||||
+# endif /* XML_DTD */
|
||||
/* never return Null for the value argument in EntityDeclHandler,
|
||||
since this would indicate an external entity; therefore we
|
||||
have to make sure that entityValuePool.start is not null */
|
||||
@@ -6129,18 +6166,18 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc,
|
||||
= entityTextPtr; /* XmlEntityValueTok doesn't always set the last arg */
|
||||
int tok = XmlEntityValueTok(enc, entityTextPtr, entityTextEnd, &next);
|
||||
|
||||
-#if defined(XML_DTD) || XML_GE == 1
|
||||
+# if defined(XML_DTD) || XML_GE == 1
|
||||
if (! accountingDiffTolerated(parser, tok, entityTextPtr, next, __LINE__,
|
||||
account)) {
|
||||
accountingOnAbort(parser);
|
||||
result = XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
|
||||
goto endEntityValue;
|
||||
}
|
||||
-#endif
|
||||
+# endif
|
||||
|
||||
switch (tok) {
|
||||
case XML_TOK_PARAM_ENTITY_REF:
|
||||
-#ifdef XML_DTD
|
||||
+# ifdef XML_DTD
|
||||
if (parser->m_isParamEntity || enc != parser->m_encoding) {
|
||||
const XML_Char *name;
|
||||
ENTITY *entity;
|
||||
@@ -6202,7 +6239,7 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc,
|
||||
}
|
||||
break;
|
||||
}
|
||||
-#endif /* XML_DTD */
|
||||
+# endif /* XML_DTD */
|
||||
/* In the internal subset, PE references are not legal
|
||||
within markup declarations, e.g entity values in this case. */
|
||||
parser->m_eventPtr = entityTextPtr;
|
||||
@@ -6283,12 +6320,38 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc,
|
||||
entityTextPtr = next;
|
||||
}
|
||||
endEntityValue:
|
||||
-#ifdef XML_DTD
|
||||
+# ifdef XML_DTD
|
||||
parser->m_prologState.inEntityValue = oldInEntityValue;
|
||||
-#endif /* XML_DTD */
|
||||
+# endif /* XML_DTD */
|
||||
return result;
|
||||
}
|
||||
|
||||
+#else /* XML_GE == 0 */
|
||||
+
|
||||
+static enum XML_Error
|
||||
+storeSelfEntityValue(XML_Parser parser, ENTITY *entity) {
|
||||
+ // This will store "&entity123;" in entity->textPtr
|
||||
+ // to end up as "&entity123;" in the handler.
|
||||
+ const char *const entity_start = "&";
|
||||
+ const char *const entity_end = ";";
|
||||
+
|
||||
+ STRING_POOL *const pool = &(parser->m_dtd->entityValuePool);
|
||||
+ if (! poolAppendString(pool, entity_start)
|
||||
+ || ! poolAppendString(pool, entity->name)
|
||||
+ || ! poolAppendString(pool, entity_end)) {
|
||||
+ poolDiscard(pool);
|
||||
+ return XML_ERROR_NO_MEMORY;
|
||||
+ }
|
||||
+
|
||||
+ entity->textPtr = poolStart(pool);
|
||||
+ entity->textLen = (int)(poolLength(pool));
|
||||
+ poolFinish(pool);
|
||||
+
|
||||
+ return XML_ERROR_NONE;
|
||||
+}
|
||||
+
|
||||
+#endif /* XML_GE == 0 */
|
||||
+
|
||||
static void FASTCALL
|
||||
normalizeLines(XML_Char *s) {
|
||||
XML_Char *p;
|
||||
--
|
||||
2.37.3.windows.1
|
||||
|
||||
|
||||
32
backport-005-CVE-2023-52425.patch
Normal file
32
backport-005-CVE-2023-52425.patch
Normal file
@ -0,0 +1,32 @@
|
||||
From 1b728cf8376a166d21eae818dfa66c55b6209bc4 Mon Sep 17 00:00:00 2001
|
||||
From: Snild Dolkow <snild@sony.com>
|
||||
Date: Thu, 24 Aug 2023 14:10:58 +0200
|
||||
Subject: [PATCH] tests: Set isFinal in test_column_number_after_parse
|
||||
|
||||
Reference: https://github.com/libexpat/libexpat/pull/745/commits/2cee1061e2fec10633c3f02a961dabf95e85910a
|
||||
Conflict: remove basic_tests.c
|
||||
change runtests.c
|
||||
|
||||
Without this, parsing of the end tag may be deferred, yielding an
|
||||
unexpected column number.
|
||||
---
|
||||
tests/runtests.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/tests/runtests.c b/tests/runtests.c
|
||||
index 8f1d11f0..9931d85e 100644
|
||||
--- a/tests/runtests.c
|
||||
+++ b/tests/runtests.c
|
||||
@@ -1071,7 +1071,7 @@ START_TEST(test_column_number_after_parse) {
|
||||
const char *text = "<tag></tag>";
|
||||
XML_Size colno;
|
||||
|
||||
- if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_FALSE)
|
||||
+ if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
|
||||
== XML_STATUS_ERROR)
|
||||
xml_failure(g_parser);
|
||||
colno = XML_GetCurrentColumnNumber(g_parser);
|
||||
--
|
||||
2.33.0
|
||||
|
||||
|
||||
39
backport-006-CVE-2023-52425.patch
Normal file
39
backport-006-CVE-2023-52425.patch
Normal file
@ -0,0 +1,39 @@
|
||||
From 25749ff3dad2216dfd7596498b592747a3d9305e Mon Sep 17 00:00:00 2001
|
||||
From: Snild Dolkow <snild@sony.com>
|
||||
Date: Thu, 31 Aug 2023 16:14:38 +0200
|
||||
Subject: [PATCH] tests: Set isFinal=1 in line/column-number-after-error tests
|
||||
|
||||
Reference: https://github.com/libexpat/libexpat/pull/745/commits/d4105a9080271a8d4996d2454f89be9992cb268a
|
||||
Conflict: remove basic_tests.c
|
||||
change runtests.c
|
||||
|
||||
---
|
||||
tests/runtests.c | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/tests/runtests.c b/tests/runtests.c
|
||||
index 45ba5d59..d367271f 100644
|
||||
--- a/tests/runtests.c
|
||||
+++ b/tests/runtests.c
|
||||
@@ -1139,7 +1139,7 @@ START_TEST(test_line_number_after_error) {
|
||||
" <b>\n"
|
||||
" </a>"; /* missing </b> */
|
||||
XML_Size lineno;
|
||||
- if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_FALSE)
|
||||
+ if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
|
||||
!= XML_STATUS_ERROR)
|
||||
fail("Expected a parse error");
|
||||
|
||||
@@ -1158,7 +1158,7 @@ START_TEST(test_column_number_after_error) {
|
||||
" <b>\n"
|
||||
" </a>"; /* missing </b> */
|
||||
XML_Size colno;
|
||||
- if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_FALSE)
|
||||
+ if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
|
||||
!= XML_STATUS_ERROR)
|
||||
fail("Expected a parse error");
|
||||
|
||||
--
|
||||
2.33.0
|
||||
|
||||
|
||||
368
backport-007-CVE-2023-52425.patch
Normal file
368
backport-007-CVE-2023-52425.patch
Normal file
@ -0,0 +1,368 @@
|
||||
From 1d784ef14933ee775fc20ba4435b8def6b70eae3 Mon Sep 17 00:00:00 2001
|
||||
From: caixiaomeng 00662745 <caixiaomeng2@huawei.com>
|
||||
Date: Mon, 4 Mar 2024 11:00:25 +0800
|
||||
Subject: [PATCH] tests: Adapat test default current cases for 2.4.1
|
||||
|
||||
Reference: https://github.com/libexpat/libexpat/commit/7474fe3d3f686a4d76f1df48c5db0eced295059b
|
||||
Conflict: yes
|
||||
|
||||
---
|
||||
tests/runtests.c | 303 +++++++++++++++++++++++++++++++----------
|
||||
1 file changed, 234 insertions(+), 69 deletions(-)
|
||||
|
||||
diff --git a/tests/runtests.c b/tests/runtests.c
|
||||
index c0aa1773..e97a7c51 100644
|
||||
--- a/tests/runtests.c
|
||||
+++ b/tests/runtests.c
|
||||
@@ -2536,34 +2536,75 @@ START_TEST(test_memory_allocation) {
|
||||
}
|
||||
END_TEST
|
||||
|
||||
+/* Handlers that record their arg and a single identifying character */
|
||||
+
|
||||
+struct handler_record_entry {
|
||||
+ const char *name;
|
||||
+ int arg;
|
||||
+};
|
||||
+struct handler_record_list {
|
||||
+ int count;
|
||||
+ struct handler_record_entry entries[50]; // arbitrary big-enough max count
|
||||
+};
|
||||
+
|
||||
+# define handler_record_get(storage, index) \
|
||||
+ _handler_record_get((storage), (index), __FILE__, __LINE__)
|
||||
+
|
||||
+# define assert_record_handler_called(storage, index, expected_name, \
|
||||
+ expected_arg) \
|
||||
+ do { \
|
||||
+ const struct handler_record_entry *e \
|
||||
+ = handler_record_get(storage, index); \
|
||||
+ assert(strcmp(e->name, expected_name) == 0); \
|
||||
+ assert(e->arg == (expected_arg)); \
|
||||
+ } while (0)
|
||||
+
|
||||
+/* Handlers that record their function name and int arg. */
|
||||
+
|
||||
+static void
|
||||
+record_call(struct handler_record_list *const rec, const char *funcname,
|
||||
+ const int arg) {
|
||||
+ const int max_entries = sizeof(rec->entries) / sizeof(rec->entries[0]);
|
||||
+ assert(rec->count < max_entries);
|
||||
+ struct handler_record_entry *const e = &rec->entries[rec->count++];
|
||||
+ e->name = funcname;
|
||||
+ e->arg = arg;
|
||||
+}
|
||||
+
|
||||
static void XMLCALL
|
||||
record_default_handler(void *userData, const XML_Char *s, int len) {
|
||||
UNUSED_P(s);
|
||||
- UNUSED_P(len);
|
||||
- CharData_AppendXMLChars((CharData *)userData, XCS("D"), 1);
|
||||
+ record_call((struct handler_record_list *)userData, __func__, len);
|
||||
}
|
||||
|
||||
static void XMLCALL
|
||||
record_cdata_handler(void *userData, const XML_Char *s, int len) {
|
||||
UNUSED_P(s);
|
||||
- UNUSED_P(len);
|
||||
- CharData_AppendXMLChars((CharData *)userData, XCS("C"), 1);
|
||||
+ record_call((struct handler_record_list *)userData, __func__, len);
|
||||
XML_DefaultCurrent(g_parser);
|
||||
}
|
||||
|
||||
static void XMLCALL
|
||||
record_cdata_nodefault_handler(void *userData, const XML_Char *s, int len) {
|
||||
UNUSED_P(s);
|
||||
- UNUSED_P(len);
|
||||
- CharData_AppendXMLChars((CharData *)userData, XCS("c"), 1);
|
||||
+ record_call((struct handler_record_list *)userData, __func__, len);
|
||||
}
|
||||
|
||||
static void XMLCALL
|
||||
record_skip_handler(void *userData, const XML_Char *entityName,
|
||||
int is_parameter_entity) {
|
||||
UNUSED_P(entityName);
|
||||
- CharData_AppendXMLChars((CharData *)userData,
|
||||
- is_parameter_entity ? XCS("E") : XCS("e"), 1);
|
||||
+ record_call((struct handler_record_list *)userData, __func__,
|
||||
+ is_parameter_entity);
|
||||
+}
|
||||
+
|
||||
+static const struct handler_record_entry *
|
||||
+_handler_record_get(const struct handler_record_list *storage, int index,
|
||||
+ const char *file, int line) {
|
||||
+ if (storage->count <= index) {
|
||||
+ fail("too few handler calls");
|
||||
+ }
|
||||
+ return &storage->entries[index];
|
||||
}
|
||||
|
||||
/* Test XML_DefaultCurrent() passes handling on correctly */
|
||||
@@ -2573,78 +2614,202 @@ START_TEST(test_default_current) {
|
||||
"<!ENTITY entity '%'>\n"
|
||||
"]>\n"
|
||||
"<doc>&entity;</doc>";
|
||||
- CharData storage;
|
||||
|
||||
- XML_SetDefaultHandler(g_parser, record_default_handler);
|
||||
- XML_SetCharacterDataHandler(g_parser, record_cdata_handler);
|
||||
- CharData_Init(&storage);
|
||||
- XML_SetUserData(g_parser, &storage);
|
||||
- if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
|
||||
- == XML_STATUS_ERROR)
|
||||
- xml_failure(g_parser);
|
||||
- CharData_CheckXMLChars(&storage, XCS("DCDCDCDCDCDD"));
|
||||
+ {
|
||||
+ struct handler_record_list storage;
|
||||
+ storage.count = 0;
|
||||
+ XML_SetDefaultHandler(g_parser, record_default_handler);
|
||||
+ XML_SetCharacterDataHandler(g_parser, record_cdata_handler);
|
||||
+ XML_SetUserData(g_parser, &storage);
|
||||
+ if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
|
||||
+ == XML_STATUS_ERROR)
|
||||
+ xml_failure(g_parser);
|
||||
+ int i = 0;
|
||||
+ assert_record_handler_called(&storage, i++, "record_default_handler", 5);
|
||||
+ // we should have gotten one or more cdata callbacks, totaling 5 chars
|
||||
+ int cdata_len_remaining = 5;
|
||||
+ while (cdata_len_remaining > 0) {
|
||||
+ const struct handler_record_entry *c_entry
|
||||
+ = handler_record_get(&storage, i++);
|
||||
+ assert(strcmp(c_entry->name, "record_cdata_handler") == 0);
|
||||
+ assert(c_entry->arg > 0);
|
||||
+ assert(c_entry->arg <= cdata_len_remaining);
|
||||
+ cdata_len_remaining -= c_entry->arg;
|
||||
+ // default handler must follow, with the exact same len argument.
|
||||
+ assert_record_handler_called(&storage, i++, "record_default_handler",
|
||||
+ c_entry->arg);
|
||||
+ }
|
||||
+ assert_record_handler_called(&storage, i++, "record_default_handler", 6);
|
||||
+ assert(storage.count == i);
|
||||
+ }
|
||||
|
||||
/* Again, without the defaulting */
|
||||
- XML_ParserReset(g_parser, NULL);
|
||||
- XML_SetDefaultHandler(g_parser, record_default_handler);
|
||||
- XML_SetCharacterDataHandler(g_parser, record_cdata_nodefault_handler);
|
||||
- CharData_Init(&storage);
|
||||
- XML_SetUserData(g_parser, &storage);
|
||||
- if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
|
||||
- == XML_STATUS_ERROR)
|
||||
- xml_failure(g_parser);
|
||||
- CharData_CheckXMLChars(&storage, XCS("DcccccD"));
|
||||
+ {
|
||||
+ struct handler_record_list storage;
|
||||
+ storage.count = 0;
|
||||
+ XML_ParserReset(g_parser, NULL);
|
||||
+ XML_SetDefaultHandler(g_parser, record_default_handler);
|
||||
+ XML_SetCharacterDataHandler(g_parser, record_cdata_nodefault_handler);
|
||||
+ XML_SetUserData(g_parser, &storage);
|
||||
+ if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
|
||||
+ == XML_STATUS_ERROR)
|
||||
+ xml_failure(g_parser);
|
||||
+ int i = 0;
|
||||
+ assert_record_handler_called(&storage, i++, "record_default_handler", 5);
|
||||
+ // we should have gotten one or more cdata callbacks, totaling 5 chars
|
||||
+ int cdata_len_remaining = 5;
|
||||
+ while (cdata_len_remaining > 0) {
|
||||
+ const struct handler_record_entry *c_entry
|
||||
+ = handler_record_get(&storage, i++);
|
||||
+ assert(strcmp(c_entry->name, "record_cdata_nodefault_handler") == 0);
|
||||
+ assert(c_entry->arg > 0);
|
||||
+ assert(c_entry->arg <= cdata_len_remaining);
|
||||
+ cdata_len_remaining -= c_entry->arg;
|
||||
+ }
|
||||
+ assert_record_handler_called(&storage, i++, "record_default_handler", 6);
|
||||
+ assert(storage.count == i);
|
||||
+ }
|
||||
|
||||
/* Now with an internal entity to complicate matters */
|
||||
- XML_ParserReset(g_parser, NULL);
|
||||
- XML_SetDefaultHandler(g_parser, record_default_handler);
|
||||
- XML_SetCharacterDataHandler(g_parser, record_cdata_handler);
|
||||
- CharData_Init(&storage);
|
||||
- XML_SetUserData(g_parser, &storage);
|
||||
- if (_XML_Parse_SINGLE_BYTES(g_parser, entity_text, (int)strlen(entity_text),
|
||||
- XML_TRUE)
|
||||
- == XML_STATUS_ERROR)
|
||||
- xml_failure(g_parser);
|
||||
- /* The default handler suppresses the entity */
|
||||
- CharData_CheckXMLChars(&storage, XCS("DDDDDDDDDDDDDDDDDDD"));
|
||||
+ {
|
||||
+ struct handler_record_list storage;
|
||||
+ storage.count = 0;
|
||||
+ XML_ParserReset(g_parser, NULL);
|
||||
+ XML_SetDefaultHandler(g_parser, record_default_handler);
|
||||
+ XML_SetCharacterDataHandler(g_parser, record_cdata_handler);
|
||||
+ XML_SetUserData(g_parser, &storage);
|
||||
+ if (_XML_Parse_SINGLE_BYTES(g_parser, entity_text, (int)strlen(entity_text),
|
||||
+ XML_TRUE)
|
||||
+ == XML_STATUS_ERROR)
|
||||
+ xml_failure(g_parser);
|
||||
+ /* The default handler suppresses the entity */
|
||||
+ assert_record_handler_called(&storage, 0, "record_default_handler", 9);
|
||||
+ assert_record_handler_called(&storage, 1, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 2, "record_default_handler", 3);
|
||||
+ assert_record_handler_called(&storage, 3, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 4, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 5, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 6, "record_default_handler", 8);
|
||||
+ assert_record_handler_called(&storage, 7, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 8, "record_default_handler", 6);
|
||||
+ assert_record_handler_called(&storage, 9, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 10, "record_default_handler", 7);
|
||||
+ assert_record_handler_called(&storage, 11, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 12, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 13, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 14, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 15, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 16, "record_default_handler", 5);
|
||||
+ assert_record_handler_called(&storage, 17, "record_default_handler", 8);
|
||||
+ assert_record_handler_called(&storage, 18, "record_default_handler", 6);
|
||||
+ assert(storage.count == 19);
|
||||
+ }
|
||||
|
||||
/* Again, with a skip handler */
|
||||
- XML_ParserReset(g_parser, NULL);
|
||||
- XML_SetDefaultHandler(g_parser, record_default_handler);
|
||||
- XML_SetCharacterDataHandler(g_parser, record_cdata_handler);
|
||||
- XML_SetSkippedEntityHandler(g_parser, record_skip_handler);
|
||||
- CharData_Init(&storage);
|
||||
- XML_SetUserData(g_parser, &storage);
|
||||
- if (_XML_Parse_SINGLE_BYTES(g_parser, entity_text, (int)strlen(entity_text),
|
||||
- XML_TRUE)
|
||||
- == XML_STATUS_ERROR)
|
||||
- xml_failure(g_parser);
|
||||
- /* The default handler suppresses the entity */
|
||||
- CharData_CheckXMLChars(&storage, XCS("DDDDDDDDDDDDDDDDDeD"));
|
||||
+ {
|
||||
+ struct handler_record_list storage;
|
||||
+ storage.count = 0;
|
||||
+ XML_ParserReset(g_parser, NULL);
|
||||
+ XML_SetDefaultHandler(g_parser, record_default_handler);
|
||||
+ XML_SetCharacterDataHandler(g_parser, record_cdata_handler);
|
||||
+ XML_SetSkippedEntityHandler(g_parser, record_skip_handler);
|
||||
+ XML_SetUserData(g_parser, &storage);
|
||||
+ if (_XML_Parse_SINGLE_BYTES(g_parser, entity_text, (int)strlen(entity_text),
|
||||
+ XML_TRUE)
|
||||
+ == XML_STATUS_ERROR)
|
||||
+ xml_failure(g_parser);
|
||||
+ /* The default handler suppresses the entity */
|
||||
+ assert_record_handler_called(&storage, 0, "record_default_handler", 9);
|
||||
+ assert_record_handler_called(&storage, 1, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 2, "record_default_handler", 3);
|
||||
+ assert_record_handler_called(&storage, 3, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 4, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 5, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 6, "record_default_handler", 8);
|
||||
+ assert_record_handler_called(&storage, 7, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 8, "record_default_handler", 6);
|
||||
+ assert_record_handler_called(&storage, 9, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 10, "record_default_handler", 7);
|
||||
+ assert_record_handler_called(&storage, 11, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 12, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 13, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 14, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 15, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 16, "record_default_handler", 5);
|
||||
+ assert_record_handler_called(&storage, 17, "record_skip_handler", 0);
|
||||
+ assert_record_handler_called(&storage, 18, "record_default_handler", 6);
|
||||
+ assert(storage.count == 19);
|
||||
+ }
|
||||
|
||||
/* This time, allow the entity through */
|
||||
- XML_ParserReset(g_parser, NULL);
|
||||
- XML_SetDefaultHandlerExpand(g_parser, record_default_handler);
|
||||
- XML_SetCharacterDataHandler(g_parser, record_cdata_handler);
|
||||
- CharData_Init(&storage);
|
||||
- XML_SetUserData(g_parser, &storage);
|
||||
- if (_XML_Parse_SINGLE_BYTES(g_parser, entity_text, (int)strlen(entity_text),
|
||||
- XML_TRUE)
|
||||
- == XML_STATUS_ERROR)
|
||||
- xml_failure(g_parser);
|
||||
- CharData_CheckXMLChars(&storage, XCS("DDDDDDDDDDDDDDDDDCDD"));
|
||||
+ {
|
||||
+ struct handler_record_list storage;
|
||||
+ storage.count = 0;
|
||||
+ XML_ParserReset(g_parser, NULL);
|
||||
+ XML_SetDefaultHandlerExpand(g_parser, record_default_handler);
|
||||
+ XML_SetCharacterDataHandler(g_parser, record_cdata_handler);
|
||||
+ XML_SetUserData(g_parser, &storage);
|
||||
+ if (_XML_Parse_SINGLE_BYTES(g_parser, entity_text, (int)strlen(entity_text),
|
||||
+ XML_TRUE)
|
||||
+ == XML_STATUS_ERROR)
|
||||
+ xml_failure(g_parser);
|
||||
+ assert_record_handler_called(&storage, 0, "record_default_handler", 9);
|
||||
+ assert_record_handler_called(&storage, 1, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 2, "record_default_handler", 3);
|
||||
+ assert_record_handler_called(&storage, 3, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 4, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 5, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 6, "record_default_handler", 8);
|
||||
+ assert_record_handler_called(&storage, 7, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 8, "record_default_handler", 6);
|
||||
+ assert_record_handler_called(&storage, 9, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 10, "record_default_handler", 7);
|
||||
+ assert_record_handler_called(&storage, 11, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 12, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 13, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 14, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 15, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 16, "record_default_handler", 5);
|
||||
+ assert_record_handler_called(&storage, 17, "record_cdata_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 18, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 19, "record_default_handler", 6);
|
||||
+ assert(storage.count == 20);
|
||||
+ }
|
||||
|
||||
/* Finally, without passing the cdata to the default handler */
|
||||
- XML_ParserReset(g_parser, NULL);
|
||||
- XML_SetDefaultHandlerExpand(g_parser, record_default_handler);
|
||||
- XML_SetCharacterDataHandler(g_parser, record_cdata_nodefault_handler);
|
||||
- CharData_Init(&storage);
|
||||
- XML_SetUserData(g_parser, &storage);
|
||||
- if (_XML_Parse_SINGLE_BYTES(g_parser, entity_text, (int)strlen(entity_text),
|
||||
- XML_TRUE)
|
||||
- == XML_STATUS_ERROR)
|
||||
- xml_failure(g_parser);
|
||||
- CharData_CheckXMLChars(&storage, XCS("DDDDDDDDDDDDDDDDDcD"));
|
||||
+ {
|
||||
+ struct handler_record_list storage;
|
||||
+ storage.count = 0;
|
||||
+ XML_ParserReset(g_parser, NULL);
|
||||
+ XML_SetDefaultHandlerExpand(g_parser, record_default_handler);
|
||||
+ XML_SetCharacterDataHandler(g_parser, record_cdata_nodefault_handler);
|
||||
+ XML_SetUserData(g_parser, &storage);
|
||||
+ if (_XML_Parse_SINGLE_BYTES(g_parser, entity_text, (int)strlen(entity_text),
|
||||
+ XML_TRUE)
|
||||
+ == XML_STATUS_ERROR)
|
||||
+ xml_failure(g_parser);
|
||||
+ assert_record_handler_called(&storage, 0, "record_default_handler", 9);
|
||||
+ assert_record_handler_called(&storage, 1, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 2, "record_default_handler", 3);
|
||||
+ assert_record_handler_called(&storage, 3, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 4, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 5, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 6, "record_default_handler", 8);
|
||||
+ assert_record_handler_called(&storage, 7, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 8, "record_default_handler", 6);
|
||||
+ assert_record_handler_called(&storage, 9, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 10, "record_default_handler", 7);
|
||||
+ assert_record_handler_called(&storage, 11, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 12, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 13, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 14, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 15, "record_default_handler", 1);
|
||||
+ assert_record_handler_called(&storage, 16, "record_default_handler", 5);
|
||||
+ assert_record_handler_called(&storage, 17, "record_cdata_nodefault_handler",
|
||||
+ 1);
|
||||
+ assert_record_handler_called(&storage, 18, "record_default_handler", 6);
|
||||
+ assert(storage.count == 19);
|
||||
+ }
|
||||
}
|
||||
END_TEST
|
||||
|
||||
--
|
||||
2.33.0
|
||||
|
||||
|
||||
54
backport-008-CVE-2023-52425.patch
Normal file
54
backport-008-CVE-2023-52425.patch
Normal file
@ -0,0 +1,54 @@
|
||||
From 7f54667c59c5a884beba5dce17003715d7cbaffa 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>
|
||||
---
|
||||
lib/internal.h | 2 ++
|
||||
lib/xmlparse.c | 5 ++++-
|
||||
2 files changed, 6 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/lib/internal.h b/lib/internal.h
|
||||
index 444eba0f..dda42d88 100644
|
||||
--- a/lib/internal.h
|
||||
+++ b/lib/internal.h
|
||||
@@ -158,6 +158,8 @@ unsigned long long testingAccountingGetCountBytesIndirect(XML_Parser parser);
|
||||
const char *unsignedCharToPrintable(unsigned char c);
|
||||
#endif
|
||||
|
||||
+extern XML_Bool g_reparseDeferralEnabledDefault; // written ONLY in runtests.c
|
||||
+
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
|
||||
index 32df1eb9..e30e76aa 100644
|
||||
--- a/lib/xmlparse.c
|
||||
+++ b/lib/xmlparse.c
|
||||
@@ -599,6 +599,8 @@ static unsigned long getDebugLevel(const char *variableName,
|
||||
? 0 \
|
||||
: ((*((pool)->ptr)++ = c), 1))
|
||||
|
||||
+XML_Bool g_reparseDeferralEnabledDefault = XML_TRUE; // write ONLY in runtests.c
|
||||
+
|
||||
struct XML_ParserStruct {
|
||||
/* The first member must be m_userData so that the XML_GetUserData
|
||||
macro works. */
|
||||
@@ -951,7 +953,8 @@ callProcessor(XML_Parser parser, const char *start, const char *end,
|
||||
const char **endPtr) {
|
||||
const size_t have_now = EXPAT_SAFE_PTR_DIFF(end, start);
|
||||
|
||||
- if (! parser->m_parsingStatus.finalBuffer) {
|
||||
+ if (g_reparseDeferralEnabledDefault
|
||||
+ && ! parser->m_parsingStatus.finalBuffer) {
|
||||
// Heuristic: don't try to parse a partial token again until the amount of
|
||||
// available data has increased significantly.
|
||||
const size_t had_before = parser->m_partialTokenBytesBefore;
|
||||
--
|
||||
2.33.0
|
||||
|
||||
|
||||
122
backport-009-CVE-2023-52425.patch
Normal file
122
backport-009-CVE-2023-52425.patch
Normal file
@ -0,0 +1,122 @@
|
||||
From 3194d762dc6a80bca5d374fe5084888386fbadcd 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>
|
||||
---
|
||||
doc/reference.html | 24 +++++++++++++++++++++++-
|
||||
lib/expat.h | 4 ++++
|
||||
lib/xmlparse.c | 13 ++++++++++++-
|
||||
3 files changed, 39 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/doc/reference.html b/doc/reference.html
|
||||
index 309cb241..1ded3bbe 100644
|
||||
--- a/doc/reference.html
|
||||
+++ b/doc/reference.html
|
||||
@@ -149,10 +149,11 @@ interface.</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>
|
||||
@@ -2172,6 +2173,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 b7d6d354..a4033742 100644
|
||||
--- a/lib/expat.h
|
||||
+++ b/lib/expat.h
|
||||
@@ -1036,6 +1036,10 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold(
|
||||
XML_Parser parser, unsigned long long activationThresholdBytes);
|
||||
#endif
|
||||
|
||||
+/* Added in Expat 2.6.0. */
|
||||
+XMLPARSEAPI(XML_Bool)
|
||||
+XML_SetReparseDeferralEnabled(XML_Parser parser, XML_Bool enabled);
|
||||
+
|
||||
/* Expat follows the semantic versioning convention.
|
||||
See http://semver.org.
|
||||
*/
|
||||
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
|
||||
index e30e76aa..d95b054b 100644
|
||||
--- a/lib/xmlparse.c
|
||||
+++ b/lib/xmlparse.c
|
||||
@@ -617,6 +617,7 @@ struct XML_ParserStruct {
|
||||
XML_Index m_parseEndByteIndex;
|
||||
const char *m_parseEndPtr;
|
||||
size_t m_partialTokenBytesBefore; /* used in heuristic to avoid O(n^2) */
|
||||
+ XML_Bool m_reparseDeferralEnabled;
|
||||
XML_Char *m_dataBuf;
|
||||
XML_Char *m_dataBufEnd;
|
||||
XML_StartElementHandler m_startElementHandler;
|
||||
@@ -953,7 +954,7 @@ callProcessor(XML_Parser parser, const char *start, const char *end,
|
||||
const char **endPtr) {
|
||||
const size_t have_now = EXPAT_SAFE_PTR_DIFF(end, start);
|
||||
|
||||
- if (g_reparseDeferralEnabledDefault
|
||||
+ if (parser->m_reparseDeferralEnabled
|
||||
&& ! parser->m_parsingStatus.finalBuffer) {
|
||||
// Heuristic: don't try to parse a partial token again until the amount of
|
||||
// available data has increased significantly.
|
||||
@@ -1149,6 +1150,7 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) {
|
||||
parser->m_parseEndByteIndex = 0;
|
||||
parser->m_parseEndPtr = NULL;
|
||||
parser->m_partialTokenBytesBefore = 0;
|
||||
+ parser->m_reparseDeferralEnabled = g_reparseDeferralEnabledDefault;
|
||||
parser->m_declElementType = NULL;
|
||||
parser->m_declAttributeId = NULL;
|
||||
parser->m_declEntity = NULL;
|
||||
@@ -2568,6 +2570,15 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold(
|
||||
}
|
||||
#endif /* defined(XML_DTD) || XML_GE == 1 */
|
||||
|
||||
+XML_Bool XMLCALL
|
||||
+XML_SetReparseDeferralEnabled(XML_Parser parser, XML_Bool enabled) {
|
||||
+ if (parser != NULL && (enabled == XML_TRUE || enabled == XML_FALSE)) {
|
||||
+ parser->m_reparseDeferralEnabled = enabled;
|
||||
+ return XML_TRUE;
|
||||
+ }
|
||||
+ return XML_FALSE;
|
||||
+}
|
||||
+
|
||||
/* Initially tag->rawName always points into the parse buffer;
|
||||
for those TAG instances opened while the current parse buffer was
|
||||
processed, and not yet closed, we need to store tag->rawName in a more
|
||||
--
|
||||
2.33.0
|
||||
|
||||
|
||||
57
backport-CVE-2024-28757-001.patch
Normal file
57
backport-CVE-2024-28757-001.patch
Normal file
@ -0,0 +1,57 @@
|
||||
From 1d50b80cf31de87750103656f6eb693746854aa8 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Mon, 4 Mar 2024 23:49:06 +0100
|
||||
Subject: [PATCH] lib/xmlparse.c: Detect billion laughs attack with isolated
|
||||
external parser
|
||||
|
||||
When parsing DTD content with code like ..
|
||||
|
||||
XML_Parser parser = XML_ParserCreate(NULL);
|
||||
XML_Parser ext_parser = XML_ExternalEntityParserCreate(parser, NULL, NULL);
|
||||
enum XML_Status status = XML_Parse(ext_parser, doc, (int)strlen(doc), XML_TRUE);
|
||||
|
||||
.. there are 0 bytes accounted as direct input and all input from accounted
|
||||
as indirect input. Now function accountingGetCurrentAmplification cannot calculate
|
||||
the current amplification ratio as "(direct + indirect) / direct", and it did refuse
|
||||
to divide by 0 as one would expect, but it returned 1.0 for this case to indicate
|
||||
no amplification over direct input. As a result, billion laughs attacks from
|
||||
DTD-only input were not detected with this isolated way of using an external parser.
|
||||
|
||||
The new approach is to assume direct input of length not 0 but 22 -- derived from
|
||||
ghost input "<!ENTITY a SYSTEM 'b'>", the shortest possible way to include an external
|
||||
DTD --, and do the usual "(direct + indirect) / direct" math with "direct := 22".
|
||||
|
||||
GitHub issue #839 has more details on this issue and its origin in ClusterFuzz
|
||||
finding 66812.
|
||||
---
|
||||
expat/lib/xmlparse.c | 6 +++++-
|
||||
1 file changed, 5 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/lib/xmlparse.c b/expat/lib/xmlparse.c
|
||||
index b884d82b..d44baa68 100644
|
||||
--- a/lib/xmlparse.c
|
||||
+++ b/lib/xmlparse.c
|
||||
@@ -7787,6 +7787,8 @@ copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) {
|
||||
|
||||
static float
|
||||
accountingGetCurrentAmplification(XML_Parser rootParser) {
|
||||
+ // 1.........1.........12 => 22
|
||||
+ const size_t lenOfShortestInclude = sizeof("<!ENTITY a SYSTEM 'b'>") - 1;
|
||||
const XmlBigCount countBytesOutput
|
||||
= rootParser->m_accounting.countBytesDirect
|
||||
+ rootParser->m_accounting.countBytesIndirect;
|
||||
@@ -7794,7 +7796,9 @@ accountingGetCurrentAmplification(XML_Parser rootParser) {
|
||||
= rootParser->m_accounting.countBytesDirect
|
||||
? (countBytesOutput
|
||||
/ (float)(rootParser->m_accounting.countBytesDirect))
|
||||
- : 1.0f;
|
||||
+ : ((lenOfShortestInclude
|
||||
+ + rootParser->m_accounting.countBytesIndirect)
|
||||
+ / (float)lenOfShortestInclude);
|
||||
assert(! rootParser->m_parentParser);
|
||||
return amplificationFactor;
|
||||
}
|
||||
--
|
||||
2.33.0
|
||||
|
||||
|
||||
26
backport-CVE-2024-28757-002.patch
Normal file
26
backport-CVE-2024-28757-002.patch
Normal file
@ -0,0 +1,26 @@
|
||||
From a4c86a395ee447c59175c762af3d17f7107b2261 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Sun, 3 Mar 2024 02:19:58 +0100
|
||||
Subject: [PATCH] lib/xmlparse.c: Reject directly recursive parameter entities
|
||||
|
||||
---
|
||||
expat/lib/xmlparse.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/lib/xmlparse.c b/expat/lib/xmlparse.c
|
||||
index b884d82b..8e667fcb 100644
|
||||
--- a/lib/xmlparse.c
|
||||
+++ b/lib/xmlparse.c
|
||||
@@ -6240,7 +6240,7 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc,
|
||||
dtd->keepProcessing = dtd->standalone;
|
||||
goto endEntityValue;
|
||||
}
|
||||
- if (entity->open) {
|
||||
+ if (entity->open || (entity == parser->m_declEntity)) {
|
||||
if (enc == parser->m_encoding)
|
||||
parser->m_eventPtr = entityTextPtr;
|
||||
result = XML_ERROR_RECURSIVE_ENTITY_REF;
|
||||
--
|
||||
2.33.0
|
||||
|
||||
|
||||
86
backport-CVE-2024-28757-003.patch
Normal file
86
backport-CVE-2024-28757-003.patch
Normal file
@ -0,0 +1,86 @@
|
||||
From bef3be76723b344565736a902c98bad5f127f5bc Mon Sep 17 00:00:00 2001
|
||||
From: caixiaomeng 00662745 <caixiaomeng2@huawei.com>
|
||||
Date: Thu, 21 Mar 2024 20:29:49 +0800
|
||||
Subject: [PATCH] tests-Cover-amplification-tracking-for-isolated-exte
|
||||
|
||||
---
|
||||
tests/runtests.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
1 file changed, 55 insertions(+)
|
||||
|
||||
diff --git a/tests/runtests.c b/tests/runtests.c
|
||||
index 2237b4d..191e7c1 100644
|
||||
--- a/tests/runtests.c
|
||||
+++ b/tests/runtests.c
|
||||
@@ -11966,6 +11966,59 @@ START_TEST(test_helper_unsigned_char_to_printable) {
|
||||
END_TEST
|
||||
#endif // defined(XML_DTD)
|
||||
|
||||
+START_TEST(test_amplification_isolated_external_parser) {
|
||||
+ // NOTE: Length 44 is precisely twice the length of "<!ENTITY a SYSTEM 'b'>"
|
||||
+ // (22) that is used in function accountingGetCurrentAmplification in
|
||||
+ // xmlparse.c.
|
||||
+ // 1.........1.........1.........1.........1..4 => 44
|
||||
+ const char doc[] = "<!ENTITY % p1 '123456789_123456789_1234567'>";
|
||||
+ const int docLen = (int)sizeof(doc) - 1;
|
||||
+ const float maximumToleratedAmplification = 2.0f;
|
||||
+
|
||||
+ struct TestCase {
|
||||
+ int offsetOfThreshold;
|
||||
+ enum XML_Status expectedStatus;
|
||||
+ };
|
||||
+
|
||||
+ struct TestCase cases[] = {
|
||||
+ {-2, XML_STATUS_ERROR}, {-1, XML_STATUS_ERROR}, {0, XML_STATUS_ERROR},
|
||||
+ {+1, XML_STATUS_OK}, {+2, XML_STATUS_OK},
|
||||
+ };
|
||||
+
|
||||
+ for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
|
||||
+ const int offsetOfThreshold = cases[i].offsetOfThreshold;
|
||||
+ const enum XML_Status expectedStatus = cases[i].expectedStatus;
|
||||
+ const unsigned long long activationThresholdBytes
|
||||
+ = docLen + offsetOfThreshold;
|
||||
+
|
||||
+ XML_Parser parser = XML_ParserCreate(NULL);
|
||||
+ assert(parser != NULL);
|
||||
+
|
||||
+ assert(XML_SetBillionLaughsAttackProtectionMaximumAmplification(
|
||||
+ parser, maximumToleratedAmplification)
|
||||
+ == XML_TRUE);
|
||||
+ assert(XML_SetBillionLaughsAttackProtectionActivationThreshold(
|
||||
+ parser, activationThresholdBytes)
|
||||
+ == XML_TRUE);
|
||||
+
|
||||
+ XML_Parser ext_parser = XML_ExternalEntityParserCreate(parser, NULL, NULL);
|
||||
+ assert(ext_parser != NULL);
|
||||
+
|
||||
+ const enum XML_Status actualStatus
|
||||
+ = _XML_Parse_SINGLE_BYTES(ext_parser, doc, docLen, XML_TRUE);
|
||||
+
|
||||
+ assert(actualStatus == expectedStatus);
|
||||
+ if (actualStatus != XML_STATUS_OK) {
|
||||
+ assert(XML_GetErrorCode(ext_parser)
|
||||
+ == XML_ERROR_AMPLIFICATION_LIMIT_BREACH);
|
||||
+ }
|
||||
+
|
||||
+ XML_ParserFree(ext_parser);
|
||||
+ XML_ParserFree(parser);
|
||||
+ }
|
||||
+}
|
||||
+END_TEST
|
||||
+
|
||||
static Suite *
|
||||
make_suite(void) {
|
||||
Suite *s = suite_create("basic");
|
||||
@@ -12206,6 +12259,8 @@ make_suite(void) {
|
||||
tcase_add_test(tc_basic, test_bad_notation);
|
||||
tcase_add_test(tc_basic, test_default_doctype_handler);
|
||||
tcase_add_test(tc_basic, test_empty_element_abort);
|
||||
+ tcase_add_test__ifdef_xml_dtd(tc_basic,
|
||||
+ test_amplification_isolated_external_parser);
|
||||
|
||||
suite_add_tcase(s, tc_namespace);
|
||||
tcase_add_checked_fixture(tc_namespace, namespace_setup, namespace_teardown);
|
||||
--
|
||||
2.33.0
|
||||
|
||||
|
||||
75
backport-CVE-2024-28757-004.patch
Normal file
75
backport-CVE-2024-28757-004.patch
Normal file
@ -0,0 +1,75 @@
|
||||
From 7cf9724d39209d085849b96e5012e658760902ad Mon Sep 17 00:00:00 2001
|
||||
From: caixiaomeng 00662745 <caixiaomeng2@huawei.com>
|
||||
Date: Thu, 21 Mar 2024 20:40:48 +0800
|
||||
Subject: [PATCH] tests-Cover-rejection-of-direct-parameter-entity-rec
|
||||
|
||||
---
|
||||
tests/runtests.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
|
||||
1 file changed, 44 insertions(+)
|
||||
|
||||
diff --git a/tests/runtests.c b/tests/runtests.c
|
||||
index 191e7c1..1c1ce56 100644
|
||||
--- a/tests/runtests.c
|
||||
+++ b/tests/runtests.c
|
||||
@@ -12019,6 +12019,48 @@ START_TEST(test_amplification_isolated_external_parser) {
|
||||
}
|
||||
END_TEST
|
||||
|
||||
+START_TEST(test_recursive_external_parameter_entity_2) {
|
||||
+ struct TestCase {
|
||||
+ const char *doc;
|
||||
+ enum XML_Status expectedStatus;
|
||||
+ };
|
||||
+
|
||||
+ struct TestCase cases[] = {
|
||||
+ {"<!ENTITY % p1 '%p1;'>", XML_STATUS_ERROR},
|
||||
+ {"<!ENTITY % p1 '%p1;'>"
|
||||
+ "<!ENTITY % p1 'first declaration wins'>",
|
||||
+ XML_STATUS_ERROR},
|
||||
+ {"<!ENTITY % p1 'first declaration wins'>"
|
||||
+ "<!ENTITY % p1 '%p1;'>",
|
||||
+ XML_STATUS_OK},
|
||||
+ {"<!ENTITY % p1 '%p1;'>", XML_STATUS_OK},
|
||||
+ };
|
||||
+
|
||||
+ for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
|
||||
+ const char *const doc = cases[i].doc;
|
||||
+ const enum XML_Status expectedStatus = cases[i].expectedStatus;
|
||||
+
|
||||
+ XML_Parser parser = XML_ParserCreate(NULL);
|
||||
+ assert(parser != NULL);
|
||||
+
|
||||
+ XML_Parser ext_parser = XML_ExternalEntityParserCreate(parser, NULL, NULL);
|
||||
+ assert(ext_parser != NULL);
|
||||
+
|
||||
+ const enum XML_Status actualStatus
|
||||
+ = _XML_Parse_SINGLE_BYTES(ext_parser, doc, (int)strlen(doc), XML_TRUE);
|
||||
+
|
||||
+ assert(actualStatus == expectedStatus);
|
||||
+ if (actualStatus != XML_STATUS_OK) {
|
||||
+ assert(XML_GetErrorCode(ext_parser)
|
||||
+ == XML_ERROR_RECURSIVE_ENTITY_REF);
|
||||
+ }
|
||||
+
|
||||
+ XML_ParserFree(ext_parser);
|
||||
+ XML_ParserFree(parser);
|
||||
+ }
|
||||
+}
|
||||
+END_TEST
|
||||
+
|
||||
static Suite *
|
||||
make_suite(void) {
|
||||
Suite *s = suite_create("basic");
|
||||
@@ -12261,6 +12303,8 @@ make_suite(void) {
|
||||
tcase_add_test(tc_basic, test_empty_element_abort);
|
||||
tcase_add_test__ifdef_xml_dtd(tc_basic,
|
||||
test_amplification_isolated_external_parser);
|
||||
+ tcase_add_test__ifdef_xml_dtd(tc_basic,
|
||||
+ test_recursive_external_parameter_entity_2);
|
||||
|
||||
suite_add_tcase(s, tc_namespace);
|
||||
tcase_add_checked_fixture(tc_namespace, namespace_setup, namespace_teardown);
|
||||
--
|
||||
2.33.0
|
||||
|
||||
|
||||
31
backport-CVE-2024-45491.patch
Normal file
31
backport-CVE-2024-45491.patch
Normal file
@ -0,0 +1,31 @@
|
||||
From 8e439a9947e9dc80a395c0c7456545d8d9d9e421 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Mon, 19 Aug 2024 22:34:13 +0200
|
||||
Subject: [PATCH] lib: Detect integer overflow in dtdCopy
|
||||
|
||||
Reported by TaiYou
|
||||
---
|
||||
expat/lib/xmlparse.c | 10 ++++++++++
|
||||
1 file changed, 10 insertions(+)
|
||||
|
||||
diff --git a/lib/xmlparse.c b/expat/lib/xmlparse.c
|
||||
index 91682c188..e2327bdcf 100644
|
||||
--- a/lib/xmlparse.c
|
||||
+++ b/lib/xmlparse.c
|
||||
@@ -7016,6 +7016,16 @@ dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd,
|
||||
if (! newE)
|
||||
return 0;
|
||||
if (oldE->nDefaultAtts) {
|
||||
+ /* Detect and prevent integer overflow.
|
||||
+ * The preprocessor guard addresses the "always false" warning
|
||||
+ * from -Wtype-limits on platforms where
|
||||
+ * sizeof(int) < sizeof(size_t), e.g. on x86_64. */
|
||||
+#if UINT_MAX >= SIZE_MAX
|
||||
+ if ((size_t)oldE->nDefaultAtts
|
||||
+ > ((size_t)(-1) / sizeof(DEFAULT_ATTRIBUTE))) {
|
||||
+ return 0;
|
||||
+ }
|
||||
+#endif
|
||||
newE->defaultAtts = (DEFAULT_ATTRIBUTE *)ms->malloc_fcn(
|
||||
oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE));
|
||||
if (! newE->defaultAtts) {
|
||||
30
backport-CVE-2024-45492.patch
Normal file
30
backport-CVE-2024-45492.patch
Normal file
@ -0,0 +1,30 @@
|
||||
From 9bf0f2c16ee86f644dd1432507edff94c08dc232 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Mon, 19 Aug 2024 22:37:16 +0200
|
||||
Subject: [PATCH] lib: Detect integer overflow in function nextScaffoldPart
|
||||
|
||||
Reported by TaiYou
|
||||
---
|
||||
expat/lib/xmlparse.c | 9 +++++++++
|
||||
1 file changed, 9 insertions(+)
|
||||
|
||||
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
|
||||
index 91682c188..f737575ea 100644
|
||||
--- a/lib/xmlparse.c
|
||||
+++ b/lib/xmlparse.c
|
||||
@@ -7558,6 +7558,15 @@ nextScaffoldPart(XML_Parser parser) {
|
||||
int next;
|
||||
|
||||
if (! dtd->scaffIndex) {
|
||||
+ /* Detect and prevent integer overflow.
|
||||
+ * The preprocessor guard addresses the "always false" warning
|
||||
+ * from -Wtype-limits on platforms where
|
||||
+ * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
|
||||
+#if UINT_MAX >= SIZE_MAX
|
||||
+ if (parser->m_groupSize > ((size_t)(-1) / sizeof(int))) {
|
||||
+ return -1;
|
||||
+ }
|
||||
+#endif
|
||||
dtd->scaffIndex = (int *)MALLOC(parser, parser->m_groupSize * sizeof(int));
|
||||
if (! dtd->scaffIndex)
|
||||
return -1;
|
||||
88
backport-CVE-2024-50602-testcase.patch
Normal file
88
backport-CVE-2024-50602-testcase.patch
Normal file
@ -0,0 +1,88 @@
|
||||
From b3836ff534c7cc78128fe7b935aad3d4353814ed Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Sun, 20 Oct 2024 23:24:27 +0200
|
||||
Subject: [PATCH 3/3] tests: Cover XML_StopParser's new handling of status
|
||||
XML_INITIALIZED
|
||||
|
||||
Prior to the fix to XML_StopParser, test test_misc_resumeparser_not_crashing
|
||||
would crash with a NULL pointer dereference in function normal_updatePosition.
|
||||
This was the AddressSanitizer output:
|
||||
|
||||
> AddressSanitizer:DEADLYSIGNAL
|
||||
> =================================================================
|
||||
> ==19700==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x5623e07ad85f bp 0x7ffcf40da650 sp 0x7ffcf40da590 T0)
|
||||
> ==19700==The signal is caused by a READ memory access.
|
||||
> ==19700==Hint: address points to the zero page.
|
||||
> #0 0x5623e07ad85f in normal_updatePosition [..]/lib/xmltok_impl.c:1781:13
|
||||
> #1 0x5623e07a52ff in initUpdatePosition [..]/lib/xmltok.c:1031:3
|
||||
> #2 0x5623e0762760 in XML_ResumeParser [..]/lib/xmlparse.c:2297:3
|
||||
> #3 0x5623e074f7c1 in test_misc_resumeparser_not_crashing() misc_tests_cxx.cpp
|
||||
> #4 0x5623e074e228 in srunner_run_all ([..]/build_asan_fuzzers/tests/runtests_cxx+0x136228)
|
||||
> #5 0x5623e0753d2d in main ([..]/build_asan_fuzzers/tests/runtests_cxx+0x13bd2d)
|
||||
> #6 0x7f802a39af79 (/lib64/libc.so.6+0x25f79)
|
||||
> #7 0x7f802a39b034 in __libc_start_main (/lib64/libc.so.6+0x26034)
|
||||
> #8 0x5623e064f340 in _start ([..]/build_asan_fuzzers/tests/runtests_cxx+0x37340)
|
||||
>
|
||||
> AddressSanitizer can not provide additional info.
|
||||
> SUMMARY: AddressSanitizer: SEGV [..]/lib/xmltok_impl.c:1781:13 in normal_updatePosition
|
||||
> ==19700==ABORTING
|
||||
|
||||
And this the UndefinedBehaviorSanitizer output:
|
||||
|
||||
> [..]/lib/xmltok_impl.c:1781:13: runtime error: load of null pointer of type 'const char'
|
||||
> SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior [..]/lib/xmltok_impl.c:1781:13 in
|
||||
---
|
||||
tests/runtests.c | 30 ++++++++++++++++++++++++++++++
|
||||
1 file changed, 30 insertions(+)
|
||||
|
||||
diff --git a/tests/runtests.c b/tests/runtests.c
|
||||
index 3c710d9..9f85011 100644
|
||||
--- a/tests/runtests.c
|
||||
+++ b/tests/runtests.c
|
||||
@@ -8089,6 +8089,34 @@ START_TEST(test_misc_deny_internal_entity_closing_doctype_issue_317) {
|
||||
}
|
||||
END_TEST
|
||||
|
||||
+START_TEST(test_misc_resumeparser_not_crashing) {
|
||||
+ XML_Parser parser = XML_ParserCreate(NULL);
|
||||
+ XML_GetBuffer(parser, 1);
|
||||
+ XML_StopParser(parser, /*resumable=*/XML_TRUE);
|
||||
+ XML_ResumeParser(parser); // could crash here, previously
|
||||
+ XML_ParserFree(parser);
|
||||
+}
|
||||
+END_TEST
|
||||
+
|
||||
+START_TEST(test_misc_stopparser_rejects_unstarted_parser) {
|
||||
+ const XML_Bool cases[] = {XML_TRUE, XML_FALSE};
|
||||
+ for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
|
||||
+ const XML_Bool resumable = cases[i];
|
||||
+ XML_Parser parser = XML_ParserCreate(NULL);
|
||||
+
|
||||
+ if (XML_GetErrorCode(parser) != XML_ERROR_NONE)
|
||||
+ fail("There was not supposed to be any initial parse error.");
|
||||
+
|
||||
+ if (XML_StopParser(parser, resumable) != XML_STATUS_ERROR)
|
||||
+ fail("Attempting to suspend a subordinate parser not faulted.");
|
||||
+
|
||||
+ if (XML_GetErrorCode(parser) != XML_ERROR_NOT_STARTED)
|
||||
+ fail("parser not started.");
|
||||
+ XML_ParserFree(parser);
|
||||
+ }
|
||||
+}
|
||||
+END_TEST
|
||||
+
|
||||
static void
|
||||
alloc_setup(void) {
|
||||
XML_Memory_Handling_Suite memsuite = {duff_allocator, duff_reallocator, free};
|
||||
@@ -12575,6 +12603,8 @@ make_suite(void) {
|
||||
tcase_add_test(tc_misc, test_misc_stop_during_end_handler_issue_240_2);
|
||||
tcase_add_test__ifdef_xml_dtd(
|
||||
tc_misc, test_misc_deny_internal_entity_closing_doctype_issue_317);
|
||||
+ tcase_add_test(tc_misc, test_misc_resumeparser_not_crashing);
|
||||
+ tcase_add_test(tc_misc, test_misc_stopparser_rejects_unstarted_parser);
|
||||
|
||||
suite_add_tcase(s, tc_alloc);
|
||||
tcase_add_checked_fixture(tc_alloc, alloc_setup, alloc_teardown);
|
||||
--
|
||||
2.27.0
|
||||
|
||||
71
backport-CVE-2024-50602.patch
Normal file
71
backport-CVE-2024-50602.patch
Normal file
@ -0,0 +1,71 @@
|
||||
From 51c7019069b862e88d94ed228659e70bddd5de09 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Mon, 21 Oct 2024 01:42:54 +0200
|
||||
Subject: [PATCH 1/3] lib: Make XML_StopParser refuse to stop/suspend an
|
||||
unstarted parser
|
||||
---
|
||||
lib/expat.h | 4 +++-
|
||||
lib/xmlparse.c | 11 ++++++++++-
|
||||
2 files changed, 13 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/lib/expat.h b/lib/expat.h
|
||||
index 29f0d1e..c5e7d02 100644
|
||||
--- a/lib/expat.h
|
||||
+++ b/lib/expat.h
|
||||
@@ -117,7 +117,9 @@ enum XML_Error {
|
||||
/* Added in 2.2.1. */
|
||||
XML_ERROR_INVALID_ARGUMENT,
|
||||
/* Added in 2.2.9. */
|
||||
- XML_ERROR_AMPLIFICATION_LIMIT_BREACH
|
||||
+ XML_ERROR_AMPLIFICATION_LIMIT_BREACH,
|
||||
+ /* Added in 2.6.4. */
|
||||
+ XML_ERROR_NOT_STARTED,
|
||||
};
|
||||
|
||||
enum XML_Content_Type {
|
||||
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
|
||||
index 56af19f..f06ed81 100644
|
||||
--- a/lib/xmlparse.c
|
||||
+++ b/lib/xmlparse.c
|
||||
@@ -2173,6 +2173,9 @@ XML_StopParser(XML_Parser parser, XML_Bool resumable) {
|
||||
if (parser == NULL)
|
||||
return XML_STATUS_ERROR;
|
||||
switch (parser->m_parsingStatus.parsing) {
|
||||
+ case XML_INITIALIZED:
|
||||
+ parser->m_errorCode = XML_ERROR_NOT_STARTED;
|
||||
+ return XML_STATUS_ERROR;
|
||||
case XML_SUSPENDED:
|
||||
if (resumable) {
|
||||
parser->m_errorCode = XML_ERROR_SUSPENDED;
|
||||
@@ -2183,7 +2186,7 @@ XML_StopParser(XML_Parser parser, XML_Bool resumable) {
|
||||
case XML_FINISHED:
|
||||
parser->m_errorCode = XML_ERROR_FINISHED;
|
||||
return XML_STATUS_ERROR;
|
||||
- default:
|
||||
+ case XML_PARSING:
|
||||
if (resumable) {
|
||||
#ifdef XML_DTD
|
||||
if (parser->m_isParamEntity) {
|
||||
@@ -2194,6 +2197,9 @@ XML_StopParser(XML_Parser parser, XML_Bool resumable) {
|
||||
parser->m_parsingStatus.parsing = XML_SUSPENDED;
|
||||
} else
|
||||
parser->m_parsingStatus.parsing = XML_FINISHED;
|
||||
+ break;
|
||||
+ default:
|
||||
+ assert(0);
|
||||
}
|
||||
return XML_STATUS_OK;
|
||||
}
|
||||
@@ -2454,6 +2460,9 @@ XML_ErrorString(enum XML_Error code) {
|
||||
case XML_ERROR_AMPLIFICATION_LIMIT_BREACH:
|
||||
return XML_L(
|
||||
"limit on input amplification factor (from DTD and entities) breached");
|
||||
+ /* Added in 2.6.4. */
|
||||
+ case XML_ERROR_NOT_STARTED:
|
||||
+ return XML_L("parser not started");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
--
|
||||
2.27.0
|
||||
|
||||
42
expat.spec
42
expat.spec
@ -1,7 +1,7 @@
|
||||
%define Rversion %(echo %{version} | sed -e 's/\\./_/g' -e 's/^/R_/')
|
||||
Name: expat
|
||||
Version: 2.2.9
|
||||
Release: 10
|
||||
Release: 15
|
||||
Summary: An XML parser library
|
||||
License: MIT
|
||||
URL: https://libexpat.github.io/
|
||||
@ -53,6 +53,29 @@ Patch41: backport-0001-CVE-2022-40674.patch
|
||||
Patch42: backport-0002-CVE-2022-40674.patch
|
||||
Patch43: backport-CVE-2022-43680.patch
|
||||
Patch44: backport-tests-Cover-overeager-DTD-destruction-in-XML_Externa.patch
|
||||
Patch45: backport-CVE-2024-28757-001.patch
|
||||
Patch46: backport-CVE-2024-28757-002.patch
|
||||
Patch47: backport-CVE-2024-28757-003.patch
|
||||
Patch48: backport-CVE-2024-28757-004.patch
|
||||
Patch49: backport-001-CVE-2023-52426.patch
|
||||
Patch50: backport-002-CVE-2023-52426.patch
|
||||
Patch51: backport-003-CVE-2023-52426.patch
|
||||
Patch52: backport-004-CVE-2023-52426.patch
|
||||
Patch53: backport-001-CVE-2023-52425.patch
|
||||
Patch54: backport-002-CVE-2023-52425.patch
|
||||
Patch55: backport-003-CVE-2023-52425.patch
|
||||
Patch56: backport-004-CVE-2023-52425.patch
|
||||
Patch57: backport-005-CVE-2023-52425.patch
|
||||
Patch58: backport-006-CVE-2023-52425.patch
|
||||
Patch59: backport-007-CVE-2023-52425.patch
|
||||
Patch60: backport-008-CVE-2023-52425.patch
|
||||
Patch61: backport-009-CVE-2023-52425.patch
|
||||
Patch62: backport-001-CVE-2024-45490.patch
|
||||
Patch63: backport-002-CVE-2024-45490.patch
|
||||
Patch64: backport-CVE-2024-45491.patch
|
||||
Patch65: backport-CVE-2024-45492.patch
|
||||
Patch66: backport-CVE-2024-50602.patch
|
||||
Patch67: backport-CVE-2024-50602-testcase.patch
|
||||
|
||||
BuildRequires: sed,autoconf,automake,gcc-c++,libtool,xmlto
|
||||
|
||||
@ -105,6 +128,21 @@ make check
|
||||
%{_mandir}/man1/*
|
||||
|
||||
%changelog
|
||||
* Tue Oct 29 2024 liningjie <liningjie@xfusion.com> - 2.2.9-15
|
||||
- fix CVE-2024-50602
|
||||
|
||||
* Wed Sep 04 2024 Funda Wang <fundawang@yeah.net> - 2.2.9-14
|
||||
- fix CVE-2024-45491, CVE-2024-45492
|
||||
|
||||
* Tue Sep 3 2024 caixiaomeng <caixiaomeng2@huawei.com> - 2.2.9-13
|
||||
- fix CVE-2024-45490
|
||||
|
||||
* Thu Apr 11 2024 caixiaomeng <caixiaomeng2@huawei.com> - 2.2.9-12
|
||||
- fix CVE-2023-52425
|
||||
|
||||
* Wed Mar 27 2024 caixiaomeng <caixiaomeng2@huawei.com> - 2.2.9-11
|
||||
- fix CVE-2024-28757 and CVE-2023-52426
|
||||
|
||||
* Tue Dec 13 2022 zhoupengcheng <zhoupengcheng11@huawei.com> - 2.2.9-10
|
||||
- Move autoreconf to build
|
||||
|
||||
@ -130,7 +168,7 @@ make check
|
||||
- Type:CVE
|
||||
- ID:CVE-2022-23852 CVE-2022-23990
|
||||
- SUG:NA
|
||||
- DESC:Fix CVE-2022-23852CVE-2022-23990
|
||||
- DESC:Fix CVE-2022-23852 CVE-2022-23990
|
||||
|
||||
* Mon Jan 17 2022 wangjie <wangjie375@huawei.com> - 2.2.9-4
|
||||
- Type:CVE
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user