!6 fix CVE-2013-0340 and fix reading uninitialized variable

From: @panxh_purple
Reviewed-by: @xiezhipeng1
Signed-off-by: @xiezhipeng1
This commit is contained in:
openeuler-ci-bot 2021-07-05 11:43:24 +00:00 committed by Gitee
commit d940c756de
20 changed files with 3295 additions and 3 deletions

View File

@ -0,0 +1,58 @@
From f01a61402cd44bb0cb59db43e70309c01acc50d1 Mon Sep 17 00:00:00 2001
From: Sebastian Pipping <sebastian@pipping.org>
Date: Mon, 5 Apr 2021 17:23:26 +0200
Subject: [PATCH] Autotools: Give test suite access to internal symbols
---
lib/Makefile.am | 10 +++++++++-
tests/Makefile.am | 4 ++--
2 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 05343e2..f35a2e1 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -34,16 +34,24 @@ include_HEADERS = \
expat_external.h
lib_LTLIBRARIES = libexpat.la
+noinst_LTLIBRARIES = libexpatinternal.la
libexpat_la_LDFLAGS = \
-no-undefined \
-version-info @LIBCURRENT@:@LIBREVISION@:@LIBAGE@
-libexpat_la_SOURCES = \
+libexpat_la_SOURCES =
+
+# This layer of indirection allows
+# the test suite to access internal symbols
+# despite compiling with -fvisibility=hidden
+libexpatinternal_la_SOURCES = \
xmlparse.c \
xmltok.c \
xmlrole.c
+libexpat_la_LIBADD = libexpatinternal.la
+
doc_DATA = \
../AUTHORS \
../Changes
diff --git a/tests/Makefile.am b/tests/Makefile.am
index e19fc1a..9724717 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -52,8 +52,8 @@ runtests_SOURCES = \
runtestspp_SOURCES = \
runtestspp.cpp
-runtests_LDADD = libruntests.a ../lib/libexpat.la
-runtestspp_LDADD = libruntests.a ../lib/libexpat.la
+runtests_LDADD = libruntests.a ../lib/libexpatinternal.la
+runtestspp_LDADD = libruntests.a ../lib/libexpatinternal.la
EXTRA_DIST = \
chardata.h \
--
1.8.3.1

View File

@ -0,0 +1,50 @@
From 1e053c698b7ff8f7b4cc3c7c6e9945b72c3d5286 Mon Sep 17 00:00:00 2001
From: Sebastian Pipping <sebastian@pipping.org>
Date: Tue, 20 Apr 2021 19:01:30 +0200
Subject: [PATCH] Autotools|CMake: Suppress -Wpedantic-ms-format false
positives
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Addresses warning:
ISO C does not support the I64 ms_printf length modifier.
It seems correct and relevant with __USE_MINGW_ANSI_STDIO, only.
And -Werror doesn't tolerate false positives...
---
CMakeLists.txt | 4 ++++
configure.ac | 2 +-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 951df0e..96ac5da 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -248,6 +248,10 @@ if(FLAG_VISIBILITY)
add_definitions(-DXML_ENABLE_VISIBILITY=1)
set(EXTRA_COMPILE_FLAGS "${EXTRA_COMPILE_FLAGS} -fvisibility=hidden")
endif(FLAG_VISIBILITY)
+if(MINGW)
+ # Without __USE_MINGW_ANSI_STDIO the compiler produces a false positive
+ set(EXTRA_COMPILE_FLAGS "${EXTRA_COMPILE_FLAGS} -Wno-pedantic-ms-format")
+endif()
if (EXPAT_WARNINGS_AS_ERRORS)
if(MSVC)
add_definitions(/WX)
diff --git a/configure.ac b/configure.ac
index f26ce6c..7d60a2c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -111,7 +111,7 @@ AS_IF([test "$GCC" = yes],
AX_APPEND_COMPILE_FLAGS([-fno-strict-aliasing -Wmissing-prototypes -Wstrict-prototypes], [CFLAGS])
AX_APPEND_COMPILE_FLAGS([-pedantic -Wduplicated-cond -Wduplicated-branches -Wlogical-op], [CFLAGS])
AX_APPEND_COMPILE_FLAGS([-Wrestrict -Wnull-dereference -Wjump-misses-init -Wdouble-promotion], [CFLAGS])
- AX_APPEND_COMPILE_FLAGS([-Wshadow -Wformat=2 -Wmisleading-indentation], [CFLAGS])])
+ AX_APPEND_COMPILE_FLAGS([-Wshadow -Wformat=2 -Wno-pedantic-ms-format -Wmisleading-indentation], [CFLAGS])])
AC_LANG_PUSH([C++])
AC_PROG_CXX
--
1.8.3.1

View File

@ -0,0 +1,68 @@
From 3f2f8786623cc3e89a1f4384715b3ad178c5ee2c Mon Sep 17 00:00:00 2001
From: Sebastian Pipping <sebastian@pipping.org>
Date: Mon, 19 Apr 2021 15:08:17 +0200
Subject: [PATCH] Changes: Document protection against billion laughs attacks
---
Changes | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/Changes b/Changes
index 2ecc8a0..a435999 100644
--- a/Changes
+++ b/Changes
@@ -3,10 +3,39 @@ NOTE: We are looking for help with a few things:
If you can help, please get in touch. Thanks!
Release 2.2.9 Wed Septemper 25 2019
+ Security fixes:
+ #34 #466 CVE-2013-0340/CWE-776 -- Protect against billion laughs attacks
+ (denial-of-service; flavors targeting CPU time or RAM or both,
+ leveraging general entities or parameter entities or both)
+ by tracking and limiting the input amplification factor
+ (<amplification> := (<direct> + <indirect>) / <direct>).
+ By conservative default, amplification up to a factor of 100.0
+ is tolerated and rejection only starts after 8 MiB of output bytes
+ (=<direct> + <indirect>) have been processed.
+ A new error code XML_ERROR_AMPLIFICATION_LIMIT_BREACH signals
+ this condition.
+
Bug fixes:
#390 #395 Fix undefined behavior during parsing when compiled with
-DXML_UNICODE that was introduced with Expat 2.0.1
+ New features:
+ #34 #466 Add two new API functions to further tighten billion laughs
+ protection parameters when desired.
+ - XML_SetBillionLaughsAttackProtectionMaximumAmplification
+ - XML_SetBillionLaughsAttackProtectionActivationThreshold
+ Please see file "doc/reference.html" for more details.
+ If you ever need to increase the defaults for non-attack XML
+ payload, please file a bug report with libexpat.
+ #34 #466 Introduce environment switches EXPAT_ACCOUNTING_DEBUG=(0|1|2|3)
+ and EXPAT_ENTITY_DEBUG=(0|1) for runtime debugging of accounting
+ and entity processing; specific behavior of these values may
+ change in the future.
+ #34 #466 xmlwf: Add arguments "-a FACTOR" and "-b BYTES" to further tighten
+ billion laughs protection parameters when desired.
+ If you ever need to increase the defaults for non-attack XML
+ payload, please file a bug report with libexpat.
+
Other changes:
examples: Drop executable bits from elements.c
#349 Windows: Change the name of the Windows DLLs from expat*.dll
@@ -20,6 +49,11 @@ Release 2.2.9 Wed Septemper 25 2019
Special thanks to:
Ben Wagner
+ Nick Wellnhofer
+ Yury Gribov
+ and
+ Clang LeakSan
+ JetBrains
Release 2.2.8 Fri Septemper 13 2019
Security fixes:
--
1.8.3.1

View File

