Compare commits

...

10 Commits

Author SHA1 Message Date
openeuler-ci-bot
8811ed3489
!87 [sync] PR-85: fix CVE-2023-52425
From: @openeuler-sync-bot 
Reviewed-by: @hubin95 
Signed-off-by: @hubin95
2024-04-12 09:31:11 +00:00
caixiaomeng
5047d33d38 fix CVE-2023-52425
(cherry picked from commit 3ee00023b305cd172bab1315b22d133119292152)
2024-04-12 15:52:35 +08:00
openeuler-ci-bot
5a9e18c940
!84 fix CVE-2024-28757 and CVE-2023-52426
From: @pshysimon 
Reviewed-by: @hubin95 
Signed-off-by: @hubin95
2024-04-08 11:39:58 +00:00
caixiaomeng
f16bfa1671 fix CVE-2024-28757 and CVE-2023-52426 2024-04-08 16:50:42 +08:00
openeuler-ci-bot
5cee5c7f83
!60 [sync] PR-57: 将autoreconf文件内容的生成移到build中
From: @openeuler-sync-bot 
Reviewed-by: @lvying6 
Signed-off-by: @lvying6
2022-12-17 09:55:55 +00:00
ZhouPengcheng
d416184f53 Move autoreconf to build
(cherry picked from commit 9542966f5578952243998da8825fd1e409df94fe)
2022-12-17 17:45:59 +08:00
openeuler-ci-bot
73c1f73638
!54 fix CVE-2022-43680
From: @fly_fzc 
Reviewed-by: @lvying6 
Signed-off-by: @lvying6
2022-10-29 03:35:36 +00:00
fly_fzc
f9da31db8e fix CVE-2022-43680 2022-10-29 10:21:46 +08:00
openeuler-ci-bot
d7063d8a95
!46 fix CVE-2022-40674
From: @panxh_purple 
Reviewed-by: @lvying6 
Signed-off-by: @lvying6
2022-09-16 07:33:07 +00:00
panxiaohe
860c80e2fd fix CVE-2022-40674 2022-09-15 15:55:32 +08:00
23 changed files with 2221 additions and 3 deletions

Binary file not shown.

View File

@ -0,0 +1,52 @@
From 4a32da87e931ba54393d465bb77c40b5c33d343b Mon Sep 17 00:00:00 2001
From: Rhodri James <rhodri@wildebeest.org.uk>
Date: Wed, 17 Aug 2022 18:26:18 +0100
Subject: [PATCH] Ensure raw tagnames are safe exiting internalEntityParser
It is possible to concoct a situation in which parsing is
suspended while substituting in an internal entity, so that
XML_ResumeParser directly uses internalEntityProcessor as
its processor. If the subsequent parse includes some unclosed
tags, this will return without calling storeRawNames to ensure
that the raw versions of the tag names are stored in memory other
than the parse buffer itself. If the parse buffer is then changed
or reallocated (for example if processing a file line by line),
badness will ensue.
This patch ensures storeRawNames is always called when needed
after calling doContent. The earlier call do doContent does
not need the same protection; it only deals with entity
substitution, which cannot leave unbalanced tags, and in any
case the raw names will be pointing into the stored entity
value not the parse buffer.
---
lib/xmlparse.c | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
index 7bcabf7f..d73f419c 100644
--- a/lib/xmlparse.c
+++ b/lib/xmlparse.c
@@ -5826,10 +5826,15 @@ internalEntityProcessor(XML_Parser parser, const char *s, const char *end,
{
parser->m_processor = contentProcessor;
/* see externalEntityContentProcessor vs contentProcessor */
- return doContent(parser, parser->m_parentParser ? 1 : 0, parser->m_encoding,
- s, end, nextPtr,
- (XML_Bool)! parser->m_parsingStatus.finalBuffer,
- XML_ACCOUNT_DIRECT);
+ result = doContent(parser, parser->m_parentParser ? 1 : 0,
+ parser->m_encoding, s, end, nextPtr,
+ (XML_Bool)! parser->m_parsingStatus.finalBuffer,
+ XML_ACCOUNT_DIRECT);
+ if (result == XML_ERROR_NONE) {
+ if (! storeRawNames(parser))
+ return XML_ERROR_NO_MEMORY;
+ }
+ return result;
}
}
--
2.27.0

View File

