Compare commits

...

10 Commits

Author SHA1 Message Date
openeuler-ci-bot
77dd4a0e20
!118 fix CVE-2024-50602
From: @li_ning_jie 
Reviewed-by: @dillon_chen 
Signed-off-by: @dillon_chen
2024-10-30 07:30:23 +00:00
liningjie
741a73e2d5 fix CVE-2024-50602 2024-10-29 20:26:01 +08:00
openeuler-ci-bot
bd13e657ad
!106 fix CVE-2024-45491, CVE-2024-45492
From: @fundawang 
Reviewed-by: @dillon_chen 
Signed-off-by: @dillon_chen
2024-09-05 09:53:50 +00:00
Funda Wang
0f4b9d1705 fix CVE-2024-45491, CVE-2024-45492 2024-09-04 08:55:03 +08:00
openeuler-ci-bot
b6a11ec365
!99 fix CVE 2024 45490
From: @pshysimon 
Reviewed-by: @hubin95 
Signed-off-by: @hubin95
2024-09-03 08:47:18 +00:00
pshysimon
b1ec6c4caa fix CVE 2024 45490 2024-09-03 15:02:28 +08:00
openeuler-ci-bot
a39fbb7c4a
!91 [sync] PR-86: fix CVE-2023-52425
From: @openeuler-sync-bot 
Reviewed-by: @hubin95 
Signed-off-by: @hubin95
2024-04-12 09:27:55 +00:00
caixiaomeng
cbc39e564c fix CVE-2023-52425
(cherry picked from commit 83b011937d6672eaaf68a4d4be2b43467c77e505)
2024-04-12 16:06:30 +08:00
openeuler-ci-bot
0eaf306133
!78 fix CVE-2024-28757 and CVE-2023-52426
From: @pshysimon 
Reviewed-by: @hubin95 
Signed-off-by: @hubin95
2024-04-08 08:13:51 +00:00
caixiaomeng
756d8acea5 fix CVE-2024-28757 and CVE-2023-52426 2024-04-03 15:26:54 +08:00
24 changed files with 2288 additions and 2 deletions

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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 "&amp;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 "&amp;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 "&amp;entity123;" in entity->textPtr
+ // to end up as "&entity123;" in the handler.
+ const char *const entity_start = "&amp;";
+ 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

View 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

View 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

View 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 '&#37;'>\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

View 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

View 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

View 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

View 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

View 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

View 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 '&#37;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

View 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) {

View 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;

View 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

View 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

View File

@ -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