@ -0,0 +1,130 @@
From 899c00e613800ef973a93ce8f83b3514992f1afa Mon Sep 17 00:00:00 2001
From: Sebastian Pipping <sebastian@pipping.org>
Date: Sun, 25 Apr 2021 20:57:17 +0200
Subject: [PATCH] doc/reference.html: Document billion laughs attack protection
API
---
doc/reference.html | 99 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 99 insertions(+)
diff --git a/doc/reference.html b/doc/reference.html
index 1b37071..06a70e2 100644
--- a/doc/reference.html
+++ b/doc/reference.html
@@ -149,6 +149,13 @@ interface.</p>
<li><a href="#XML_GetInputContext">XML_GetInputContext</a></li>
</ul>
</li>
+ <li>
+ <a href="#billion-laughs">Billion Laughs Attack Protection</a>
+ <ul>
+ <li><a href="#XML_SetBillionLaughsAttackProtectionMaximumAmplification">XML_SetBillionLaughsAttackProtectionMaximumAmplification</a></li>
+ <li><a href="#XML_SetBillionLaughsAttackProtectionActivationThreshold">XML_SetBillionLaughsAttackProtectionActivationThreshold</a></li>
+ </ul>
+ </li>
<li><a href="#miscellaneous">Miscellaneous Functions</a>
<ul>
<li><a href="#XML_SetUserData">XML_SetUserData</a></li>
@@ -2074,6 +2081,98 @@ parse position may be before the beginning of the buffer.</p>
return NULL.</p>
</div>
+<h3><a name="billion-laughs">Billion Laughs Attack Protection</a></h3>
+
+<p>The functions in this section configure the built-in
+ protection against various forms of
+ <a href="https://en.wikipedia.org/wiki/Billion_laughs_attack">billion laughs attacks</a>.</p>
+
+<h4 id="XML_SetBillionLaughsAttackProtectionMaximumAmplification">XML_SetBillionLaughsAttackProtectionMaximumAmplification</h4>
+<pre class="fcndec">
+/* Added in Expat 2.4.0. */
+XML_Bool XMLCALL
+XML_SetBillionLaughsAttackProtectionMaximumAmplification(XML_Parser p,
+ float maximumAmplificationFactor);
+</pre>
+<div class="fcndef">
+ <p>
+ Sets the maximum tolerated amplification factor
+ for protection against
+ <a href="https://en.wikipedia.org/wiki/Billion_laughs_attack">billion laughs attacks</a>
+ (default: <code>100.0</code>)
+ of parser <code>p</code> to <code>maximumAmplificationFactor</code>, and
+ returns <code>XML_TRUE</code> upon success and <code>XML_TRUE</code> upon error.
+ </p>
+
+ The amplification factor is calculated as ..
+ <pre>
+ amplification := (direct + indirect) / direct
+ </pre>
+ .. while parsing, whereas
+ <code>direct</code> is the number of bytes read from the primary document in parsing and
+ <code>indirect</code> is the number of bytes added by expanding entities and reading of external DTD files, combined.
+
+ <p>For a call to <code>XML_SetBillionLaughsAttackProtectionMaximumAmplification</code> to succeed:</p>
+ <ul>
+ <li>parser <code>p</code> must be a non-<code>NULL</code> root parser (without any parent parsers) and</li>
+ <li><code>maximumAmplificationFactor</code> must be non-<code>NaN</code> and greater than or equal to <code>1.0</code>.</li>
+ </ul>
+
+ <p>
+ <strong>Note:</strong>
+ If you ever need to increase this value for non-attack payload,
+ please <a href="https://github.com/libexpat/libexpat/issues">file a bug report</a>.
+ </p>
+
+ <p>
+ <strong>Note:</strong>
+ Peak amplifications
+ of factor 15,000 for the entire payload and
+ of factor 30,000 in the middle of parsing
+ have been observed with small benign files in practice.
+
+ So if you do reduce the maximum allowed amplification,
+ please make sure that the activation threshold is still big enough
+ to not end up with undesired false positives (i.e. benign files being rejected).
+ </p>
+</div>
+
+<h4 id="XML_SetBillionLaughsAttackProtectionActivationThreshold">XML_SetBillionLaughsAttackProtectionActivationThreshold</h4>
+<pre class="fcndec">
+/* Added in Expat 2.4.0. */
+XML_Bool XMLCALL
+XML_SetBillionLaughsAttackProtectionActivationThreshold(XML_Parser p,
+ unsigned long long activationThresholdBytes);
+</pre>
+<div class="fcndef">
+ <p>
+ Sets number of output bytes (including amplification from entity expansion and reading DTD files)
+ needed to activate protection against
+ <a href="https://en.wikipedia.org/wiki/Billion_laughs_attack">billion laughs attacks</a>
+ (default: <code>8 MiB</code>)
+ of parser <code>p</code> to <code>activationThresholdBytes</code>, and
+ returns <code>XML_TRUE</code> upon success and <code>XML_TRUE</code> upon error.
+ </p>
+
+ <p>For a call to <code>XML_SetBillionLaughsAttackProtectionActivationThreshold</code> to succeed:</p>
+ <ul>
+ <li>parser <code>p</code> must be a non-<code>NULL</code> root parser (without any parent parsers).</li>
+ </ul>
+
+ <p>
+ <strong>Note:</strong>
+ If you ever need to increase this value for non-attack payload,
+ please <a href="https://github.com/libexpat/libexpat/issues">file a bug report</a>.
+ </p>
+
+ <p>
+ <strong>Note:</strong>
+ Activation thresholds below 4 MiB are known to break support for
+ <a href="https://en.wikipedia.org/wiki/Darwin_Information_Typing_Architecture">DITA</a> 1.3 payload
+ and are hence not recommended.
+ </p>
+</div>
+
<h3><a name="miscellaneous">Miscellaneous functions</a></h3>
<p>The functions in this section either obtain state information from
--
1.8.3.1

View File