@ -0,0 +1,104 @@
From a7ce80a013f2a08cb1ac4aac368f2250eea03ebf Mon Sep 17 00:00:00 2001
From: Sebastian Pipping <sebastian@pipping.org>
Date: Sun, 11 Sep 2022 19:34:33 +0200
Subject: [PATCH] tests: Cover heap use-after-free issue in doContent
---
tests/runtests.c | 74 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 74 insertions(+)
diff --git a/tests/runtests.c b/tests/runtests.c
index ea371b42..ab3aff65 100644
--- a/tests/runtests.c
+++ b/tests/runtests.c
@@ -4990,6 +4990,78 @@ START_TEST(test_suspend_resume_internal_entity) {
}
END_TEST
+void
+suspending_comment_handler(void *userData, const XML_Char *data) {
+ UNUSED_P(data);
+ XML_Parser parser = (XML_Parser)userData;
+ XML_StopParser(parser, XML_TRUE);
+}
+
+START_TEST(test_suspend_resume_internal_entity_issue_629) {
+ const char *const text
+ = "<!DOCTYPE a [<!ENTITY e '<!--COMMENT-->a'>]><a>&e;<b>\n"
+ "<"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "/>"
+ "</b></a>";
+ const size_t firstChunkSizeBytes = 54;
+
+ XML_Parser parser = XML_ParserCreate(NULL);
+ XML_SetUserData(parser, parser);
+ XML_SetCommentHandler(parser, suspending_comment_handler);
+
+ if (XML_Parse(parser, text, (int)firstChunkSizeBytes, XML_FALSE)
+ != XML_STATUS_SUSPENDED)
+ xml_failure(parser);
+ if (XML_ResumeParser(parser) != XML_STATUS_OK)
+ xml_failure(parser);
+ if (XML_Parse(parser, text + firstChunkSizeBytes,
+ (int)(strlen(text) - firstChunkSizeBytes), XML_TRUE)
+ != XML_STATUS_OK)
+ xml_failure(parser);
+ XML_ParserFree(parser);
+}
+END_TEST
+
/* Test syntax error is caught at parse resumption */
START_TEST(test_resume_entity_with_syntax_error) {
const char *text = "<!DOCTYPE doc [\n"
@@ -12016,6 +12088,8 @@ make_suite(void) {
tcase_add_test(tc_basic, test_partial_char_in_epilog);
tcase_add_test(tc_basic, test_hash_collision);
tcase_add_test__ifdef_xml_dtd(tc_basic, test_suspend_resume_internal_entity);
+ tcase_add_test__ifdef_xml_dtd(tc_basic,
+ test_suspend_resume_internal_entity_issue_629);
tcase_add_test__ifdef_xml_dtd(tc_basic, test_resume_entity_with_syntax_error);
tcase_add_test__ifdef_xml_dtd(tc_basic, test_suspend_resume_parameter_entity);
tcase_add_test(tc_basic, test_restart_on_error);
--
2.27.0

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,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,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 | 71 +--
xmlwf/xmlwf.c | 18 +-
10 files changed, 644 insertions(+), 46 deletions(-)
diff --git a/lib/expat.h b/lib/expat.h
index b7d6d35..a4fb7cf 100644
--- a/lib/expat.h
+++ b/lib/expat.h
@@ -1024,13 +1024,15 @@ typedef struct {
XMLPARSEAPI(const XML_Feature *)
XML_GetFeatureList(void);
-#ifdef XML_DTD
-/* Added in Expat 2.4.0. */
+#if defined(XML_DTD) || XML_GE == 1
+/* Added in Expat 2.4.0 for XML_DTD defined and
+ * added in Expat 2.6.0 for XML_GE == 1. */
XMLPARSEAPI(XML_Bool)
XML_SetBillionLaughsAttackProtectionMaximumAmplification(
XML_Parser parser, float maximumAmplificationFactor);
-/* Added in Expat 2.4.0. */
+/* Added in Expat 2.4.0 for XML_DTD defined and
+ * added in Expat 2.6.0 for XML_GE == 1. */
XMLPARSEAPI(XML_Bool)
XML_SetBillionLaughsAttackProtectionActivationThreshold(
XML_Parser parser, unsigned long long activationThresholdBytes);
diff --git a/lib/internal.h b/lib/internal.h
index 444eba0..0b1fbfb 100644
--- a/lib/internal.h
+++ b/lib/internal.h
@@ -152,7 +152,7 @@ extern "C" {
void _INTERNAL_trim_to_complete_utf8_characters(const char *from,
const char **fromLimRef);
-#if defined(XML_DTD)
+#if defined(XML_DTD) || XML_GE == 1
unsigned long long testingAccountingGetCountBytesDirect(XML_Parser parser);
unsigned long long testingAccountingGetCountBytesIndirect(XML_Parser parser);
const char *unsignedCharToPrintable(unsigned char c);
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
index 3e458e1..689095a 100644
--- a/lib/xmlparse.c
+++ b/lib/xmlparse.c
@@ -404,7 +404,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;
@@ -420,7 +420,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);
@@ -558,7 +558,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);
@@ -581,7 +581,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);
@@ -699,7 +699,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
@@ -1158,7 +1158,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
@@ -2517,8 +2517,9 @@ XML_GetFeatureList(void) {
#ifdef XML_ATTR_INFO
{XML_FEATURE_ATTR_INFO, XML_L("XML_ATTR_INFO"), 0},
#endif
-#ifdef XML_DTD
- /* Added in Expat 2.4.0. */
+#if defined(XML_DTD) || XML_GE == 1
+ /* Added in Expat 2.4.0 for XML_DTD defined and
+ * added in Expat 2.6.0 for XML_GE == 1. */
{XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT,
XML_L("XML_BLAP_MAX_AMP"),
(long int)
@@ -2532,7 +2533,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) {
@@ -2554,7 +2555,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
@@ -2640,13 +2641,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
@@ -2760,7 +2761,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)
@@ -2826,14 +2827,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)
@@ -4033,7 +4034,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;
@@ -4185,7 +4186,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);
@@ -4277,7 +4278,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);
@@ -4484,7 +4485,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);
@@ -4700,11 +4701,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)) {
@@ -5641,7 +5644,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);
@@ -5721,7 +5724,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;
@@ -5755,9 +5758,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 */
@@ -5805,7 +5808,7 @@ internalEntityProcessor(XML_Parser parser, const char *s, const char *end,
entity->processed = (int)(next - (const 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;
@@ -5877,7 +5880,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;
@@ -5942,14 +5945,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;
@@ -6027,14 +6030,14 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
enum XML_Error result;
const XML_Char *textEnd = entity->textPtr + entity->textLen;
entity->open = XML_TRUE;
-#ifdef XML_DTD
+#if defined(XML_DTD) || XML_GE == 1
entityTrackingOnOpen(parser, entity, __LINE__);
#endif
result = appendAttributeValue(parser, parser->m_internalEncoding,
isCdata, (const char *)entity->textPtr,
(const char *)textEnd, pool,
XML_ACCOUNT_ENTITY_EXPANSION);
-#ifdef XML_DTD
+#if defined(XML_DTD) || XML_GE == 1
entityTrackingOnClose(parser, entity, __LINE__);
#endif
entity->open = XML_FALSE;
@@ -6090,7 +6093,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);
@@ -7636,7 +7639,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) {
@@ -8367,7 +8370,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 342d6c5..20b0504 100644
--- a/xmlwf/xmlwf.c
+++ b/xmlwf/xmlwf.c
@@ -1061,9 +1061,10 @@ tmain(int argc, XML_Char **argv) {
" (needs a floating point number greater or equal than 1.0)"));
exit(XMLWF_EXIT_USAGE_ERROR);
}
-#ifndef XML_DTD
- ftprintf(stderr, T("Warning: Given amplification limit ignored") T(
- ", xmlwf has been compiled without DTD support.\n"));
+#if ! defined(XML_DTD) && XML_GE == 0
+ ftprintf(stderr,
+ T("Warning: Given amplification limit ignored")
+ T(", xmlwf has been compiled without DTD/GE support.\n"));
#endif
break;
}
@@ -1082,9 +1083,10 @@ tmain(int argc, XML_Char **argv) {
exit(XMLWF_EXIT_USAGE_ERROR);
}
attackThresholdGiven = XML_TRUE;
-#ifndef XML_DTD
- ftprintf(stderr, T("Warning: Given attack threshold ignored") T(
- ", xmlwf has been compiled without DTD support.\n"));
+#if ! defined(XML_DTD) && XML_GE == 0
+ ftprintf(stderr,
+ T("Warning: Given attack threshold ignored")
+ T(", xmlwf has been compiled without DTD/GE support.\n"));
#endif
break;
}
@@ -1119,13 +1121,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,29 @@
From 5290462a7ea1278a8d5c0d5b2860d4e244f997e4 Mon Sep 17 00:00:00 2001
From: Sebastian Pipping <sebastian@pipping.org>
Date: Tue, 20 Sep 2022 02:44:34 +0200
Subject: [PATCH] lib: Fix overeager DTD destruction in
XML_ExternalEntityParserCreate
---
lib/xmlparse.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
index aacd6e7fc..57bf103cc 100644
--- a/lib/xmlparse.c
+++ b/lib/xmlparse.c
@@ -1068,6 +1068,14 @@ parserCreate(const XML_Char *encodingName,
parserInit(parser, encodingName);
if (encodingName && ! parser->m_protocolEncodingName) {
+ if (dtd) {
+ // We need to stop the upcoming call to XML_ParserFree from happily
+ // destroying parser->m_dtd because the DTD is shared with the parent
+ // parser and the only guard that keeps XML_ParserFree from destroying
+ // parser->m_dtd is parser->m_isParamEntity but it will be set to
+ // XML_TRUE only later in XML_ExternalEntityParserCreate (or not at all).
+ parser->m_dtd = NULL;
+ }
XML_ParserFree(parser);
return NULL;
}

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,77 @@
From 43992e4ae25fc3dc0eec0cd3a29313555d56aee2 Mon Sep 17 00:00:00 2001
From: Sebastian Pipping <sebastian@pipping.org>
Date: Mon, 19 Sep 2022 18:16:15 +0200
Subject: [PATCH] tests: Cover overeager DTD destruction in
XML_ExternalEntityParserCreate
---
tests/runtests.c | 49 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 49 insertions(+)
diff --git a/tests/runtests.c b/tests/runtests.c
index 245fe9bda..acb744dd4 100644
--- a/tests/runtests.c
+++ b/tests/runtests.c
@@ -10208,6 +10208,53 @@ START_TEST(test_alloc_long_notation) {
}
END_TEST
+static int XMLCALL
+external_entity_parser_create_alloc_fail_handler(XML_Parser parser,
+ const XML_Char *context,
+ const XML_Char *base,
+ const XML_Char *systemId,
+ const XML_Char *publicId) {
+ UNUSED_P(base);
+ UNUSED_P(systemId);
+ UNUSED_P(publicId);
+
+ if (context != NULL)
+ fail("Unexpected non-NULL context");
+
+ // The following number intends to fail the upcoming allocation in line
+ // "parser->m_protocolEncodingName = copyString(encodingName,
+ // &(parser->m_mem));" in function parserInit.
+ allocation_count = 3;
+
+ const XML_Char *const encodingName = XCS("UTF-8"); // needs something non-NULL
+ const XML_Parser ext_parser
+ = XML_ExternalEntityParserCreate(parser, context, encodingName);
+ if (ext_parser != NULL)
+ fail(
+ "Call to XML_ExternalEntityParserCreate was expected to fail out-of-memory");
+
+ allocation_count = ALLOC_ALWAYS_SUCCEED;
+ return XML_STATUS_ERROR;
+}
+
+START_TEST(test_alloc_reset_after_external_entity_parser_create_fail) {
+ const char *const text = "<!DOCTYPE doc SYSTEM 'foo'><doc/>";
+
+ XML_SetExternalEntityRefHandler(
+ g_parser, external_entity_parser_create_alloc_fail_handler);
+ XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+
+ if (XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE)
+ != XML_STATUS_ERROR)
+ fail("Call to parse was expected to fail");
+
+ if (XML_GetErrorCode(g_parser) != XML_ERROR_EXTERNAL_ENTITY_HANDLING)
+ fail("Call to parse was expected to fail from the external entity handler");
+
+ XML_ParserReset(g_parser, NULL);
+}
+END_TEST
+
static void
nsalloc_setup(void) {
XML_Memory_Handling_Suite memsuite = {duff_allocator, duff_reallocator, free};
@@ -12401,6 +12448,8 @@ make_suite(void) {
tcase_add_test(tc_alloc, test_alloc_long_public_id);
tcase_add_test(tc_alloc, test_alloc_long_entity_value);
tcase_add_test(tc_alloc, test_alloc_long_notation);
+ tcase_add_test__ifdef_xml_dtd(
+ tc_alloc, test_alloc_reset_after_external_entity_parser_create_fail);
suite_add_tcase(s, tc_nsalloc);
tcase_add_checked_fixture(tc_nsalloc, nsalloc_setup, nsalloc_teardown);

View File

@ -1,7 +1,7 @@
%define Rversion %(echo %{version} | sed -e 's/\\./_/g' -e 's/^/R_/')
Name: expat
Version: 2.4.1
Release: 5
Release: 11
Summary: An XML parser library
License: MIT
URL: https://libexpat.github.io/
@ -26,6 +26,27 @@ Patch15: backport-lib-Fix-harmless-use-of-uninitialized-memory.patch
Patch16: backport-lib-Drop-unused-macro-UTF8_GET_NAMING.patch
Patch17: backport-lib-Relax-fix-to-CVE-2022-25236-with-regard-to-RFC-3.patch
Patch18: backport-tests-Cover-relaxed-fix-to-CVE-2022-25236.patch
Patch19: backport-0001-CVE-2022-40674.patch
Patch20: backport-0002-CVE-2022-40674.patch
Patch21: backport-CVE-2022-43680.patch
Patch22: backport-tests-Cover-overeager-DTD-destruction-in-XML_Externa.patch
Patch23: backport-CVE-2024-28757-001.patch
Patch24: backport-CVE-2024-28757-002.patch
Patch25: backport-CVE-2024-28757-003.patch
Patch26: backport-CVE-2024-28757-004.patch
Patch27: backport-001-CVE-2023-52426.patch
Patch28: backport-002-CVE-2023-52426.patch
Patch29: backport-003-CVE-2023-52426.patch
Patch30: backport-004-CVE-2023-52426.patch
Patch31: backport-001-CVE-2023-52425.patch
Patch32: backport-002-CVE-2023-52425.patch
Patch33: backport-003-CVE-2023-52425.patch
Patch34: backport-004-CVE-2023-52425.patch
Patch35: backport-005-CVE-2023-52425.patch
Patch36: backport-006-CVE-2023-52425.patch
Patch37: backport-007-CVE-2023-52425.patch
Patch38: backport-008-CVE-2023-52425.patch
Patch39: backport-009-CVE-2023-52425.patch
BuildRequires: sed,autoconf,automake,gcc-c++,libtool,xmlto
@ -45,8 +66,8 @@ This package provides with static libraries and header files for developing wit
%prep
%autosetup -p1
autoreconf -fiv
%build
autoreconf -fiv
%configure CFLAGS="$RPM_OPT_FLAGS -fPIC" DOCBOOK_TO_MAN="xmlto man --skip-validation"
%make_build
@ -79,6 +100,27 @@ make check
%{_mandir}/man1/*
%changelog
* Thu Apr 11 2024 caixiaomeng <caixiaomeng2@huawei.com> - 2.4.1-11
- fix CVE-2023-52425
* Mon Mar 25 2024 caixiaomeng <caixiaomeng2@huawei.com> - 2.4.1-10
- fix CVE-2023-52426
* Mon Mar 25 2024 caixiaomeng <caixiaomeng2@huawei.com> - 2.4.1-9
- fix CVE-2024-28757
* Sat Oct 29 2022 fuanan <fuanan3@h-partners.com> - 2.4.1-8
- fix CVE-2022-43680
* Tue Oct 11 2022 huangduirong <huangduirong@huawei.com> - 2.4.1-7
- Type:bugfix
- ID:NA
- SUG:NA
- DESC:Move autoreconf to build
* Thu Sep 15 2022 panxiaohe <panxh.life@foxmail.com> - 2.4.1-6
- fix CVE-2022-40674
* Mon Mar 7 2022 yangzhuangzhuang <yangzhuangzhuang1@h-partners.com> - 2.4.1-5
- Type:bugfix
- ID:NA
@ -87,7 +129,7 @@ make check
* Sat Feb 26 2022 yangzhuangzhuang <yangzhuangzhuang1@h-partners.com> - 2.4.1-4
- Type:CVE
- ID:CVE-2022-25235 CVE-2022-25236 CVE-2022-25313 CVE-2022-25314 CVE-2022-25315
- ID:Fix CVE-2022-25235 CVE-2022-25236 CVE-2022-25313 CVE-2022-25314 CVE-2022-25315
- SUG:NA
- DESC:Fix CVE-2022-25235 CVE-2022-25236 CVE-2022-25313 CVE-2022-25314 CVE-2022-25315
@ -146,3 +188,4 @@ make check
* Thu Aug 29 2019 openEuler Buildteam <buildteam@openeuler.org> - 2.2.6-1
- Package Init