@ -0,0 +1,26 @@
From 857fdc4c3bf47eb3fedcd15d3763f62727476df0 Mon Sep 17 00:00:00 2001
From: Sebastian Pipping <sebastian@pipping.org>
Date: Wed, 14 Apr 2021 17:30:22 +0200
Subject: [PATCH] lib: Add prefix "expat: " to EXPAT_ENTROPY_DEBUG=1 stderr
output
---
lib/xmlparse.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
index fce7447..641f005 100644
--- a/lib/xmlparse.c
+++ b/lib/xmlparse.c
@@ -889,7 +889,7 @@ static unsigned long
ENTROPY_DEBUG(const char *label, unsigned long entropy) {
const char *const EXPAT_ENTROPY_DEBUG = getenv("EXPAT_ENTROPY_DEBUG");
if (EXPAT_ENTROPY_DEBUG && ! strcmp(EXPAT_ENTROPY_DEBUG, "1")) {
- fprintf(stderr, "Entropy: %s --> 0x%0*lx (%lu bytes)\n", label,
+ fprintf(stderr, "expat: Entropy: %s --> 0x%0*lx (%lu bytes)\n", label,
(int)sizeof(entropy) * 2, entropy, (unsigned long)sizeof(entropy));
}
return entropy;
--
1.8.3.1

View File

@ -0,0 +1,36 @@
From fcd0e14c3e35a56eb7ec42142a12e984fbe1b3c0 Mon Sep 17 00:00:00 2001
From: Sebastian Pipping <sebastian@pipping.org>
Date: Tue, 20 Apr 2021 14:01:27 +0200
Subject: [PATCH] lib: Address Cppcheck 2.4.1 warning "uninitvar"
---
lib/xmlparse.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
index 97e7980..4628def 100644
--- a/lib/xmlparse.c
+++ b/lib/xmlparse.c
@@ -5581,7 +5581,8 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
#endif
for (;;) {
- const char *next;
+ const char *next
+ = ptr; /* XmlAttributeValueTok doesn't always set the last arg */
int tok = XmlAttributeValueTok(enc, ptr, end, &next);
#ifdef XML_DTD
if (! accountingDiffTolerated(parser, tok, ptr, next, __LINE__, account)) {
@@ -5792,7 +5793,8 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc,
}
for (;;) {
- const char *next;
+ const char *next
+ = entityTextPtr; /* XmlEntityValueTok doesn't always set the last arg */
int tok = XmlEntityValueTok(enc, entityTextPtr, entityTextEnd, &next);
#ifdef XML_DTD
--
1.8.3.1

View File

@ -0,0 +1,63 @@
From 8af7d22ff029796484c78a16d4c6d44a47cb5729 Mon Sep 17 00:00:00 2001
From: Sebastian Pipping <sebastian@pipping.org>
Date: Mon, 19 Apr 2021 21:44:38 +0200
Subject: [PATCH] lib: Allow test suite to access raw accounting values
---
lib/internal.h | 7 +++++++
lib/xmlparse.c | 14 ++++++++++++++
2 files changed, 21 insertions(+)
diff --git a/lib/internal.h b/lib/internal.h
index 40c5033..ce6d27a 100644
--- a/lib/internal.h
+++ b/lib/internal.h
@@ -139,6 +139,8 @@
8388608 // 8 MiB, 2^23
/* NOTE END */
+#include "expat.h" // so we can use type XML_Parser below
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -146,6 +148,11 @@ extern "C" {
_INTERNAL_trim_to_complete_utf8_characters(const char *from,
const char **fromLimRef);
+#if defined(XML_DTD)
+unsigned long long testingAccountingGetCountBytesDirect(XML_Parser parser);
+unsigned long long testingAccountingGetCountBytesIndirect(XML_Parser parser);
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
index 4628def..fce7447 100644
--- a/lib/xmlparse.c
+++ b/lib/xmlparse.c
@@ -7357,6 +7357,20 @@ accountingDiffTolerated(XML_Parser originParser, int tok, const char *before,
return tolerated;
}
+unsigned long long
+testingAccountingGetCountBytesDirect(XML_Parser parser) {
+ if (! parser)
+ return 0;
+ return parser->m_accounting.countBytesDirect;
+}
+
+unsigned long long
+testingAccountingGetCountBytesIndirect(XML_Parser parser) {
+ if (! parser)
+ return 0;
+ return parser->m_accounting.countBytesIndirect;
+}
+
static void
entityTrackingReportStats(XML_Parser rootParser, ENTITY *entity,
const char *action, int sourceLine) {
--
1.8.3.1

View File

@ -0,0 +1,119 @@
From 60959f2b491876199879d97c8ed956eabb0c2e73 Mon Sep 17 00:00:00 2001
From: Sebastian Pipping <sebastian@pipping.org>
Date: Fri, 14 May 2021 20:09:22 +0200
Subject: [PATCH] lib: Fix accounting of CDATA sections inside of general
entities
---
Changes | 9 +++++----
lib/xmlparse.c | 20 ++++++++++++--------
2 files changed, 17 insertions(+), 12 deletions(-)
diff --git a/Changes b/Changes
index a435999..e62814b 100644
--- a/Changes
+++ b/Changes
@@ -4,7 +4,7 @@ NOTE: We are looking for help with a few things:
Release 2.2.9 Wed Septemper 25 2019
Security fixes:
- #34 #466 CVE-2013-0340/CWE-776 -- Protect against billion laughs attacks
+ #34 #466 #484 CVE-2013-0340/CWE-776 -- Protect against billion laughs attacks
(denial-of-service; flavors targeting CPU time or RAM or both,
leveraging general entities or parameter entities or both)
by tracking and limiting the input amplification factor
@@ -20,18 +20,18 @@ Release 2.2.9 Wed Septemper 25 2019
-DXML_UNICODE that was introduced with Expat 2.0.1
New features:
- #34 #466 Add two new API functions to further tighten billion laughs
+ #34 #466 #484 Add two new API functions to further tighten billion laughs
protection parameters when desired.
- XML_SetBillionLaughsAttackProtectionMaximumAmplification
- XML_SetBillionLaughsAttackProtectionActivationThreshold
Please see file "doc/reference.html" for more details.
If you ever need to increase the defaults for non-attack XML
payload, please file a bug report with libexpat.
- #34 #466 Introduce environment switches EXPAT_ACCOUNTING_DEBUG=(0|1|2|3)
+ #34 #466 #484 Introduce environment switches EXPAT_ACCOUNTING_DEBUG=(0|1|2|3)
and EXPAT_ENTITY_DEBUG=(0|1) for runtime debugging of accounting
and entity processing; specific behavior of these values may
change in the future.
- #34 #466 xmlwf: Add arguments "-a FACTOR" and "-b BYTES" to further tighten
+ #34 #466 #484 xmlwf: Add arguments "-a FACTOR" and "-b BYTES" to further tighten
billion laughs protection parameters when desired.
If you ever need to increase the defaults for non-attack XML
payload, please file a bug report with libexpat.
@@ -54,6 +54,7 @@ Release 2.2.9 Wed Septemper 25 2019
and
Clang LeakSan
JetBrains
+ OSS-Fuzz
Release 2.2.8 Fri Septemper 13 2019
Security fixes:
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
index 3870dfa..64d5dc3 100644
--- a/lib/xmlparse.c
+++ b/lib/xmlparse.c
@@ -448,7 +448,8 @@ static enum XML_Error doContent(XML_Parser parser, int startTagLevel,
XML_Bool haveMore, enum XML_Account account);
static enum XML_Error doCdataSection(XML_Parser parser, const ENCODING *,
const char **startPtr, const char *end,
- const char **nextPtr, XML_Bool haveMore);
+ const char **nextPtr, XML_Bool haveMore,
+ enum XML_Account account);
#ifdef XML_DTD
static enum XML_Error doIgnoreSection(XML_Parser parser, const ENCODING *,
const char **startPtr, const char *end,
@@ -3062,7 +3063,8 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
/* END disabled code */
else if (parser->m_defaultHandler)
reportDefault(parser, enc, s, next);
- result = doCdataSection(parser, enc, &next, end, nextPtr, haveMore);
+ result
+ = doCdataSection(parser, enc, &next, end, nextPtr, haveMore, account);
if (result != XML_ERROR_NONE)
return result;
else if (! next) {
@@ -3691,9 +3693,9 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId,
static enum XML_Error PTRCALL
cdataSectionProcessor(XML_Parser parser, const char *start, const char *end,
const char **endPtr) {
- enum XML_Error result
- = doCdataSection(parser, parser->m_encoding, &start, end, endPtr,
- (XML_Bool)! parser->m_parsingStatus.finalBuffer);
+ enum XML_Error result = doCdataSection(
+ parser, parser->m_encoding, &start, end, endPtr,
+ (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_ACCOUNT_DIRECT);
if (result != XML_ERROR_NONE)
return result;
if (start) {
@@ -3713,7 +3715,8 @@ cdataSectionProcessor(XML_Parser parser, const char *start, const char *end,
*/
static enum XML_Error
doCdataSection(XML_Parser parser, const ENCODING *enc, const char **startPtr,
- const char *end, const char **nextPtr, XML_Bool haveMore) {
+ const char *end, const char **nextPtr, XML_Bool haveMore,
+ enum XML_Account account) {
const char *s = *startPtr;
const char **eventPP;
const char **eventEndPP;
@@ -3732,11 +3735,12 @@ doCdataSection(XML_Parser parser, const ENCODING *enc, const char **startPtr,
const char *next;
int tok = XmlCdataSectionTok(enc, s, end, &next);
#ifdef XML_DTD
- if (! accountingDiffTolerated(parser, tok, s, next, __LINE__,
- XML_ACCOUNT_DIRECT)) {
+ if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, account)) {
accountingOnAbort(parser);
return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
}
+#else
+ UNUSED_P(account);
#endif
*eventEndPP = next;
switch (tok) {
--
1.8.3.1

View File

@ -0,0 +1,27 @@
From 29c3748788ff5ba0e4b14b02dfa15080177a3c8c Mon Sep 17 00:00:00 2001
From: Sebastian Pipping <sebastian@pipping.org>
Date: Mon, 19 Apr 2021 20:39:42 +0200
Subject: [PATCH] lib: Make EXPAT_ENTROPY_DEBUG consistent with other
EXPAT_*_DEBUG variables
---
lib/xmlparse.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
index 641f005..adaab23 100644
--- a/lib/xmlparse.c
+++ b/lib/xmlparse.c
@@ -887,8 +887,7 @@ gather_time_entropy(void) {
static unsigned long
ENTROPY_DEBUG(const char *label, unsigned long entropy) {
- const char *const EXPAT_ENTROPY_DEBUG = getenv("EXPAT_ENTROPY_DEBUG");
- if (EXPAT_ENTROPY_DEBUG && ! strcmp(EXPAT_ENTROPY_DEBUG, "1")) {
+ if (getDebugLevel("EXPAT_ENTROPY_DEBUG", 0) >= 1u) {
fprintf(stderr, "expat: Entropy: %s --> 0x%0*lx (%lu bytes)\n", label,
(int)sizeof(entropy) * 2, entropy, (unsigned long)sizeof(entropy));
}
--
1.8.3.1

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,34 @@
From 77cfb8f4cd9679cef27ae9bc38e39ac51235af2d Mon Sep 17 00:00:00 2001
From: Sebastian Pipping <sebastian@pipping.org>
Date: Fri, 14 May 2021 20:26:26 +0200
Subject: [PATCH] tests: Cover accounting of CDATA sections inside of general
entities
---
tests/runtests.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/tests/runtests.c b/tests/runtests.c
index 0e2b49f..e394456 100644
--- a/tests/runtests.c
+++ b/tests/runtests.c
@@ -11318,6 +11318,16 @@ START_TEST(test_accounting_precision) {
/* CDATA */
{"<e><![CDATA[one two three]]></e>", NULL, NULL, 0, filled_later},
+ /* The following is the essence of this OSS-Fuzz finding:
+ https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=34302
+ https://oss-fuzz.com/testcase-detail/4860575394955264
+ */
+ {"<!DOCTYPE r [\n"
+ "<!ENTITY e \"111<![CDATA[2 <= 2]]>333\">\n"
+ "]>\n"
+ "<r>&e;</r>\n",
+ NULL, NULL, sizeof(XML_Char) * strlen("111<![CDATA[2 <= 2]]>333"),
+ filled_later},
/* Conditional sections */
{"<!DOCTYPE r [\n"
--
1.8.3.1

View File

@ -0,0 +1,374 @@
From 271efb6069c5c979545cfbe00b738184c5632b93 Mon Sep 17 00:00:00 2001
From: Sebastian Pipping <sebastian@pipping.org>
Date: Wed, 21 Apr 2021 00:11:04 +0200
Subject: [PATCH] tests: Cover accounting
---
lib/internal.h | 6 +-
tests/runtests.c | 300 ++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 304 insertions(+), 2 deletions(-)
diff --git a/lib/internal.h b/lib/internal.h
index ce6d27a..377c12b 100644
--- a/lib/internal.h
+++ b/lib/internal.h
@@ -109,10 +109,12 @@
#if defined(_WIN32) && ! defined(__USE_MINGW_ANSI_STDIO)
# define EXPAT_FMT_ULL(midpart) "%" midpart "I64u"
-# if defined(_WIN64) // Note: modifier "td" does not work for MinGW
+# if defined(_WIN64) // Note: modifiers "td" and "zu" do not work for MinGW
# define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "I64d"
+# define EXPAT_FMT_SIZE_T(midpart) "%" midpart "I64u"
# else
# define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "d"
+# define EXPAT_FMT_SIZE_T(midpart) "%" midpart "u"
# endif
#else
# define EXPAT_FMT_ULL(midpart) "%" midpart "llu"
@@ -120,8 +122,10 @@
# error Compiler did not define ULONG_MAX for us
# elif ULONG_MAX == 18446744073709551615u // 2^64-1
# define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "ld"
+# define EXPAT_FMT_SIZE_T(midpart) "%" midpart "lu"
# else
# define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "d"
+# define EXPAT_FMT_SIZE_T(midpart) "%" midpart "u"
# endif
#endif
diff --git a/tests/runtests.c b/tests/runtests.c
index 40fdfb4..5234f49 100644
--- a/tests/runtests.c
+++ b/tests/runtests.c
@@ -61,7 +61,7 @@
#include "expat.h"
#include "chardata.h"
#include "structdata.h"
-#include "internal.h" /* for UNUSED_P only */
+#include "internal.h"
#include "minicheck.h"
#include "memcheck.h"
#include "siphash.h"
@@ -11225,6 +11225,296 @@ START_TEST(test_nsalloc_prefixed_element) {
}
END_TEST
+#if defined(XML_DTD)
+typedef enum XML_Status (*XmlParseFunction)(XML_Parser, const char *, int, int);
+
+struct AccountingTestCase {
+ const char *primaryText;
+ const char *firstExternalText; /* often NULL */
+ const char *secondExternalText; /* often NULL */
+ const unsigned long long expectedCountBytesIndirectExtra;
+ XML_Bool singleBytesWanted;
+};
+
+static int
+accounting_external_entity_ref_handler(XML_Parser parser,
+ const XML_Char *context,
+ const XML_Char *base,
+ const XML_Char *systemId,
+ const XML_Char *publicId) {
+ UNUSED_P(context);
+ UNUSED_P(base);
+ UNUSED_P(publicId);
+
+ const struct AccountingTestCase *const testCase
+ = (const struct AccountingTestCase *)XML_GetUserData(parser);
+
+ const char *externalText = NULL;
+ if (xcstrcmp(systemId, XCS("first.ent")) == 0) {
+ externalText = testCase->firstExternalText;
+ } else if (xcstrcmp(systemId, XCS("second.ent")) == 0) {
+ externalText = testCase->secondExternalText;
+ } else {
+ assert(! "systemId is neither \"first.ent\" nor \"second.ent\"");
+ }
+ assert(externalText);
+
+ XML_Parser entParser = XML_ExternalEntityParserCreate(parser, context, 0);
+ assert(entParser);
+
+ const XmlParseFunction xmlParseFunction
+ = testCase->singleBytesWanted ? _XML_Parse_SINGLE_BYTES : XML_Parse;
+
+ const enum XML_Status status = xmlParseFunction(
+ entParser, externalText, (int)strlen(externalText), XML_TRUE);
+
+ XML_ParserFree(entParser);
+ return status;
+}
+
+START_TEST(test_accounting_precision) {
+ const XML_Bool filled_later = XML_TRUE; /* value is arbitrary */
+ struct AccountingTestCase cases[] = {
+ {"<e/>", NULL, NULL, 0, 0},
+ {"<e></e>", NULL, NULL, 0, 0},
+
+ /* Attributes */
+ {"<e k1=\"v2\" k2=\"v2\"/>", NULL, NULL, 0, filled_later},
+ {"<e k1=\"v2\" k2=\"v2\"></e>", NULL, NULL, 0, 0},
+ {"<p:e xmlns:p=\"https://domain.invalid/\" />", NULL, NULL, 0,
+ filled_later},
+ {"<e k=\"&amp;&apos;&gt;&lt;&quot;\" />", NULL, NULL,
+ sizeof(XML_Char) * 5 /* number of predefined entites */, filled_later},
+ {"<e1 xmlns='https://example.org/'>\n"
+ " <e2 xmlns=''/>\n"
+ "</e1>",
+ NULL, NULL, 0, filled_later},
+
+ /* Text */
+ {"<e>text</e>", NULL, NULL, 0, filled_later},
+ {"<e1><e2>text1<e3/>text2</e2></e1>", NULL, NULL, 0, filled_later},
+ {"<e>&amp;&apos;&gt;&lt;&quot;</e>", NULL, NULL,
+ sizeof(XML_Char) * 5 /* number of predefined entites */, filled_later},
+ {"<e>&#65;&#41;</e>", NULL, NULL, 0, filled_later},
+
+ /* Prolog */
+ {"<?xml version=\"1.0\"?><root/>", NULL, NULL, 0, filled_later},
+
+ /* Whitespace */
+ {" <e1> <e2> </e2> </e1> ", NULL, NULL, 0, filled_later},
+ {"<e1 ><e2 /></e1 >", NULL, NULL, 0, filled_later},
+ {"<e1><e2 k = \"v\"/><e3 k = 'v'/></e1>", NULL, NULL, 0, filled_later},
+
+ /* Comments */
+ {"<!-- Comment --><e><!-- Comment --></e>", NULL, NULL, 0, filled_later},
+
+ /* Processing instructions */
+ {"<?xml-stylesheet type=\"text/xsl\" href=\"https://domain.invalid/\" media=\"all\"?><e/>",
+ NULL, NULL, 0, filled_later},
+ {"<?pi0?><?pi1 ?><?pi2 ?><!DOCTYPE r SYSTEM 'first.ent'><r/>",
+ "<?pi3?><!ENTITY % e1 SYSTEM 'second.ent'><?pi4?>%e1;<?pi5?>", "<?pi6?>",
+ 0, filled_later},
+
+ /* CDATA */
+ {"<e><![CDATA[one two three]]></e>", NULL, NULL, 0, filled_later},
+
+ /* Conditional sections */
+ {"<!DOCTYPE r [\n"
+ "<!ENTITY % draft 'INCLUDE'>\n"
+ "<!ENTITY % final 'IGNORE'>\n"
+ "<!ENTITY % import SYSTEM \"first.ent\">\n"
+ "%import;\n"
+ "]>\n"
+ "<r/>\n",
+ "<![%draft;[<!--1-->]]>\n"
+ "<![%final;[<!--22-->]]>",
+ NULL, sizeof(XML_Char) * (strlen("INCLUDE") + strlen("IGNORE")),
+ filled_later},
+
+ /* General entities */
+ {"<!DOCTYPE root [\n"
+ "<!ENTITY nine \"123456789\">\n"
+ "]>\n"
+ "<root>&nine;</root>",
+ NULL, NULL, sizeof(XML_Char) * strlen("123456789"), filled_later},
+ {"<!DOCTYPE root [\n"
+ "<!ENTITY nine \"123456789\">\n"
+ "]>\n"
+ "<root k1=\"&nine;\"/>",
+ NULL, NULL, sizeof(XML_Char) * strlen("123456789"), filled_later},
+ {"<!DOCTYPE root [\n"
+ "<!ENTITY nine \"123456789\">\n"
+ "<!ENTITY nine2 \"&nine;&nine;\">\n"
+ "]>\n"
+ "<root>&nine2;&nine2;&nine2;</root>",
+ NULL, NULL,
+ sizeof(XML_Char) * 3 /* calls to &nine2; */ * 2 /* calls to &nine; */
+ * (strlen("&nine;") + strlen("123456789")),
+ filled_later},
+ {"<!DOCTYPE r [\n"
+ " <!ENTITY five SYSTEM 'first.ent'>\n"
+ "]>\n"
+ "<r>&five;</r>",
+ "12345", NULL, 0, filled_later},
+
+ /* Parameter entities */
+ {"<!DOCTYPE r [\n"
+ "<!ENTITY % comment \"<!---->\">\n"
+ "%comment;\n"
+ "]>\n"
+ "<r/>",
+ NULL, NULL, sizeof(XML_Char) * strlen("<!---->"), filled_later},
+ {"<!DOCTYPE r [\n"
+ "<!ENTITY % ninedef \"&#60;!ENTITY nine &#34;123456789&#34;&#62;\">\n"
+ "%ninedef;\n"
+ "]>\n"
+ "<r>&nine;</r>",
+ NULL, NULL,
+ sizeof(XML_Char)
+ * (strlen("<!ENTITY nine \"123456789\">") + strlen("123456789")),
+ filled_later},
+ {"<!DOCTYPE r [\n"
+ "<!ENTITY % comment \"<!--1-->\">\n"
+ "<!ENTITY % comment2 \"&#37;comment;<!--22-->&#37;comment;\">\n"
+ "%comment2;\n"
+ "]>\n"
+ "<r/>\n",
+ NULL, NULL,
+ sizeof(XML_Char)
+ * (strlen("%comment;<!--22-->%comment;") + 2 * strlen("<!--1-->")),
+ filled_later},
+ {"<!DOCTYPE r [\n"
+ " <!ENTITY % five \"12345\">\n"
+ " <!ENTITY % five2def \"&#60;!ENTITY five2 &#34;[&#37;five;][&#37;five;]]]]&#34;&#62;\">\n"
+ " %five2def;\n"
+ "]>\n"
+ "<r>&five2;</r>",
+ NULL, NULL, /* from "%five2def;": */
+ sizeof(XML_Char)
+ * (strlen("<!ENTITY five2 \"[%five;][%five;]]]]\">")
+ + 2 /* calls to "%five;" */ * strlen("12345")
+ + /* from "&five2;": */ strlen("[12345][12345]]]]")),
+ filled_later},
+ {"<!DOCTYPE r SYSTEM \"first.ent\">\n"
+ "<r/>",
+ "<!ENTITY % comment '<!--1-->'>\n"
+ "<!ENTITY % comment2 '<!--22-->%comment;<!--22-->%comment;<!--22-->'>\n"
+ "%comment2;",
+ NULL,
+ sizeof(XML_Char)
+ * (strlen("<!--22-->%comment;<!--22-->%comment;<!--22-->")
+ + 2 /* calls to "%comment;" */ * strlen("<!---->")),
+ filled_later},
+ {"<!DOCTYPE r SYSTEM 'first.ent'>\n"
+ "<r/>",
+ "<!ENTITY % e1 PUBLIC 'foo' 'second.ent'>\n"
+ "<!ENTITY % e2 '<!--22-->%e1;<!--22-->'>\n"
+ "%e2;\n",
+ "<!--1-->", sizeof(XML_Char) * strlen("<!--22--><!--1--><!--22-->"),
+ filled_later},
+ {
+ "<!DOCTYPE r SYSTEM 'first.ent'>\n"
+ "<r/>",
+ "<!ENTITY % e1 SYSTEM 'second.ent'>\n"
+ "<!ENTITY % e2 '%e1;'>",
+ "<?xml version='1.0' encoding='utf-8'?>\n"
+ "hello\n"
+ "xml" /* without trailing newline! */,
+ 0,
+ filled_later,
+ },
+ {
+ "<!DOCTYPE r SYSTEM 'first.ent'>\n"
+ "<r/>",
+ "<!ENTITY % e1 SYSTEM 'second.ent'>\n"
+ "<!ENTITY % e2 '%e1;'>",
+ "<?xml version='1.0' encoding='utf-8'?>\n"
+ "hello\n"
+ "xml\n" /* with trailing newline! */,
+ 0,
+ filled_later,
+ },
+ {"<!DOCTYPE doc SYSTEM 'first.ent'>\n"
+ "<doc></doc>\n",
+ "<!ELEMENT doc EMPTY>\n"
+ "<!ENTITY % e1 SYSTEM 'second.ent'>\n"
+ "<!ENTITY % e2 '%e1;'>\n"
+ "%e1;\n",
+ "\xEF\xBB\xBF<!ATTLIST doc a1 CDATA 'value'>" /* UTF-8 BOM */,
+ strlen("\xEF\xBB\xBF<!ATTLIST doc a1 CDATA 'value'>"), filled_later},
+ {"<!DOCTYPE r [\n"
+ " <!ENTITY five SYSTEM 'first.ent'>\n"
+ "]>\n"
+ "<r>&five;</r>",
+ "\xEF\xBB\xBF" /* UTF-8 BOM */, NULL, 0, filled_later},
+ };
+
+ const size_t countCases = sizeof(cases) / sizeof(cases[0]);
+ size_t u = 0;
+ for (; u < countCases; u++) {
+ size_t v = 0;
+ for (; v < 2; v++) {
+ const XML_Bool singleBytesWanted = (v == 0) ? XML_FALSE : XML_TRUE;
+ const unsigned long long expectedCountBytesDirect
+ = strlen(cases[u].primaryText);
+ const unsigned long long expectedCountBytesIndirect
+ = (cases[u].firstExternalText ? strlen(cases[u].firstExternalText)
+ : 0)
+ + (cases[u].secondExternalText ? strlen(cases[u].secondExternalText)
+ : 0)
+ + cases[u].expectedCountBytesIndirectExtra;
+
+ XML_Parser parser = XML_ParserCreate(NULL);
+ XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+ if (cases[u].firstExternalText) {
+ XML_SetExternalEntityRefHandler(parser,
+ accounting_external_entity_ref_handler);
+ XML_SetUserData(parser, (void *)&cases[u]);
+ cases[u].singleBytesWanted = singleBytesWanted;
+ }
+
+ const XmlParseFunction xmlParseFunction
+ = singleBytesWanted ? _XML_Parse_SINGLE_BYTES : XML_Parse;
+
+ enum XML_Status status
+ = xmlParseFunction(parser, cases[u].primaryText,
+ (int)strlen(cases[u].primaryText), XML_TRUE);
+ if (status != XML_STATUS_OK) {
+ _xml_failure(parser, __FILE__, __LINE__);
+ }
+
+ const unsigned long long actualCountBytesDirect
+ = testingAccountingGetCountBytesDirect(parser);
+ const unsigned long long actualCountBytesIndirect
+ = testingAccountingGetCountBytesIndirect(parser);
+
+ XML_ParserFree(parser);
+
+ if (actualCountBytesDirect != expectedCountBytesDirect) {
+ fprintf(
+ stderr,
+ "Document " EXPAT_FMT_SIZE_T("") " of " EXPAT_FMT_SIZE_T("") ", %s: Expected " EXPAT_FMT_ULL(
+ "") " count direct bytes, got " EXPAT_FMT_ULL("") " instead.\n",
+ u + 1, countCases, singleBytesWanted ? "single bytes" : "chunks",
+ expectedCountBytesDirect, actualCountBytesDirect);
+ fail("Count of direct bytes is off");
+ }
+
+ if (actualCountBytesIndirect != expectedCountBytesIndirect) {
+ fprintf(
+ stderr,
+ "Document " EXPAT_FMT_SIZE_T("") " of " EXPAT_FMT_SIZE_T("") ", %s: Expected " EXPAT_FMT_ULL(
+ "") " count indirect bytes, got " EXPAT_FMT_ULL("") " instead.\n",
+ u + 1, countCases, singleBytesWanted ? "single bytes" : "chunks",
+ expectedCountBytesIndirect, actualCountBytesIndirect);
+ fail("Count of indirect bytes is off");
+ }
+ }
+ }
+}
+END_TEST
+#endif // defined(XML_DTD)
+
static Suite *
make_suite(void) {
Suite *s = suite_create("basic");
@@ -11233,6 +11523,9 @@ make_suite(void) {
TCase *tc_misc = tcase_create("miscellaneous tests");
TCase *tc_alloc = tcase_create("allocation tests");
TCase *tc_nsalloc = tcase_create("namespace allocation tests");
+#if defined(XML_DTD)
+ TCase *tc_accounting = tcase_create("accounting tests");
+#endif
suite_add_tcase(s, tc_basic);
tcase_add_checked_fixture(tc_basic, basic_setup, basic_teardown);
@@ -11593,6 +11886,11 @@ make_suite(void) {
tcase_add_test(tc_nsalloc, test_nsalloc_long_systemid_in_ext);
tcase_add_test(tc_nsalloc, test_nsalloc_prefixed_element);
+#if defined(XML_DTD)
+ suite_add_tcase(s, tc_accounting);
+ tcase_add_test(tc_accounting, test_accounting_precision);
+#endif
+
return s;
}
--
1.8.3.1

View File

@ -0,0 +1,103 @@
From e9d8f115580c3a25a9579c213f096af623dd92ce Mon Sep 17 00:00:00 2001
From: Sebastian Pipping <sebastian@pipping.org>
Date: Mon, 26 Apr 2021 14:52:45 +0200
Subject: [PATCH] tests: Cover billion laughs attack protection API
---
tests/runtests.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 66 insertions(+)
diff --git a/tests/runtests.c b/tests/runtests.c
index 100574a..d4e05a7 100644
--- a/tests/runtests.c
+++ b/tests/runtests.c
@@ -45,6 +45,7 @@
#include <stddef.h> /* ptrdiff_t */
#include <ctype.h>
#include <limits.h>
+#include <math.h> /* NAN, INFINITY, isnan */
#if defined(_WIN32) && defined(_MSC_VER) && (_MSC_VER < 1600)
/* For vs2003/7.1 up to vs2008/9.0; _MSC_VER 1600 is vs2010/10.0 */
@@ -11485,6 +11486,70 @@ START_TEST(test_accounting_precision) {
}
}
END_TEST
+
+START_TEST(test_billion_laughs_attack_protection_api) {
+ XML_Parser parserWithoutParent = XML_ParserCreate(NULL);
+ XML_Parser parserWithParent
+ = XML_ExternalEntityParserCreate(parserWithoutParent, NULL, NULL);
+ if (parserWithoutParent == NULL)
+ fail("parserWithoutParent is NULL");
+ if (parserWithParent == NULL)
+ fail("parserWithParent is NULL");
+
+ // XML_SetBillionLaughsAttackProtectionMaximumAmplification, error cases
+ if (XML_SetBillionLaughsAttackProtectionMaximumAmplification(NULL, 123.0f)
+ == XML_TRUE)
+ fail("Call with NULL parser is NOT supposed to succeed");
+ if (XML_SetBillionLaughsAttackProtectionMaximumAmplification(parserWithParent,
+ 123.0f)
+ == XML_TRUE)
+ fail("Call with non-root parser is NOT supposed to succeed");
+ if (XML_SetBillionLaughsAttackProtectionMaximumAmplification(
+ parserWithoutParent, NAN)
+ == XML_TRUE)
+ fail("Call with NaN limit is NOT supposed to succeed");
+ if (XML_SetBillionLaughsAttackProtectionMaximumAmplification(
+ parserWithoutParent, -1.0f)
+ == XML_TRUE)
+ fail("Call with negative limit is NOT supposed to succeed");
+ if (XML_SetBillionLaughsAttackProtectionMaximumAmplification(
+ parserWithoutParent, 0.9f)
+ == XML_TRUE)
+ fail("Call with positive limit <1.0 is NOT supposed to succeed");
+
+ // XML_SetBillionLaughsAttackProtectionMaximumAmplification, success cases
+ if (XML_SetBillionLaughsAttackProtectionMaximumAmplification(
+ parserWithoutParent, 1.0f)
+ == XML_FALSE)
+ fail("Call with positive limit >=1.0 is supposed to succeed");
+ if (XML_SetBillionLaughsAttackProtectionMaximumAmplification(
+ parserWithoutParent, 123456.789f)
+ == XML_FALSE)
+ fail("Call with positive limit >=1.0 is supposed to succeed");
+ if (XML_SetBillionLaughsAttackProtectionMaximumAmplification(
+ parserWithoutParent, INFINITY)
+ == XML_FALSE)
+ fail("Call with positive limit >=1.0 is supposed to succeed");
+
+ // XML_SetBillionLaughsAttackProtectionActivationThreshold, error cases
+ if (XML_SetBillionLaughsAttackProtectionActivationThreshold(NULL, 123)
+ == XML_TRUE)
+ fail("Call with NULL parser is NOT supposed to succeed");
+ if (XML_SetBillionLaughsAttackProtectionActivationThreshold(parserWithParent,
+ 123)
+ == XML_TRUE)
+ fail("Call with non-root parser is NOT supposed to succeed");
+
+ // XML_SetBillionLaughsAttackProtectionActivationThreshold, success cases
+ if (XML_SetBillionLaughsAttackProtectionActivationThreshold(
+ parserWithoutParent, 123)
+ == XML_FALSE)
+ fail("Call with non-NULL parentless parser is supposed to succeed");
+
+ XML_ParserFree(parserWithParent);
+ XML_ParserFree(parserWithoutParent);
+}
+END_TEST
#endif // defined(XML_DTD)
static Suite *
@@ -11859,6 +11924,7 @@ make_suite(void) {
#if defined(XML_DTD)
suite_add_tcase(s, tc_accounting);
tcase_add_test(tc_accounting, test_accounting_precision);
+ tcase_add_test(tc_accounting, test_billion_laughs_attack_protection_api);
#endif
return s;
--
1.8.3.1

View File

@ -0,0 +1,85 @@
From 5dbc857f47ecd6c37748ce279c6535fbbf526590 Mon Sep 17 00:00:00 2001
From: Sebastian Pipping <sebastian@pipping.org>
Date: Mon, 26 Apr 2021 15:12:53 +0200
Subject: [PATCH] tests: Cover helper unsignedCharToPrintable
---
lib/internal.h | 1 +
lib/xmlparse.c | 3 +--
tests/runtests.c | 20 ++++++++++++++++++++
3 files changed, 22 insertions(+), 2 deletions(-)
diff --git a/lib/internal.h b/lib/internal.h
index 377c12b..444eba0 100644
--- a/lib/internal.h
+++ b/lib/internal.h
@@ -155,6 +155,7 @@ void _INTERNAL_trim_to_complete_utf8_characters(const char *from,
#if defined(XML_DTD)
unsigned long long testingAccountingGetCountBytesDirect(XML_Parser parser);
unsigned long long testingAccountingGetCountBytesIndirect(XML_Parser parser);
+const char *unsignedCharToPrintable(unsigned char c);
#endif
#ifdef __cplusplus
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
index adaab23..a1aadd8 100644
--- a/lib/xmlparse.c
+++ b/lib/xmlparse.c
@@ -580,7 +580,6 @@ static void entityTrackingOnClose(XML_Parser parser, ENTITY *entity,
static XML_Parser getRootParserOf(XML_Parser parser,
unsigned int *outLevelDiff);
-static const char *unsignedCharToPrintable(unsigned char c);
#endif /* XML_DTD */
static unsigned long getDebugLevel(const char *variableName,
@@ -7433,7 +7432,7 @@ getRootParserOf(XML_Parser parser, unsigned int *outLevelDiff) {
return rootParser;
}
-static const char *
+const char *
unsignedCharToPrintable(unsigned char c) {
switch (c) {
case 0:
diff --git a/tests/runtests.c b/tests/runtests.c
index 8c5ad72..0e2b49f 100644
--- a/tests/runtests.c
+++ b/tests/runtests.c
@@ -11578,6 +11578,25 @@ START_TEST(test_billion_laughs_attack_protection_api) {
XML_ParserFree(parserWithoutParent);
}
END_TEST
+
+START_TEST(test_helper_unsigned_char_to_printable) {
+ // Smoke test
+ unsigned char uc = 0;
+ for (; uc < (unsigned char)-1; uc++) {
+ const char *const printable = unsignedCharToPrintable(uc);
+ if (printable == NULL)
+ fail("unsignedCharToPrintable returned NULL");
+ if (strlen(printable) < (size_t)1)
+ fail("unsignedCharToPrintable returned empty string");
+ }
+
+ // Two concrete samples
+ if (strcmp(unsignedCharToPrintable('A'), "A") != 0)
+ fail("unsignedCharToPrintable result mistaken");
+ if (strcmp(unsignedCharToPrintable('\\'), "\\\\") != 0)
+ fail("unsignedCharToPrintable result mistaken");
+}
+END_TEST
#endif // defined(XML_DTD)
static Suite *
@@ -11955,6 +11974,7 @@ make_suite(void) {
suite_add_tcase(s, tc_accounting);
tcase_add_test(tc_accounting, test_accounting_precision);
tcase_add_test(tc_accounting, test_billion_laughs_attack_protection_api);
+ tcase_add_test(tc_accounting, test_helper_unsigned_char_to_printable);
#endif
return s;
--
1.8.3.1

View File

@ -0,0 +1,161 @@
From c6223b3b0f3d8e6a37b5775b44eeded02e9c3ea7 Mon Sep 17 00:00:00 2001
From: Sebastian Pipping <sebastian@pipping.org>
Date: Sat, 17 Apr 2021 17:26:17 +0200
Subject: [PATCH] xmlwf: Add support for custom attack protection parameters
---
xmlwf/xmltchar.h | 5 +++-
xmlwf/xmlwf.c | 69 ++++++++++++++++++++++++++++++++++++++++++++
xmlwf/xmlwf_helpgen.py | 8 +++++
3 files changed, 82 insertions(+), 1 deletion(-)
diff --git a/xmlwf/xmltchar.h b/xmlwf/xmltchar.h
index 4843fbe..30283d0 100644
--- a/xmlwf/xmltchar.h
+++ b/xmlwf/xmltchar.h
@@ -55,6 +55,8 @@
# define tmain wmain
# define tremove _wremove
# define tchar wchar_t
+# define tcstof wcstof
+# define tcstoull wcstoull
#else /* not XML_UNICODE */
# define T(x) x
# define ftprintf fprintf
@@ -72,4 +74,6 @@
# define tmain main
# define tremove remove
# define tchar char
+# define tcstof strtof
+# define tcstoull strtoull
#endif /* not XML_UNICODE */
diff --git a/xmlwf/xmlwf.c b/xmlwf/xmlwf.c
index 2b86966..342d6c5 100644
--- a/xmlwf/xmlwf.c
+++ b/xmlwf/xmlwf.c
@@ -46,6 +46,8 @@
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
+#include <math.h> /* for isnan */
+#include <errno.h>
#include "expat.h"
#include "codepage.h"
@@ -905,6 +907,12 @@ usage(const XML_Char *prog, int rc) {
T(" -t write no XML output for [t]iming of plain parsing\n")
T(" -N enable adding doctype and [n]otation declarations\n")
T("\n")
+ T("billion laughs attack protection:\n")
+ T(" NOTE: If you ever need to increase these values for non-attack payload, please file a bug report.\n")
+ T("\n")
+ T(" -a FACTOR set maximum tolerated [a]mplification factor (default: 100.0)\n")
+ T(" -b BYTES set number of output [b]ytes needed to activate (default: 8 MiB)\n")
+ T("\n")
T("info arguments:\n")
T(" -h show this [h]elp message and exit\n")
T(" -v show program's [v]ersion number and exit\n")
@@ -953,6 +961,11 @@ tmain(int argc, XML_Char **argv) {
int useNamespaces = 0;
int requireStandalone = 0;
int requiresNotations = 0;
+
+ float attackMaximumAmplification = -1.0f; /* signaling "not set" */
+ unsigned long long attackThresholdBytes;
+ XML_Bool attackThresholdGiven = XML_FALSE;
+
enum XML_ParamEntityParsing paramEntityParsing
= XML_PARAM_ENTITY_PARSING_NEVER;
int useStdin = 0;
@@ -1032,6 +1045,49 @@ tmain(int argc, XML_Char **argv) {
case T('v'):
showVersion(argv[0]);
return 0;
+ case T('a'): {
+ const XML_Char *valueText = NULL;
+ XMLWF_SHIFT_ARG_INTO(valueText, argc, argv, i, j);
+
+ errno = 0;
+ XML_Char *afterValueText = (XML_Char *)valueText;
+ attackMaximumAmplification = tcstof(valueText, &afterValueText);
+ if ((errno != 0) || (afterValueText[0] != T('\0'))
+ || isnan(attackMaximumAmplification)
+ || (attackMaximumAmplification < 1.0f)) {
+ // This prevents tperror(..) from reporting misleading "[..]: Success"
+ errno = ERANGE;
+ tperror(T("invalid amplification limit") T(
+ " (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"));
+#endif
+ break;
+ }
+ case T('b'): {
+ const XML_Char *valueText = NULL;
+ XMLWF_SHIFT_ARG_INTO(valueText, argc, argv, i, j);
+
+ errno = 0;
+ XML_Char *afterValueText = (XML_Char *)valueText;
+ attackThresholdBytes = tcstoull(valueText, &afterValueText, 10);
+ if ((errno != 0) || (afterValueText[0] != T('\0'))) {
+ // This prevents tperror(..) from reporting misleading "[..]: Success"
+ errno = ERANGE;
+ tperror(T("invalid ignore threshold")
+ T(" (needs an integer from 0 to 2^64-1)"));
+ 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"));
+#endif
+ break;
+ }
case T('\0'):
if (j > 1) {
i++;
@@ -1062,6 +1118,19 @@ tmain(int argc, XML_Char **argv) {
exit(1);
}
+ if (attackMaximumAmplification != -1.0f) {
+#ifdef XML_DTD
+ XML_SetBillionLaughsAttackProtectionMaximumAmplification(
+ parser, attackMaximumAmplification);
+#endif
+ }
+ if (attackThresholdGiven) {
+#ifdef XML_DTD
+ XML_SetBillionLaughsAttackProtectionActivationThreshold(
+ parser, attackThresholdBytes);
+#endif
+ }
+
if (requireStandalone)
XML_SetNotStandaloneHandler(parser, notStandalone);
XML_SetParamEntityParsing(parser, paramEntityParsing);
diff --git a/xmlwf/xmlwf_helpgen.py b/xmlwf/xmlwf_helpgen.py
index 8ec8d4e..c2a527f 100755
--- a/xmlwf/xmlwf_helpgen.py
+++ b/xmlwf/xmlwf_helpgen.py
@@ -73,6 +73,14 @@ output_mode.add_argument('-m', action='store_true', help='write [m]eta XML, not
output_mode.add_argument('-t', action='store_true', help='write no XML output for [t]iming of plain parsing')
output_related.add_argument('-N', action='store_true', help='enable adding doctype and [n]otation declarations')
+billion_laughs = parser.add_argument_group('billion laughs attack protection',
+ description='NOTE: '
+ 'If you ever need to increase these values '
+ 'for non-attack payload, please file a bug report.')
+billion_laughs.add_argument('-a', metavar='FACTOR',
+ help='set maximum tolerated [a]mplification factor (default: 100.0)')
+billion_laughs.add_argument('-b', metavar='BYTES', help='set number of output [b]ytes needed to activate (default: 8 MiB)')
+
parser.add_argument('files', metavar='FILE', nargs='*', help='files to process (default: STDIN)')
info = parser.add_argument_group('info arguments')
--
1.8.3.1

View File

@ -0,0 +1,26 @@
From 65cddaa4e93263fb88261b60f97cf29f1589d038 Mon Sep 17 00:00:00 2001
From: Sebastian Pipping <sebastian@pipping.org>
Date: Sun, 18 Apr 2021 20:17:43 +0200
Subject: [PATCH] xmlwf: Include expat_config.h so we can check for macro
XML_DTD
---
xmlwf/xmlwf.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/xmlwf/xmlwf.c b/xmlwf/xmlwf.c
index 4242e1c..2b86966 100644
--- a/xmlwf/xmlwf.c
+++ b/xmlwf/xmlwf.c
@@ -39,6 +39,8 @@
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
+#include <expat_config.h>
+
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
--
1.8.3.1

View File

@ -0,0 +1,84 @@
From bf878495985b81731c620bbac26df79e6c98c9fd Mon Sep 17 00:00:00 2001
From: Sebastian Pipping <sebastian@pipping.org>
Date: Sun, 25 Apr 2021 18:16:14 +0200
Subject: [PATCH] xmlwf.1: Document arguments -a and -b
---
doc/xmlwf.xml | 47 ++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 46 insertions(+), 1 deletion(-)
diff --git a/doc/xmlwf.xml b/doc/xmlwf.xml
index 5e2a4ae..648b581 100644
--- a/doc/xmlwf.xml
+++ b/doc/xmlwf.xml
@@ -3,7 +3,7 @@
<!ENTITY dhfirstname "<firstname>Scott</firstname>">
<!ENTITY dhsurname "<surname>Bronson</surname>">
<!-- Please adjust the date whenever revising the manpage. -->
- <!ENTITY dhdate "<date>March 11, 2016</date>">
+ <!ENTITY dhdate "<date>May 4, 2021</date>">
<!-- SECTION should be 1-8, maybe w/ subsection other parameters are
allowed: see man(7), man(1). -->
<!ENTITY dhsection "<manvolnum>1</manvolnum>">
@@ -140,6 +140,50 @@ supports both.
<variablelist>
<varlistentry>
+ <term><option>-a</option> <replaceable>factor</replaceable></term>
+ <listitem>
+ <para>
+ Sets the maximum tolerated amplification factor
+ for protection against billion laughs attacks (default: 100.0).
+ The amplification factor is calculated as ..
+ </para>
+ <literallayout>
+ amplification := (direct + indirect) / direct
+ </literallayout>
+ <para>
+ .. while parsing, whereas
+ &lt;direct&gt; is the number of bytes read
+ from the primary document in parsing and
+ &lt;indirect&gt; is the number of bytes
+ added by expanding entities and reading of external DTD files,
+ combined.
+ </para>
+ <para>
+ <emphasis>NOTE</emphasis>:
+ If you ever need to increase this value for non-attack payload,
+ please file a bug report.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-b</option> <replaceable>bytes</replaceable></term>
+ <listitem>
+ <para>
+ Sets the number of output bytes (including amplification)
+ needed to activate protection against billion laughs attacks
+ (default: 8 MiB).
+ This can be thought of as an &quot;activation threshold&quot;.
+ </para>
+ <para>
+ <emphasis>NOTE</emphasis>:
+ If you ever need to increase this value for non-attack payload,
+ please file a bug report.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><option>-c</option></term>
<listitem>
<para>
@@ -434,6 +478,7 @@ http://www.xml.com/pub/a/tools/ruwf/check.html
<literallayout>
The Expat home page: http://www.libexpat.org/
The W3 XML specification: http://www.w3.org/TR/REC-xml
+Billion laughs attack: https://en.wikipedia.org/wiki/Billion_laughs_attack
</literallayout>
</para>
--
1.8.3.1

View File

@ -0,0 +1,56 @@
From 13bb381d296ef8da09a3f00f5b23724b5deb6a38 Mon Sep 17 00:00:00 2001w
From: Sebastian Pipping <sebastian@pipping.org>
Date: Wed, 27 May 2020 20:28:06 +0200
Subject: [PATCH] xmlparse.c: Fix reading uninitialized variable (#404)
---
Changes | 3 +++
lib/xmlparse.c | 4 ++--
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/Changes b/Changes
index a642bf6..72c1f13 100644
--- a/Changes
+++ b/Changes
@@ -18,6 +18,7 @@ Release 2.2.9 Wed Septemper 25 2019
Bug fixes:
#390 #395 Fix undefined behavior during parsing when compiled with
-DXML_UNICODE that was introduced with Expat 2.0.1
+ #404 Fix reading uninitialized variable during parsing
New features:
#34 #466 #484 Add two new API functions to further tighten billion laughs
@@ -55,6 +56,8 @@ Release 2.2.9 Wed Septemper 25 2019
Clang LeakSan
JetBrains
OSS-Fuzz
+ and
+ Cppcheck 2.0 and the Cppcheck team
Release 2.2.8 Fri Septemper 13 2019
Security fixes:
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
index f2ad416..03da172 100644
--- a/lib/xmlparse.c
+++ b/lib/xmlparse.c
@@ -3732,7 +3732,7 @@ doCdataSection(XML_Parser parser, const ENCODING *enc, const char **startPtr,
*startPtr = NULL;
for (;;) {
- const char *next;
+ 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 (! accountingDiffTolerated(parser, tok, s, next, __LINE__, account)) {
@@ -3858,7 +3858,7 @@ ignoreSectionProcessor(XML_Parser parser, const char *start, const char *end,
static enum XML_Error
doIgnoreSection(XML_Parser parser, const ENCODING *enc, const char **startPtr,
const char *end, const char **nextPtr, XML_Bool haveMore) {
- const char *next;
+ const char *next = *startPtr; /* in case of XML_TOK_NONE or XML_TOK_PARTIAL */
int tok;
const char *s = *startPtr;
const char **eventPP;
--
1.8.3.1

View File

@ -0,0 +1,63 @@
From 385aeb477bac9538ab2b3d5cf0e76e8c751374be Mon Sep 17 00:00:00 2001
From: Sebastian Pipping <sebastian@pipping.org>
Date: Sat, 17 Apr 2021 18:35:51 +0200
Subject: [PATCH] xmlwf: Extract macro XMLWF_SHIFT_ARGUMENT
---
xmlwf/xmlwf.c | 31 +++++++++++++++----------------
1 file changed, 15 insertions(+), 16 deletions(-)
diff --git a/xmlwf/xmlwf.c b/xmlwf/xmlwf.c
index 493b697..fc83f73 100644
--- a/xmlwf/xmlwf.c
+++ b/xmlwf/xmlwf.c
@@ -908,6 +908,19 @@ usage(const XML_Char *prog, int rc) {
int wmain(int argc, XML_Char **argv);
#endif
+#define XMLWF_SHIFT_ARG_INTO(constCharStarTarget, argc, argv, i, j) \
+ { \
+ if (argv[i][j + 1] == T('\0')) { \
+ if (++i == argc) \
+ usage(argv[0], 2); \
+ constCharStarTarget = argv[i]; \
+ } else { \
+ constCharStarTarget = argv[i] + j + 1; \
+ } \
+ i++; \
+ j = 0; \
+ }
+
int
tmain(int argc, XML_Char **argv) {
int i, j;
@@ -984,24 +997,10 @@ tmain(int argc, XML_Char **argv) {
j++;
break;
case T('d'):
- if (argv[i][j + 1] == T('\0')) {
- if (++i == argc)
- usage(argv[0], 2);
- outputDir = argv[i];
- } else
- outputDir = argv[i] + j + 1;
- i++;
- j = 0;
+ XMLWF_SHIFT_ARG_INTO(outputDir, argc, argv, i, j);
break;
case T('e'):
- if (argv[i][j + 1] == T('\0')) {
- if (++i == argc)
- usage(argv[0], 2);
- encoding = argv[i];
- } else
- encoding = argv[i] + j + 1;
- i++;
- j = 0;
+ XMLWF_SHIFT_ARG_INTO(encoding, argc, argv, i, j);
break;
case T('h'):
usage(argv[0], 0);
--
1.8.3.1

View File

@ -1,14 +1,34 @@
%define Rversion %(echo %{version} | sed -e 's/\\./_/g' -e 's/^/R_/')
Name: expat
Version: 2.2.9
Release: 2
Release: 3
Summary: An XML parser library
License: MIT
URL: https://libexpat.github.io/
Source0: https://github.com/libexpat/libexpat/releases/download/%{Rversion}/expat-%{version}.tar.gz
Patch0000: xmlparse.c-Fix-undefined-behavior-for-XML_UNICODE.patch
Patch0001: Don-t-add-to-NULL-in-iterator.patch
Patch0: xmlparse.c-Fix-undefined-behavior-for-XML_UNICODE.patch
Patch1: Don-t-add-to-NULL-in-iterator.patch
Patch2: backport-Autotools-Give-test-suite-access-to-internal-symbols.patch
Patch3: backport-xmlwf-Extract-macro-XMLWF_SHIFT_ARGUMENT.patch
Patch4: backport-CVE-2013-0340-lib-Add-prefix-expat-to-EXPAT_ENTROPY_DEBUG-1-stderr.patch
Patch5: backport-CVE-2013-0340-xmlwf-Add-support-for-custom-attack-protection-param.patch
Patch6: backport-CVE-2013-0340-xmlwf-Include-expat_config.h-so-we-can-check-for-mac.patch
Patch7: backport-CVE-2013-0340-Changes-Document-protection-against-billion-laughs-a.patch
Patch8: backport-CVE-2013-0340-lib-Protect-against-billion-laughs-attacks-approach-.patch
Patch9: backport-CVE-2013-0340-lib-Make-EXPAT_ENTROPY_DEBUG-consistent-with-other-E.patch
Patch10: backport-CVE-2013-0340-lib-Allow-test-suite-to-access-raw-accounting-values.patch
Patch11: backport-CVE-2013-0340-Autotools-CMake-Suppress-Wpedantic-ms-format-false-p.patch
Patch12: backport-CVE-2013-0340-lib-Address-Cppcheck-2.4.1-warning-uninitvar.patch
Patch13: backport-CVE-2013-0340-tests-Cover-accounting.patch
Patch14: backport-CVE-2013-0340-xmlwf.1-Document-arguments-a-and-b.patch
Patch15: backport-CVE-2013-0340-doc-reference.html-Document-billion-laughs-attack-pr.patch
Patch16: backport-CVE-2013-0340-tests-Cover-billion-laughs-attack-protection-API.patch
Patch17: backport-CVE-2013-0340-tests-Cover-helper-unsignedCharToPrintable.patch
Patch18: backport-CVE-2013-0340-tests-Cover-accounting-of-CDATA-sections.patch
Patch19: backport-CVE-2013-0340-lib-Fix-accounting-of-CDATA-sections-inside.patch
Patch20: backport-xmlparse.c-Fix-reading-uninitialized-variable-404.patch
BuildRequires: sed,autoconf,automake,gcc-c++,libtool,xmlto
@ -61,6 +81,10 @@ make check
%{_mandir}/man1/*
%changelog
* Fri Jul 2 2021 panxiaohe <panxiaohe@huawei.com> - 2.2.9-3
- fix CVE-2013-0340
- xmlparse.c: Fix reading uninitialized variable
* Sun Jun 28 2020 liuchenguang <liuchenguang4@huawei.com> - 2.2.9-2
- quality enhancement synchronization github patch