diff --git a/backport-Autotools-Give-test-suite-access-to-internal-symbols.patch b/backport-Autotools-Give-test-suite-access-to-internal-symbols.patch
new file mode 100644
index 0000000000000000000000000000000000000000..8b08f7de39343f85ffeadeebac4a0c82bd5bd9ea
--- /dev/null
+++ b/backport-Autotools-Give-test-suite-access-to-internal-symbols.patch
@@ -0,0 +1,58 @@
+From f01a61402cd44bb0cb59db43e70309c01acc50d1 Mon Sep 17 00:00:00 2001
+From: Sebastian Pipping
+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
+
diff --git a/backport-CVE-2013-0340-Autotools-CMake-Suppress-Wpedantic-ms-format-false-p.patch b/backport-CVE-2013-0340-Autotools-CMake-Suppress-Wpedantic-ms-format-false-p.patch
new file mode 100644
index 0000000000000000000000000000000000000000..8b22fb1bc593dfa58bc23364a0692653332c4296
--- /dev/null
+++ b/backport-CVE-2013-0340-Autotools-CMake-Suppress-Wpedantic-ms-format-false-p.patch
@@ -0,0 +1,50 @@
+From 1e053c698b7ff8f7b4cc3c7c6e9945b72c3d5286 Mon Sep 17 00:00:00 2001
+From: Sebastian Pipping
+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
+
diff --git a/backport-CVE-2013-0340-Changes-Document-protection-against-billion-laughs-a.patch b/backport-CVE-2013-0340-Changes-Document-protection-against-billion-laughs-a.patch
new file mode 100644
index 0000000000000000000000000000000000000000..0de92a612e668e9bb817f4d3c57b8eb9c1859897
--- /dev/null
+++ b/backport-CVE-2013-0340-Changes-Document-protection-against-billion-laughs-a.patch
@@ -0,0 +1,68 @@
+From 3f2f8786623cc3e89a1f4384715b3ad178c5ee2c Mon Sep 17 00:00:00 2001
+From: Sebastian Pipping
+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
++ ( := ( + ) / ).
++ By conservative default, amplification up to a factor of 100.0
++ is tolerated and rejection only starts after 8 MiB of output bytes
++ (= + ) 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
+
diff --git a/backport-CVE-2013-0340-doc-reference.html-Document-billion-laughs-attack-pr.patch b/backport-CVE-2013-0340-doc-reference.html-Document-billion-laughs-attack-pr.patch
new file mode 100644
index 0000000000000000000000000000000000000000..009e92b7853fe2f07ddc4ed94a42bf2126a9b2b5
--- /dev/null
+++ b/backport-CVE-2013-0340-doc-reference.html-Document-billion-laughs-attack-pr.patch
@@ -0,0 +1,130 @@
+From 899c00e613800ef973a93ce8f83b3514992f1afa Mon Sep 17 00:00:00 2001
+From: Sebastian Pipping
+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.
+ Miscellaneous Functions
+
+ XML_SetUserData
+@@ -2074,6 +2081,98 @@ parse position may be before the beginning of the buffer.
+ return NULL.
+
+
++
++
++The functions in this section configure the built-in
++ protection against various forms of
++ billion laughs attacks .
++
++XML_SetBillionLaughsAttackProtectionMaximumAmplification
++
++/* Added in Expat 2.4.0. */
++XML_Bool XMLCALL
++XML_SetBillionLaughsAttackProtectionMaximumAmplification(XML_Parser p,
++ float maximumAmplificationFactor);
++
++
++
++ Sets the maximum tolerated amplification factor
++ for protection against
++ billion laughs attacks
++ (default: 100.0)
++ of parser p to maximumAmplificationFactor, and
++ returns XML_TRUE upon success and XML_TRUE upon error.
++
++
++ The amplification factor is calculated as ..
++
++ amplification := (direct + indirect) / direct
++
++ .. while parsing, whereas
++
direct is the number of bytes read from the primary document in parsing and
++
indirect is the number of bytes added by expanding entities and reading of external DTD files, combined.
++
++
For a call to XML_SetBillionLaughsAttackProtectionMaximumAmplification to succeed:
++
++ parser p must be a non-NULL root parser (without any parent parsers) and
++ maximumAmplificationFactor must be non-NaN and greater than or equal to 1.0.
++
++
++
++ Note:
++ If you ever need to increase this value for non-attack payload,
++ please file a bug report .
++
++
++
++ Note:
++ 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).
++
++
++
++XML_SetBillionLaughsAttackProtectionActivationThreshold
++
++/* Added in Expat 2.4.0. */
++XML_Bool XMLCALL
++XML_SetBillionLaughsAttackProtectionActivationThreshold(XML_Parser p,
++ unsigned long long activationThresholdBytes);
++
++
++
++ Sets number of output bytes (including amplification from entity expansion and reading DTD files)
++ needed to activate protection against
++ billion laughs attacks
++ (default: 8 MiB)
++ of parser p to activationThresholdBytes, and
++ returns XML_TRUE upon success and XML_TRUE upon error.
++
++
++
For a call to XML_SetBillionLaughsAttackProtectionActivationThreshold to succeed:
++
++ parser p must be a non-NULL root parser (without any parent parsers).
++
++
++
++ Note:
++ If you ever need to increase this value for non-attack payload,
++ please file a bug report .
++
++
++
++ Note:
++ Activation thresholds below 4 MiB are known to break support for
++ DITA 1.3 payload
++ and are hence not recommended.
++
++
++
+
+
+ The functions in this section either obtain state information from
+--
+1.8.3.1
+
diff --git a/backport-CVE-2013-0340-lib-Add-prefix-expat-to-EXPAT_ENTROPY_DEBUG-1-stderr.patch b/backport-CVE-2013-0340-lib-Add-prefix-expat-to-EXPAT_ENTROPY_DEBUG-1-stderr.patch
new file mode 100644
index 0000000000000000000000000000000000000000..22172eed603214fef7eda433fb6a9865722162cd
--- /dev/null
+++ b/backport-CVE-2013-0340-lib-Add-prefix-expat-to-EXPAT_ENTROPY_DEBUG-1-stderr.patch
@@ -0,0 +1,26 @@
+From 857fdc4c3bf47eb3fedcd15d3763f62727476df0 Mon Sep 17 00:00:00 2001
+From: Sebastian Pipping
+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
+
diff --git a/backport-CVE-2013-0340-lib-Address-Cppcheck-2.4.1-warning-uninitvar.patch b/backport-CVE-2013-0340-lib-Address-Cppcheck-2.4.1-warning-uninitvar.patch
new file mode 100644
index 0000000000000000000000000000000000000000..87b90045c2ef66b9f3ba306d3c7bf0819a38881b
--- /dev/null
+++ b/backport-CVE-2013-0340-lib-Address-Cppcheck-2.4.1-warning-uninitvar.patch
@@ -0,0 +1,36 @@
+From fcd0e14c3e35a56eb7ec42142a12e984fbe1b3c0 Mon Sep 17 00:00:00 2001
+From: Sebastian Pipping
+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
+
diff --git a/backport-CVE-2013-0340-lib-Allow-test-suite-to-access-raw-accounting-values.patch b/backport-CVE-2013-0340-lib-Allow-test-suite-to-access-raw-accounting-values.patch
new file mode 100644
index 0000000000000000000000000000000000000000..8021394371dcf2d6e657918cb43cabab2df6aab2
--- /dev/null
+++ b/backport-CVE-2013-0340-lib-Allow-test-suite-to-access-raw-accounting-values.patch
@@ -0,0 +1,63 @@
+From 8af7d22ff029796484c78a16d4c6d44a47cb5729 Mon Sep 17 00:00:00 2001
+From: Sebastian Pipping
+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
+
diff --git a/backport-CVE-2013-0340-lib-Fix-accounting-of-CDATA-sections-inside.patch b/backport-CVE-2013-0340-lib-Fix-accounting-of-CDATA-sections-inside.patch
new file mode 100644
index 0000000000000000000000000000000000000000..14553917502fcb645b7f0ee026a9af4af1837ede
--- /dev/null
+++ b/backport-CVE-2013-0340-lib-Fix-accounting-of-CDATA-sections-inside.patch
@@ -0,0 +1,119 @@
+From 60959f2b491876199879d97c8ed956eabb0c2e73 Mon Sep 17 00:00:00 2001
+From: Sebastian Pipping
+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
+
diff --git a/backport-CVE-2013-0340-lib-Make-EXPAT_ENTROPY_DEBUG-consistent-with-other-E.patch b/backport-CVE-2013-0340-lib-Make-EXPAT_ENTROPY_DEBUG-consistent-with-other-E.patch
new file mode 100644
index 0000000000000000000000000000000000000000..b2a7f60c7bfddc7d5b1996d5d073d6efe8265f7c
--- /dev/null
+++ b/backport-CVE-2013-0340-lib-Make-EXPAT_ENTROPY_DEBUG-consistent-with-other-E.patch
@@ -0,0 +1,27 @@
+From 29c3748788ff5ba0e4b14b02dfa15080177a3c8c Mon Sep 17 00:00:00 2001
+From: Sebastian Pipping
+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
+
diff --git a/backport-CVE-2013-0340-lib-Protect-against-billion-laughs-attacks-approach-.patch b/backport-CVE-2013-0340-lib-Protect-against-billion-laughs-attacks-approach-.patch
new file mode 100644
index 0000000000000000000000000000000000000000..13e359ee1f1e0e7ea23bd602f71f4e71349d4d4b
--- /dev/null
+++ b/backport-CVE-2013-0340-lib-Protect-against-billion-laughs-attacks-approach-.patch
@@ -0,0 +1,1705 @@
+From b1d039607d3d8a042bf0466bfcc1c0f104e353c8 Mon Sep 17 00:00:00 2001
+From: Sebastian Pipping
+Date: Mon, 19 Apr 2021 21:42:51 +0200
+Subject: [PATCH] lib: Protect against billion laughs attacks (approach 3.0.21)
+
+---
+ lib/expat.h | 21 +-
+ lib/internal.h | 30 ++
+ lib/libexpat.def | 3 +
+ lib/libexpatw.def | 3 +
+ lib/xmlparse.c | 1126 ++++++++++++++++++++++++++++++++++++++++++++++++++---
+ 5 files changed, 1130 insertions(+), 53 deletions(-)
+
+diff --git a/lib/expat.h b/lib/expat.h
+index 48a6e2a..0fb70d9 100644
+--- a/lib/expat.h
++++ b/lib/expat.h
+@@ -115,7 +115,9 @@ enum XML_Error {
+ XML_ERROR_RESERVED_PREFIX_XMLNS,
+ XML_ERROR_RESERVED_NAMESPACE_URI,
+ /* Added in 2.2.1. */
+- XML_ERROR_INVALID_ARGUMENT
++ XML_ERROR_INVALID_ARGUMENT,
++ /* Added in 2.2.9. */
++ XML_ERROR_AMPLIFICATION_LIMIT_BREACH
+ };
+
+ enum XML_Content_Type {
+@@ -997,7 +999,10 @@ enum XML_FeatureEnum {
+ XML_FEATURE_SIZEOF_XML_LCHAR,
+ XML_FEATURE_NS,
+ XML_FEATURE_LARGE_SIZE,
+- XML_FEATURE_ATTR_INFO
++ XML_FEATURE_ATTR_INFO,
++ /* Added in Expat 2.2.9. */
++ XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT,
++ XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT
+ /* Additional features must be added to the end of this enum. */
+ };
+
+@@ -1010,6 +1015,18 @@ typedef struct {
+ XMLPARSEAPI(const XML_Feature *)
+ XML_GetFeatureList(void);
+
++#ifdef XML_DTD
++/* Added in Expat 2.2.9. */
++XMLPARSEAPI(XML_Bool)
++XML_SetBillionLaughsAttackProtectionMaximumAmplification(
++ XML_Parser parser, float maximumAmplificationFactor);
++
++/* Added in Expat 2.2.9. */
++XMLPARSEAPI(XML_Bool)
++XML_SetBillionLaughsAttackProtectionActivationThreshold(
++ XML_Parser parser, unsigned long long activationThresholdBytes);
++#endif
++
+ /* Expat follows the semantic versioning convention.
+ See http://semver.org.
+ */
+diff --git a/lib/internal.h b/lib/internal.h
+index 60913da..d8b31fa 100644
+--- a/lib/internal.h
++++ b/lib/internal.h
+@@ -101,10 +101,40 @@
+ # endif
+ #endif
+
++#include // ULONG_MAX
++
++#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
++# define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "I64d"
++# else
++# define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "d"
++# endif
++#else
++# define EXPAT_FMT_ULL(midpart) "%" midpart "llu"
++# if ! defined(ULONG_MAX)
++# error Compiler did not define ULONG_MAX for us
++# elif ULONG_MAX == 18446744073709551615u // 2^64-1
++# define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "ld"
++# else
++# define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "d"
++# endif
++#endif
++
+ #ifndef UNUSED_P
+ # define UNUSED_P(p) (void)p
+ #endif
+
++/* NOTE BEGIN If you ever patch these defaults to greater values
++ for non-attack XML payload in your environment,
++ please file a bug report with libexpat. Thank you!
++*/
++#define EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT \
++ 100.0f
++#define EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT \
++ 8388608 // 8 MiB, 2^23
++/* NOTE END */
++
+ #ifdef __cplusplus
+ extern "C" {
+ #endif
+diff --git a/lib/libexpat.def b/lib/libexpat.def
+index 16faf59..5aefa6d 100644
+--- a/lib/libexpat.def
++++ b/lib/libexpat.def
+@@ -76,3 +76,6 @@ EXPORTS
+ XML_SetHashSalt @67
+ ; added with version 2.2.5
+ _INTERNAL_trim_to_complete_utf8_characters @68
++; added with version 2.2.9
++ XML_SetBillionLaughsAttackProtectionActivationThreshold @69
++ XML_SetBillionLaughsAttackProtectionMaximumAmplification @70
+diff --git a/lib/libexpatw.def b/lib/libexpatw.def
+index 16faf59..5aefa6d 100644
+--- a/lib/libexpatw.def
++++ b/lib/libexpatw.def
+@@ -76,3 +76,6 @@ EXPORTS
+ XML_SetHashSalt @67
+ ; added with version 2.2.5
+ _INTERNAL_trim_to_complete_utf8_characters @68
++; added with version 2.2.9
++ XML_SetBillionLaughsAttackProtectionActivationThreshold @69
++ XML_SetBillionLaughsAttackProtectionMaximumAmplification @70
+diff --git a/lib/xmlparse.c b/lib/xmlparse.c
+index 005e675..98ac5c3 100644
+--- a/lib/xmlparse.c
++++ b/lib/xmlparse.c
+@@ -47,6 +47,7 @@
+ #include /* UINT_MAX */
+ #include /* fprintf */
+ #include /* getenv, rand_s */
++#include /* isnan */
+
+ #if defined(_WIN32) && defined(_MSC_VER) && (_MSC_VER < 1600)
+ /* vs2008/9.0 and earlier lack stdint.h; _MSC_VER 1600 is vs2010/10.0 */
+@@ -382,6 +383,31 @@ typedef struct open_internal_entity {
+ XML_Bool betweenDecl; /* WFC: PE Between Declarations */
+ } OPEN_INTERNAL_ENTITY;
+
++enum XML_Account {
++ XML_ACCOUNT_DIRECT, /* bytes directly passed to the Expat parser */
++ XML_ACCOUNT_ENTITY_EXPANSION, /* intermediate bytes produced during entity
++ expansion */
++ XML_ACCOUNT_NONE /* i.e. do not account, was accounted already */
++};
++
++#ifdef XML_DTD
++typedef unsigned long long XmlBigCount;
++typedef struct accounting {
++ XmlBigCount countBytesDirect;
++ XmlBigCount countBytesIndirect;
++ int debugLevel;
++ float maximumAmplificationFactor; // >=1.0
++ unsigned long long activationThresholdBytes;
++} ACCOUNTING;
++
++typedef struct entity_stats {
++ unsigned int countEverOpened;
++ unsigned int currentDepth;
++ unsigned int maximumDepthSeen;
++ int debugLevel;
++} ENTITY_STATS;
++#endif /* XML_DTD */
++
+ typedef enum XML_Error PTRCALL Processor(XML_Parser parser, const char *start,
+ const char *end, const char **endPtr);
+
+@@ -412,13 +438,14 @@ static enum XML_Error initializeEncoding(XML_Parser parser);
+ static enum XML_Error doProlog(XML_Parser parser, const ENCODING *enc,
+ const char *s, const char *end, int tok,
+ const char *next, const char **nextPtr,
+- XML_Bool haveMore, XML_Bool allowClosingDoctype);
++ XML_Bool haveMore, XML_Bool allowClosingDoctype,
++ enum XML_Account account);
+ static enum XML_Error processInternalEntity(XML_Parser parser, ENTITY *entity,
+ XML_Bool betweenDecl);
+ static enum XML_Error doContent(XML_Parser parser, int startTagLevel,
+ const ENCODING *enc, const char *start,
+ const char *end, const char **endPtr,
+- XML_Bool haveMore);
++ 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);
+@@ -431,7 +458,8 @@ static enum XML_Error doIgnoreSection(XML_Parser parser, const ENCODING *,
+ static void freeBindings(XML_Parser parser, BINDING *bindings);
+ static enum XML_Error storeAtts(XML_Parser parser, const ENCODING *,
+ const char *s, TAG_NAME *tagNamePtr,
+- BINDING **bindingsPtr);
++ BINDING **bindingsPtr,
++ enum XML_Account account);
+ static enum XML_Error addBinding(XML_Parser parser, PREFIX *prefix,
+ const ATTRIBUTE_ID *attId, const XML_Char *uri,
+ BINDING **bindingsPtr);
+@@ -440,15 +468,18 @@ static int defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *, XML_Bool isCdata,
+ XML_Parser parser);
+ static enum XML_Error storeAttributeValue(XML_Parser parser, const ENCODING *,
+ XML_Bool isCdata, const char *,
+- const char *, STRING_POOL *);
++ const char *, STRING_POOL *,
++ enum XML_Account account);
+ static enum XML_Error appendAttributeValue(XML_Parser parser, const ENCODING *,
+ XML_Bool isCdata, const char *,
+- const char *, STRING_POOL *);
++ const char *, STRING_POOL *,
++ enum XML_Account account);
+ static ATTRIBUTE_ID *getAttributeId(XML_Parser parser, const ENCODING *enc,
+ const char *start, const char *end);
+ static int setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *);
+ static enum XML_Error storeEntityValue(XML_Parser parser, const ENCODING *enc,
+- const char *start, const char *end);
++ const char *start, const char *end,
++ enum XML_Account account);
+ static int reportProcessingInstruction(XML_Parser parser, const ENCODING *enc,
+ const char *start, const char *end);
+ static int reportComment(XML_Parser parser, const ENCODING *enc,
+@@ -512,6 +543,35 @@ static XML_Parser parserCreate(const XML_Char *encodingName,
+
+ static void parserInit(XML_Parser parser, const XML_Char *encodingName);
+
++#ifdef XML_DTD
++static float accountingGetCurrentAmplification(XML_Parser rootParser);
++static void accountingReportStats(XML_Parser originParser, const char *epilog);
++static void accountingOnAbort(XML_Parser originParser);
++static void accountingReportDiff(XML_Parser rootParser,
++ unsigned int levelsAwayFromRootParser,
++ const char *before, const char *after,
++ ptrdiff_t bytesMore, int source_line,
++ enum XML_Account account);
++static XML_Bool accountingDiffTolerated(XML_Parser originParser, int tok,
++ const char *before, const char *after,
++ int source_line,
++ enum XML_Account account);
++
++static void entityTrackingReportStats(XML_Parser parser, ENTITY *entity,
++ const char *action, int sourceLine);
++static void entityTrackingOnOpen(XML_Parser parser, ENTITY *entity,
++ int sourceLine);
++static void entityTrackingOnClose(XML_Parser parser, ENTITY *entity,
++ int sourceLine);
++
++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,
++ unsigned long defaultDebugLevel);
++
+ #define poolStart(pool) ((pool)->start)
+ #define poolEnd(pool) ((pool)->ptr)
+ #define poolLength(pool) ((pool)->ptr - (pool)->start)
+@@ -625,6 +685,10 @@ struct XML_ParserStruct {
+ enum XML_ParamEntityParsing m_paramEntityParsing;
+ #endif
+ unsigned long m_hash_secret_salt;
++#ifdef XML_DTD
++ ACCOUNTING m_accounting;
++ ENTITY_STATS m_entity_stats;
++#endif
+ };
+
+ #define MALLOC(parser, s) (parser->m_mem.malloc_fcn((s)))
+@@ -1064,6 +1128,18 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) {
+ parser->m_paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER;
+ #endif
+ parser->m_hash_secret_salt = 0;
++
++#ifdef XML_DTD
++ memset(&parser->m_accounting, 0, sizeof(ACCOUNTING));
++ parser->m_accounting.debugLevel = getDebugLevel("EXPAT_ACCOUNTING_DEBUG", 0u);
++ parser->m_accounting.maximumAmplificationFactor
++ = EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT;
++ parser->m_accounting.activationThresholdBytes
++ = EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT;
++
++ memset(&parser->m_entity_stats, 0, sizeof(ENTITY_STATS));
++ parser->m_entity_stats.debugLevel = getDebugLevel("EXPAT_ENTITY_DEBUG", 0u);
++#endif
+ }
+
+ /* moves list of bindings to m_freeBindingList */
+@@ -2327,6 +2403,10 @@ XML_ErrorString(enum XML_Error code) {
+ /* Added in 2.2.5. */
+ case XML_ERROR_INVALID_ARGUMENT: /* Constant added in 2.2.1, already */
+ return XML_L("invalid argument");
++ /* Added in 2.2.9. */
++ case XML_ERROR_AMPLIFICATION_LIMIT_BREACH:
++ return XML_L(
++ "limit on input amplification factor (from DTD and entities) breached");
+ }
+ return NULL;
+ }
+@@ -2363,41 +2443,75 @@ XML_ExpatVersionInfo(void) {
+
+ const XML_Feature *XMLCALL
+ XML_GetFeatureList(void) {
+- static const XML_Feature features[]
+- = {{XML_FEATURE_SIZEOF_XML_CHAR, XML_L("sizeof(XML_Char)"),
+- sizeof(XML_Char)},
+- {XML_FEATURE_SIZEOF_XML_LCHAR, XML_L("sizeof(XML_LChar)"),
+- sizeof(XML_LChar)},
++ static const XML_Feature features[] = {
++ {XML_FEATURE_SIZEOF_XML_CHAR, XML_L("sizeof(XML_Char)"),
++ sizeof(XML_Char)},
++ {XML_FEATURE_SIZEOF_XML_LCHAR, XML_L("sizeof(XML_LChar)"),
++ sizeof(XML_LChar)},
+ #ifdef XML_UNICODE
+- {XML_FEATURE_UNICODE, XML_L("XML_UNICODE"), 0},
++ {XML_FEATURE_UNICODE, XML_L("XML_UNICODE"), 0},
+ #endif
+ #ifdef XML_UNICODE_WCHAR_T
+- {XML_FEATURE_UNICODE_WCHAR_T, XML_L("XML_UNICODE_WCHAR_T"), 0},
++ {XML_FEATURE_UNICODE_WCHAR_T, XML_L("XML_UNICODE_WCHAR_T"), 0},
+ #endif
+ #ifdef XML_DTD
+- {XML_FEATURE_DTD, XML_L("XML_DTD"), 0},
++ {XML_FEATURE_DTD, XML_L("XML_DTD"), 0},
+ #endif
+ #ifdef XML_CONTEXT_BYTES
+- {XML_FEATURE_CONTEXT_BYTES, XML_L("XML_CONTEXT_BYTES"),
+- XML_CONTEXT_BYTES},
++ {XML_FEATURE_CONTEXT_BYTES, XML_L("XML_CONTEXT_BYTES"),
++ XML_CONTEXT_BYTES},
+ #endif
+ #ifdef XML_MIN_SIZE
+- {XML_FEATURE_MIN_SIZE, XML_L("XML_MIN_SIZE"), 0},
++ {XML_FEATURE_MIN_SIZE, XML_L("XML_MIN_SIZE"), 0},
+ #endif
+ #ifdef XML_NS
+- {XML_FEATURE_NS, XML_L("XML_NS"), 0},
++ {XML_FEATURE_NS, XML_L("XML_NS"), 0},
+ #endif
+ #ifdef XML_LARGE_SIZE
+- {XML_FEATURE_LARGE_SIZE, XML_L("XML_LARGE_SIZE"), 0},
++ {XML_FEATURE_LARGE_SIZE, XML_L("XML_LARGE_SIZE"), 0},
+ #endif
+ #ifdef XML_ATTR_INFO
+- {XML_FEATURE_ATTR_INFO, XML_L("XML_ATTR_INFO"), 0},
++ {XML_FEATURE_ATTR_INFO, XML_L("XML_ATTR_INFO"), 0},
+ #endif
+- {XML_FEATURE_END, NULL, 0}};
++#ifdef XML_DTD
++ /* Added in Expat 2.2.9. */
++ {XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT,
++ XML_L("XML_BLAP_MAX_AMP"),
++ (long int)
++ EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT},
++ {XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT,
++ XML_L("XML_BLAP_ACT_THRES"),
++ EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT},
++#endif
++ {XML_FEATURE_END, NULL, 0}};
+
+ return features;
+ }
+
++#ifdef XML_DTD
++XML_Bool XMLCALL
++XML_SetBillionLaughsAttackProtectionMaximumAmplification(
++ XML_Parser parser, float maximumAmplificationFactor) {
++ if ((parser == NULL) || (parser->m_parentParser != NULL)
++ || isnan(maximumAmplificationFactor)
++ || (maximumAmplificationFactor < 1.0f)) {
++ return XML_FALSE;
++ }
++ parser->m_accounting.maximumAmplificationFactor = maximumAmplificationFactor;
++ return XML_TRUE;
++}
++
++XML_Bool XMLCALL
++XML_SetBillionLaughsAttackProtectionActivationThreshold(
++ XML_Parser parser, unsigned long long activationThresholdBytes) {
++ if ((parser == NULL) || (parser->m_parentParser != NULL)) {
++ return XML_FALSE;
++ }
++ parser->m_accounting.activationThresholdBytes = activationThresholdBytes;
++ return XML_TRUE;
++}
++#endif /* XML_DTD */
++
+ /* 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
+@@ -2450,9 +2564,9 @@ storeRawNames(XML_Parser parser) {
+ static enum XML_Error PTRCALL
+ contentProcessor(XML_Parser parser, const char *start, const char *end,
+ const char **endPtr) {
+- enum XML_Error result
+- = doContent(parser, 0, parser->m_encoding, start, end, endPtr,
+- (XML_Bool)! parser->m_parsingStatus.finalBuffer);
++ enum XML_Error result = doContent(
++ parser, 0, parser->m_encoding, start, end, endPtr,
++ (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_ACCOUNT_DIRECT);
+ if (result == XML_ERROR_NONE) {
+ if (! storeRawNames(parser))
+ return XML_ERROR_NO_MEMORY;
+@@ -2477,6 +2591,14 @@ 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 (! accountingDiffTolerated(parser, tok, start, next, __LINE__,
++ XML_ACCOUNT_DIRECT)) {
++ accountingOnAbort(parser);
++ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
++ }
++#endif /* XML_DTD */
++
+ /* If we are at the end of the buffer, this would cause the next stage,
+ i.e. externalEntityInitProcessor3, to pass control directly to
+ doContent (by detecting XML_TOK_NONE) without processing any xml text
+@@ -2514,6 +2636,10 @@ externalEntityInitProcessor3(XML_Parser parser, const char *start,
+ const char *next = start; /* XmlContentTok doesn't always set the last arg */
+ parser->m_eventPtr = start;
+ tok = XmlContentTok(parser->m_encoding, start, end, &next);
++ /* Note: These bytes are accounted later in:
++ - processXmlDecl
++ - externalEntityContentProcessor
++ */
+ parser->m_eventEndPtr = next;
+
+ switch (tok) {
+@@ -2555,7 +2681,8 @@ externalEntityContentProcessor(XML_Parser parser, const char *start,
+ const char *end, const char **endPtr) {
+ enum XML_Error result
+ = doContent(parser, 1, parser->m_encoding, start, end, endPtr,
+- (XML_Bool)! parser->m_parsingStatus.finalBuffer);
++ (XML_Bool)! parser->m_parsingStatus.finalBuffer,
++ XML_ACCOUNT_ENTITY_EXPANSION);
+ if (result == XML_ERROR_NONE) {
+ if (! storeRawNames(parser))
+ return XML_ERROR_NO_MEMORY;
+@@ -2566,7 +2693,7 @@ externalEntityContentProcessor(XML_Parser parser, const char *start,
+ static enum XML_Error
+ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
+ const char *s, const char *end, const char **nextPtr,
+- XML_Bool haveMore) {
++ XML_Bool haveMore, enum XML_Account account) {
+ /* save one level of indirection */
+ DTD *const dtd = parser->m_dtd;
+
+@@ -2584,6 +2711,17 @@ 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
++ const char *accountAfter
++ = ((tok == XML_TOK_TRAILING_RSQB) || (tok == XML_TOK_TRAILING_CR))
++ ? (haveMore ? s /* i.e. 0 bytes */ : end)
++ : next;
++ if (! accountingDiffTolerated(parser, tok, s, accountAfter, __LINE__,
++ account)) {
++ accountingOnAbort(parser);
++ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
++ }
++#endif
+ *eventEndPP = next;
+ switch (tok) {
+ case XML_TOK_TRAILING_CR:
+@@ -2639,6 +2777,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
++ /* 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 */
+ if (parser->m_characterDataHandler)
+ parser->m_characterDataHandler(parser->m_handlerArg, &ch, 1);
+ else if (parser->m_defaultHandler)
+@@ -2757,7 +2903,8 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
+ }
+ tag->name.str = (XML_Char *)tag->buf;
+ *toPtr = XML_T('\0');
+- result = storeAtts(parser, enc, s, &(tag->name), &(tag->bindings));
++ result
++ = storeAtts(parser, enc, s, &(tag->name), &(tag->bindings), account);
+ if (result)
+ return result;
+ if (parser->m_startElementHandler)
+@@ -2781,7 +2928,8 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
+ if (! name.str)
+ return XML_ERROR_NO_MEMORY;
+ poolFinish(&parser->m_tempPool);
+- result = storeAtts(parser, enc, s, &name, &bindings);
++ result = storeAtts(parser, enc, s, &name, &bindings,
++ XML_ACCOUNT_NONE /* token spans whole start tag */);
+ if (result != XML_ERROR_NONE) {
+ freeBindings(parser, bindings);
+ return result;
+@@ -3045,7 +3193,8 @@ freeBindings(XML_Parser parser, BINDING *bindings) {
+ */
+ static enum XML_Error
+ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
+- TAG_NAME *tagNamePtr, BINDING **bindingsPtr) {
++ TAG_NAME *tagNamePtr, BINDING **bindingsPtr,
++ enum XML_Account account) {
+ DTD *const dtd = parser->m_dtd; /* save one level of indirection */
+ ELEMENT_TYPE *elementType;
+ int nDefaultAtts;
+@@ -3155,7 +3304,7 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
+ /* normalize the attribute value */
+ result = storeAttributeValue(
+ parser, enc, isCdata, parser->m_atts[i].valuePtr,
+- parser->m_atts[i].valueEnd, &parser->m_tempPool);
++ parser->m_atts[i].valueEnd, &parser->m_tempPool, account);
+ if (result)
+ return result;
+ appAtts[attIndex] = poolStart(&parser->m_tempPool);
+@@ -3584,6 +3733,13 @@ doCdataSection(XML_Parser parser, const ENCODING *enc, const char **startPtr,
+ for (;;) {
+ const char *next;
+ int tok = XmlCdataSectionTok(enc, s, end, &next);
++#ifdef XML_DTD
++ if (! accountingDiffTolerated(parser, tok, s, next, __LINE__,
++ XML_ACCOUNT_DIRECT)) {
++ accountingOnAbort(parser);
++ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
++ }
++#endif
+ *eventEndPP = next;
+ switch (tok) {
+ case XML_TOK_CDATA_SECT_CLOSE:
+@@ -3728,6 +3884,13 @@ doIgnoreSection(XML_Parser parser, const ENCODING *enc, const char **startPtr,
+ *eventPP = s;
+ *startPtr = NULL;
+ tok = XmlIgnoreSectionTok(enc, s, end, &next);
++# ifdef XML_DTD
++ if (! accountingDiffTolerated(parser, tok, s, next, __LINE__,
++ XML_ACCOUNT_DIRECT)) {
++ accountingOnAbort(parser);
++ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
++ }
++# endif
+ *eventEndPP = next;
+ switch (tok) {
+ case XML_TOK_IGNORE_SECT:
+@@ -3812,6 +3975,15 @@ processXmlDecl(XML_Parser parser, int isGeneralTextEntity, const char *s,
+ const char *versionend;
+ const XML_Char *storedversion = NULL;
+ int standalone = -1;
++
++#ifdef XML_DTD
++ if (! accountingDiffTolerated(parser, XML_TOK_XML_DECL, s, next, __LINE__,
++ XML_ACCOUNT_DIRECT)) {
++ accountingOnAbort(parser);
++ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
++ }
++#endif
++
+ if (! (parser->m_ns ? XmlParseXmlDeclNS : XmlParseXmlDecl)(
+ isGeneralTextEntity, parser->m_encoding, s, next, &parser->m_eventPtr,
+ &version, &versionend, &encodingName, &newEncoding, &standalone)) {
+@@ -3961,6 +4133,10 @@ entityValueInitProcessor(XML_Parser parser, const char *s, const char *end,
+
+ for (;;) {
+ tok = XmlPrologTok(parser->m_encoding, start, end, &next);
++ /* Note: Except for XML_TOK_BOM below, these bytes are accounted later in:
++ - storeEntityValue
++ - processXmlDecl
++ */
+ parser->m_eventEndPtr = next;
+ if (tok <= 0) {
+ if (! parser->m_parsingStatus.finalBuffer && tok != XML_TOK_INVALID) {
+@@ -3979,7 +4155,8 @@ entityValueInitProcessor(XML_Parser parser, const char *s, const char *end,
+ break;
+ }
+ /* found end of entity value - can store it now */
+- return storeEntityValue(parser, parser->m_encoding, s, end);
++ return storeEntityValue(parser, parser->m_encoding, s, end,
++ XML_ACCOUNT_DIRECT);
+ } else if (tok == XML_TOK_XML_DECL) {
+ enum XML_Error result;
+ result = processXmlDecl(parser, 0, start, next);
+@@ -4006,6 +4183,14 @@ 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 (! accountingDiffTolerated(parser, tok, s, next, __LINE__,
++ XML_ACCOUNT_DIRECT)) {
++ accountingOnAbort(parser);
++ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
++ }
++# endif
++
+ *nextPtr = next;
+ return XML_ERROR_NONE;
+ }
+@@ -4048,16 +4233,24 @@ externalParEntProcessor(XML_Parser parser, const char *s, const char *end,
+ }
+ /* This would cause the next stage, i.e. doProlog to be passed XML_TOK_BOM.
+ However, when parsing an external subset, doProlog will not accept a BOM
+- as valid, and report a syntax error, so we have to skip the BOM
++ as valid, and report a syntax error, so we have to skip the BOM, and
++ account for the BOM bytes.
+ */
+ else if (tok == XML_TOK_BOM) {
++ if (! accountingDiffTolerated(parser, tok, s, next, __LINE__,
++ XML_ACCOUNT_DIRECT)) {
++ accountingOnAbort(parser);
++ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
++ }
++
+ s = next;
+ tok = XmlPrologTok(parser->m_encoding, s, end, &next);
+ }
+
+ parser->m_processor = prologProcessor;
+ return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr,
+- (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE);
++ (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE,
++ XML_ACCOUNT_DIRECT);
+ }
+
+ static enum XML_Error PTRCALL
+@@ -4070,6 +4263,9 @@ entityValueProcessor(XML_Parser parser, const char *s, const char *end,
+
+ for (;;) {
+ tok = XmlPrologTok(enc, start, end, &next);
++ /* Note: These bytes are accounted later in:
++ - storeEntityValue
++ */
+ if (tok <= 0) {
+ if (! parser->m_parsingStatus.finalBuffer && tok != XML_TOK_INVALID) {
+ *nextPtr = s;
+@@ -4087,7 +4283,7 @@ entityValueProcessor(XML_Parser parser, const char *s, const char *end,
+ break;
+ }
+ /* found end of entity value - can store it now */
+- return storeEntityValue(parser, enc, s, end);
++ return storeEntityValue(parser, enc, s, end, XML_ACCOUNT_DIRECT);
+ }
+ start = next;
+ }
+@@ -4101,13 +4297,14 @@ prologProcessor(XML_Parser parser, const char *s, const char *end,
+ const char *next = s;
+ int tok = XmlPrologTok(parser->m_encoding, s, end, &next);
+ return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr,
+- (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE);
++ (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE,
++ XML_ACCOUNT_DIRECT);
+ }
+
+ static enum XML_Error
+ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
+ int tok, const char *next, const char **nextPtr, XML_Bool haveMore,
+- XML_Bool allowClosingDoctype) {
++ XML_Bool allowClosingDoctype, enum XML_Account account) {
+ #ifdef XML_DTD
+ static const XML_Char externalSubsetName[] = {ASCII_HASH, '\0'};
+ #endif /* XML_DTD */
+@@ -4134,6 +4331,10 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
+ static const XML_Char enumValueSep[] = {ASCII_PIPE, '\0'};
+ static const XML_Char enumValueStart[] = {ASCII_LPAREN, '\0'};
+
++#ifndef XML_DTD
++ UNUSED_P(account);
++#endif
++
+ /* save one level of indirection */
+ DTD *const dtd = parser->m_dtd;
+
+@@ -4198,6 +4399,19 @@ 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
++ 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
++ break;
++ default:
++ if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, account)) {
++ accountingOnAbort(parser);
++ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
++ }
++ }
++#endif
+ switch (role) {
+ case XML_ROLE_XML_DECL: {
+ enum XML_Error result = processXmlDecl(parser, 0, s, next);
+@@ -4473,7 +4687,8 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
+ const XML_Char *attVal;
+ enum XML_Error result = storeAttributeValue(
+ parser, enc, parser->m_declAttributeIsCdata,
+- s + enc->minBytesPerChar, next - enc->minBytesPerChar, &dtd->pool);
++ s + enc->minBytesPerChar, next - enc->minBytesPerChar, &dtd->pool,
++ XML_ACCOUNT_NONE);
+ if (result)
+ return result;
+ attVal = poolStart(&dtd->pool);
+@@ -4506,8 +4721,9 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
+ break;
+ case XML_ROLE_ENTITY_VALUE:
+ if (dtd->keepProcessing) {
+- enum XML_Error result = storeEntityValue(
+- parser, enc, s + enc->minBytesPerChar, next - enc->minBytesPerChar);
++ enum XML_Error result
++ = storeEntityValue(parser, enc, s + enc->minBytesPerChar,
++ next - enc->minBytesPerChar, XML_ACCOUNT_NONE);
+ if (parser->m_declEntity) {
+ parser->m_declEntity->textPtr = poolStart(&dtd->entityValuePool);
+ parser->m_declEntity->textLen
+@@ -4897,12 +5113,15 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
+ if (parser->m_externalEntityRefHandler) {
+ dtd->paramEntityRead = XML_FALSE;
+ entity->open = XML_TRUE;
++ entityTrackingOnOpen(parser, entity, __LINE__);
+ if (! parser->m_externalEntityRefHandler(
+ parser->m_externalEntityRefHandlerArg, 0, entity->base,
+ entity->systemId, entity->publicId)) {
++ entityTrackingOnClose(parser, entity, __LINE__);
+ entity->open = XML_FALSE;
+ return XML_ERROR_EXTERNAL_ENTITY_HANDLING;
+ }
++ entityTrackingOnClose(parser, entity, __LINE__);
+ entity->open = XML_FALSE;
+ handleDefault = XML_FALSE;
+ if (! dtd->paramEntityRead) {
+@@ -5100,6 +5319,13 @@ 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 (! accountingDiffTolerated(parser, tok, s, next, __LINE__,
++ XML_ACCOUNT_DIRECT)) {
++ accountingOnAbort(parser);
++ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
++ }
++#endif
+ parser->m_eventEndPtr = next;
+ switch (tok) {
+ /* report partial linebreak - it might be the last token */
+@@ -5173,6 +5399,9 @@ processInternalEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl) {
+ return XML_ERROR_NO_MEMORY;
+ }
+ entity->open = XML_TRUE;
++#ifdef XML_DTD
++ entityTrackingOnOpen(parser, entity, __LINE__);
++#endif
+ entity->processed = 0;
+ openEntity->next = parser->m_openInternalEntities;
+ parser->m_openInternalEntities = openEntity;
+@@ -5191,17 +5420,22 @@ processInternalEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl) {
+ int tok
+ = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next);
+ result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd,
+- tok, next, &next, XML_FALSE, XML_FALSE);
++ tok, next, &next, XML_FALSE, XML_FALSE,
++ XML_ACCOUNT_ENTITY_EXPANSION);
+ } else
+ #endif /* XML_DTD */
+ result = doContent(parser, parser->m_tagLevel, parser->m_internalEncoding,
+- textStart, textEnd, &next, XML_FALSE);
++ textStart, textEnd, &next, XML_FALSE,
++ XML_ACCOUNT_ENTITY_EXPANSION);
+
+ if (result == XML_ERROR_NONE) {
+ if (textEnd != next && parser->m_parsingStatus.parsing == XML_SUSPENDED) {
+ entity->processed = (int)(next - textStart);
+ parser->m_processor = internalEntityProcessor;
+ } else {
++#ifdef XML_DTD
++ entityTrackingOnClose(parser, entity, __LINE__);
++#endif /* XML_DTD */
+ entity->open = XML_FALSE;
+ parser->m_openInternalEntities = openEntity->next;
+ /* put openEntity back in list of free instances */
+@@ -5234,12 +5468,13 @@ internalEntityProcessor(XML_Parser parser, const char *s, const char *end,
+ int tok
+ = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next);
+ result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd,
+- tok, next, &next, XML_FALSE, XML_TRUE);
++ tok, next, &next, XML_FALSE, XML_TRUE,
++ XML_ACCOUNT_ENTITY_EXPANSION);
+ } else
+ #endif /* XML_DTD */
+ result = doContent(parser, openEntity->startTagLevel,
+ parser->m_internalEncoding, textStart, textEnd, &next,
+- XML_FALSE);
++ XML_FALSE, XML_ACCOUNT_ENTITY_EXPANSION);
+
+ if (result != XML_ERROR_NONE)
+ return result;
+@@ -5248,6 +5483,9 @@ internalEntityProcessor(XML_Parser parser, const char *s, const char *end,
+ entity->processed = (int)(next - (char *)entity->textPtr);
+ return result;
+ } else {
++#ifdef XML_DTD
++ entityTrackingOnClose(parser, entity, __LINE__);
++#endif
+ entity->open = XML_FALSE;
+ parser->m_openInternalEntities = openEntity->next;
+ /* put openEntity back in list of free instances */
+@@ -5261,7 +5499,8 @@ internalEntityProcessor(XML_Parser parser, const char *s, const char *end,
+ parser->m_processor = prologProcessor;
+ tok = XmlPrologTok(parser->m_encoding, s, end, &next);
+ return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr,
+- (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE);
++ (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE,
++ XML_ACCOUNT_DIRECT);
+ } else
+ #endif /* XML_DTD */
+ {
+@@ -5269,7 +5508,8 @@ internalEntityProcessor(XML_Parser parser, const char *s, const char *end,
+ /* 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_Bool)! parser->m_parsingStatus.finalBuffer,
++ XML_ACCOUNT_DIRECT);
+ }
+ }
+
+@@ -5284,9 +5524,10 @@ errorProcessor(XML_Parser parser, const char *s, const char *end,
+
+ static enum XML_Error
+ storeAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
+- const char *ptr, const char *end, STRING_POOL *pool) {
++ const char *ptr, const char *end, STRING_POOL *pool,
++ enum XML_Account account) {
+ enum XML_Error result
+- = appendAttributeValue(parser, enc, isCdata, ptr, end, pool);
++ = appendAttributeValue(parser, enc, isCdata, ptr, end, pool, account);
+ if (result)
+ return result;
+ if (! isCdata && poolLength(pool) && poolLastChar(pool) == 0x20)
+@@ -5298,11 +5539,22 @@ storeAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
+
+ static enum XML_Error
+ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
+- const char *ptr, const char *end, STRING_POOL *pool) {
++ const char *ptr, const char *end, STRING_POOL *pool,
++ enum XML_Account account) {
+ DTD *const dtd = parser->m_dtd; /* save one level of indirection */
++#ifndef XML_DTD
++ UNUSED_P(account);
++#endif
++
+ for (;;) {
+ const char *next;
+ int tok = XmlAttributeValueTok(enc, ptr, end, &next);
++#ifdef XML_DTD
++ if (! accountingDiffTolerated(parser, tok, ptr, next, __LINE__, account)) {
++ accountingOnAbort(parser);
++ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
++ }
++#endif
+ switch (tok) {
+ case XML_TOK_NONE:
+ return XML_ERROR_NONE;
+@@ -5362,6 +5614,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
++ /* 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 */
+ if (! poolAppendChar(pool, ch))
+ return XML_ERROR_NO_MEMORY;
+ break;
+@@ -5439,9 +5699,16 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
+ enum XML_Error result;
+ const XML_Char *textEnd = entity->textPtr + entity->textLen;
+ entity->open = XML_TRUE;
++#ifdef XML_DTDded to activate (
++ entityTrackingOnOpen(parser, entity, __LINE__);ded to activate (
++#endif
+ result = appendAttributeValue(parser, parser->m_internalEncoding,
+ isCdata, (char *)entity->textPtr,
+- (char *)textEnd, pool);
++ (char *)textEnd, pool,
++ XML_ACCOUNT_ENTITY_EXPANSION);
++#ifdef XML_DTD
++ entityTrackingOnClose(parser, entity, __LINE__);
++#endif
+ entity->open = XML_FALSE;
+ if (result)
+ return result;
+@@ -5471,13 +5738,16 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
+
+ static enum XML_Error
+ storeEntityValue(XML_Parser parser, const ENCODING *enc,
+- const char *entityTextPtr, const char *entityTextEnd) {
++ const char *entityTextPtr, const char *entityTextEnd,
++ enum XML_Account account) {
+ 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
+ int oldInEntityValue = parser->m_prologState.inEntityValue;
+ parser->m_prologState.inEntityValue = 1;
++#else
++ UNUSED_P(account);
+ #endif /* XML_DTD */
+ /* never return Null for the value argument in EntityDeclHandler,
+ since this would indicate an external entity; therefore we
+@@ -5490,6 +5760,16 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc,
+ for (;;) {
+ const char *next;
+ int tok = XmlEntityValueTok(enc, entityTextPtr, entityTextEnd, &next);
++
++#ifdef XML_DTD
++ if (! accountingDiffTolerated(parser, tok, entityTextPtr, next, __LINE__,
++ account)) {
++ accountingOnAbort(parser);
++ result = XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
++ goto endEntityValue;
++ }
++#endif
++
+ switch (tok) {
+ case XML_TOK_PARAM_ENTITY_REF:
+ #ifdef XML_DTD
+@@ -5525,13 +5805,16 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc,
+ if (parser->m_externalEntityRefHandler) {
+ dtd->paramEntityRead = XML_FALSE;
+ entity->open = XML_TRUE;
++ entityTrackingOnOpen(parser, entity, __LINE__);
+ if (! parser->m_externalEntityRefHandler(
+ parser->m_externalEntityRefHandlerArg, 0, entity->base,
+ entity->systemId, entity->publicId)) {
++ entityTrackingOnClose(parser, entity, __LINE__);
+ entity->open = XML_FALSE;
+ result = XML_ERROR_EXTERNAL_ENTITY_HANDLING;
+ goto endEntityValue;
+ }
++ entityTrackingOnClose(parser, entity, __LINE__);
+ entity->open = XML_FALSE;
+ if (! dtd->paramEntityRead)
+ dtd->keepProcessing = dtd->standalone;
+@@ -5539,9 +5822,12 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc,
+ dtd->keepProcessing = dtd->standalone;
+ } else {
+ entity->open = XML_TRUE;
++ entityTrackingOnOpen(parser, entity, __LINE__);
+ result = storeEntityValue(
+ parser, parser->m_internalEncoding, (char *)entity->textPtr,
+- (char *)(entity->textPtr + entity->textLen));
++ (char *)(entity->textPtr + entity->textLen),
++ XML_ACCOUNT_ENTITY_EXPANSION);
++ entityTrackingOnClose(parser, entity, __LINE__);
+ entity->open = XML_FALSE;
+ if (result)
+ goto endEntityValue;
+@@ -6902,3 +7188,741 @@ copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) {
+ memcpy(result, s, charsRequired * sizeof(XML_Char));
+ return result;
+ }
++
++#ifdef XML_DTD
++
++static float
++accountingGetCurrentAmplification(XML_Parser rootParser) {
++ const XmlBigCount countBytesOutput
++ = rootParser->m_accounting.countBytesDirect
++ + rootParser->m_accounting.countBytesIndirect;
++ const float amplificationFactor
++ = rootParser->m_accounting.countBytesDirect
++ ? (countBytesOutput
++ / (float)(rootParser->m_accounting.countBytesDirect))
++ : 1.0f;
++ assert(! rootParser->m_parentParser);
++ return amplificationFactor;
++}
++
++static void
++accountingReportStats(XML_Parser originParser, const char *epilog) {
++ const XML_Parser rootParser = getRootParserOf(originParser, NULL);
++ assert(! rootParser->m_parentParser);
++
++ if (rootParser->m_accounting.debugLevel < 1) {
++ return;
++ }
++
++ const float amplificationFactor
++ = accountingGetCurrentAmplification(rootParser);
++ fprintf(stderr,
++ "expat: Accounting(%p): Direct " EXPAT_FMT_ULL(
++ "10") ", indirect " EXPAT_FMT_ULL("10") ", amplification %8.2f%s",
++ (void *)rootParser, rootParser->m_accounting.countBytesDirect,
++ rootParser->m_accounting.countBytesIndirect,
++ (double)amplificationFactor, epilog);
++}
++
++static void
++accountingOnAbort(XML_Parser originParser) {
++ accountingReportStats(originParser, " ABORTING\n");
++}
++
++static void
++accountingReportDiff(XML_Parser rootParser,
++ unsigned int levelsAwayFromRootParser, const char *before,
++ const char *after, ptrdiff_t bytesMore, int source_line,
++ enum XML_Account account) {
++ assert(! rootParser->m_parentParser);
++
++ fprintf(stderr,
++ " (+" EXPAT_FMT_PTRDIFF_T("6") " bytes %s|%d, xmlparse.c:%d) %*s\"",
++ bytesMore, (account == XML_ACCOUNT_DIRECT) ? "DIR" : "EXP",
++ levelsAwayFromRootParser, source_line, 10, "");
++
++ const char ellipis[] = "[..]";
++ const size_t ellipsisLength = sizeof(ellipis) /* because compile-time */ - 1;
++ const unsigned int contextLength = 10;
++
++ /* Note: Performance is of no concern here */
++ const char *walker = before;
++ if ((rootParser->m_accounting.debugLevel >= 3)
++ || (after - before)
++ <= (ptrdiff_t)(contextLength + ellipsisLength + contextLength)) {
++ for (; walker < after; walker++) {
++ fprintf(stderr, "%s", unsignedCharToPrintable(walker[0]));
++ }
++ } else {
++ for (; walker < before + contextLength; walker++) {
++ fprintf(stderr, "%s", unsignedCharToPrintable(walker[0]));
++ }
++ fprintf(stderr, ellipis);
++ walker = after - contextLength;
++ for (; walker < after; walker++) {
++ fprintf(stderr, "%s", unsignedCharToPrintable(walker[0]));
++ }
++ }
++ fprintf(stderr, "\"\n");
++}
++
++static XML_Bool
++accountingDiffTolerated(XML_Parser originParser, int tok, const char *before,
++ const char *after, int source_line,
++ enum XML_Account account) {
++ /* Note: We need to check the token type *first* to be sure that
++ * we can even access variable , safely.
++ * E.g. for XML_TOK_NONE may hold an invalid pointer. */
++ switch (tok) {
++ case XML_TOK_INVALID:
++ case XML_TOK_PARTIAL:
++ case XML_TOK_PARTIAL_CHAR:
++ case XML_TOK_NONE:
++ return XML_TRUE;
++ }
++
++ if (account == XML_ACCOUNT_NONE)
++ return XML_TRUE; /* because these bytes have been accounted for, already */
++
++ unsigned int levelsAwayFromRootParser;
++ const XML_Parser rootParser
++ = getRootParserOf(originParser, &levelsAwayFromRootParser);
++ assert(! rootParser->m_parentParser);
++
++ const int isDirect
++ = (account == XML_ACCOUNT_DIRECT) && (originParser == rootParser);
++ const ptrdiff_t bytesMore = after - before;
++
++ XmlBigCount *const additionTarget
++ = isDirect ? &rootParser->m_accounting.countBytesDirect
++ : &rootParser->m_accounting.countBytesIndirect;
++
++ /* Detect and avoid integer overflow */
++ if (*additionTarget > (XmlBigCount)(-1) - (XmlBigCount)bytesMore)
++ return XML_FALSE;
++ *additionTarget += bytesMore;
++
++ const XmlBigCount countBytesOutput
++ = rootParser->m_accounting.countBytesDirect
++ + rootParser->m_accounting.countBytesIndirect;
++ const float amplificationFactor
++ = accountingGetCurrentAmplification(rootParser);
++ const XML_Bool tolerated
++ = (countBytesOutput < rootParser->m_accounting.activationThresholdBytes)
++ || (amplificationFactor
++ <= rootParser->m_accounting.maximumAmplificationFactor);
++
++ if (rootParser->m_accounting.debugLevel >= 2) {
++ accountingReportStats(rootParser, "");
++ accountingReportDiff(rootParser, levelsAwayFromRootParser, before, after,
++ bytesMore, source_line, account);
++ }
++
++ return tolerated;
++}
++
++static void
++entityTrackingReportStats(XML_Parser rootParser, ENTITY *entity,
++ const char *action, int sourceLine) {
++ assert(! rootParser->m_parentParser);
++ if (rootParser->m_entity_stats.debugLevel < 1)
++ return;
++
++# if defined(XML_UNICODE)
++ const char *const entityName = "[..]";
++# else
++ const char *const entityName = entity->name;
++# endif
++
++ fprintf(
++ stderr,
++ "expat: Entities(%p): Count %9d, depth %2d/%2d %*s%s%s; %s length %d (xmlparse.c:%d)\n",
++ (void *)rootParser, rootParser->m_entity_stats.countEverOpened,
++ rootParser->m_entity_stats.currentDepth,
++ rootParser->m_entity_stats.maximumDepthSeen,
++ (rootParser->m_entity_stats.currentDepth - 1) * 2, "",
++ entity->is_param ? "%" : "&", entityName, action, entity->textLen,
++ sourceLine);
++}
++
++static void
++entityTrackingOnOpen(XML_Parser originParser, ENTITY *entity, int sourceLine) {
++ const XML_Parser rootParser = getRootParserOf(originParser, NULL);
++ assert(! rootParser->m_parentParser);
++
++ rootParser->m_entity_stats.countEverOpened++;
++ rootParser->m_entity_stats.currentDepth++;
++ if (rootParser->m_entity_stats.currentDepth
++ > rootParser->m_entity_stats.maximumDepthSeen) {
++ rootParser->m_entity_stats.maximumDepthSeen++;
++ }
++
++ entityTrackingReportStats(rootParser, entity, "OPEN ", sourceLine);
++}
++
++static void
++entityTrackingOnClose(XML_Parser originParser, ENTITY *entity, int sourceLine) {
++ const XML_Parser rootParser = getRootParserOf(originParser, NULL);
++ assert(! rootParser->m_parentParser);
++
++ entityTrackingReportStats(rootParser, entity, "CLOSE", sourceLine);
++ rootParser->m_entity_stats.currentDepth--;
++}
++
++static XML_Parser
++getRootParserOf(XML_Parser parser, unsigned int *outLevelDiff) {
++ XML_Parser rootParser = parser;
++ unsigned int stepsTakenUpwards = 0;
++ while (rootParser->m_parentParser) {
++ rootParser = rootParser->m_parentParser;
++ stepsTakenUpwards++;
++ }
++ assert(! rootParser->m_parentParser);
++ if (outLevelDiff != NULL) {
++ *outLevelDiff = stepsTakenUpwards;
++ }
++ return rootParser;
++}
++
++static const char *
++unsignedCharToPrintable(unsigned char c) {
++ switch (c) {
++ case 0:
++ return "\\0";
++ case 1:
++ return "\\x1";
++ case 2:
++ return "\\x2";
++ case 3:
++ return "\\x3";
++ case 4:
++ return "\\x4";
++ case 5:
++ return "\\x5";
++ case 6:
++ return "\\x6";
++ case 7:
++ return "\\x7";
++ case 8:
++ return "\\x8";
++ case 9:
++ return "\\t";
++ case 10:
++ return "\\n";
++ case 11:
++ return "\\xB";
++ case 12:
++ return "\\xC";
++ case 13:
++ return "\\r";
++ case 14:
++ return "\\xE";
++ case 15:
++ return "\\xF";
++ case 16:
++ return "\\x10";
++ case 17:
++ return "\\x11";
++ case 18:
++ return "\\x12";
++ case 19:
++ return "\\x13";
++ case 20:
++ return "\\x14";
++ case 21:
++ return "\\x15";
++ case 22:
++ return "\\x16";
++ case 23:
++ return "\\x17";
++ case 24:
++ return "\\x18";
++ case 25:
++ return "\\x19";
++ case 26:
++ return "\\x1A";
++ case 27:
++ return "\\x1B";
++ case 28:
++ return "\\x1C";
++ case 29:
++ return "\\x1D";
++ case 30:
++ return "\\x1E";
++ case 31:
++ return "\\x1F";
++ case 32:
++ return " ";
++ case 33:
++ return "!";
++ case 34:
++ return "\\\"";
++ case 35:
++ return "#";
++ case 36:
++ return "$";
++ case 37:
++ return "%";
++ case 38:
++ return "&";
++ case 39:
++ return "'";
++ case 40:
++ return "(";
++ case 41:
++ return ")";
++ case 42:
++ return "*";
++ case 43:
++ return "+";
++ case 44:
++ return ",";
++ case 45:
++ return "-";
++ case 46:
++ return ".";
++ case 47:
++ return "/";
++ case 48:
++ return "0";
++ case 49:
++ return "1";
++ case 50:
++ return "2";
++ case 51:
++ return "3";
++ case 52:
++ return "4";
++ case 53:
++ return "5";
++ case 54:
++ return "6";
++ case 55:
++ return "7";
++ case 56:
++ return "8";
++ case 57:
++ return "9";
++ case 58:
++ return ":";
++ case 59:
++ return ";";
++ case 60:
++ return "<";
++ case 61:
++ return "=";
++ case 62:
++ return ">";
++ case 63:
++ return "?";
++ case 64:
++ return "@";
++ case 65:
++ return "A";
++ case 66:
++ return "B";
++ case 67:
++ return "C";
++ case 68:
++ return "D";
++ case 69:
++ return "E";
++ case 70:
++ return "F";
++ case 71:
++ return "G";
++ case 72:
++ return "H";
++ case 73:
++ return "I";
++ case 74:
++ return "J";
++ case 75:
++ return "K";
++ case 76:
++ return "L";
++ case 77:
++ return "M";
++ case 78:
++ return "N";
++ case 79:
++ return "O";
++ case 80:
++ return "P";
++ case 81:
++ return "Q";
++ case 82:
++ return "R";
++ case 83:
++ return "S";
++ case 84:
++ return "T";
++ case 85:
++ return "U";
++ case 86:
++ return "V";
++ case 87:
++ return "W";
++ case 88:
++ return "X";
++ case 89:
++ return "Y";
++ case 90:
++ return "Z";
++ case 91:
++ return "[";
++ case 92:
++ return "\\\\";
++ case 93:
++ return "]";
++ case 94:
++ return "^";
++ case 95:
++ return "_";
++ case 96:
++ return "`";
++ case 97:
++ return "a";
++ case 98:
++ return "b";
++ case 99:
++ return "c";
++ case 100:
++ return "d";
++ case 101:
++ return "e";
++ case 102:
++ return "f";
++ case 103:
++ return "g";
++ case 104:
++ return "h";
++ case 105:
++ return "i";
++ case 106:
++ return "j";
++ case 107:
++ return "k";
++ case 108:
++ return "l";
++ case 109:
++ return "m";
++ case 110:
++ return "n";
++ case 111:
++ return "o";
++ case 112:
++ return "p";
++ case 113:
++ return "q";
++ case 114:
++ return "r";
++ case 115:
++ return "s";
++ case 116:
++ return "t";
++ case 117:
++ return "u";
++ case 118:
++ return "v";
++ case 119:
++ return "w";
++ case 120:
++ return "x";
++ case 121:
++ return "y";
++ case 122:
++ return "z";
++ case 123:
++ return "{";
++ case 124:
++ return "|";
++ case 125:
++ return "}";
++ case 126:
++ return "~";
++ case 127:
++ return "\\x7F";
++ case 128:
++ return "\\x80";
++ case 129:
++ return "\\x81";
++ case 130:
++ return "\\x82";
++ case 131:
++ return "\\x83";
++ case 132:
++ return "\\x84";
++ case 133:
++ return "\\x85";
++ case 134:
++ return "\\x86";
++ case 135:
++ return "\\x87";
++ case 136:
++ return "\\x88";
++ case 137:
++ return "\\x89";
++ case 138:
++ return "\\x8A";
++ case 139:
++ return "\\x8B";
++ case 140:
++ return "\\x8C";
++ case 141:
++ return "\\x8D";
++ case 142:
++ return "\\x8E";
++ case 143:
++ return "\\x8F";
++ case 144:
++ return "\\x90";
++ case 145:
++ return "\\x91";
++ case 146:
++ return "\\x92";
++ case 147:
++ return "\\x93";
++ case 148:
++ return "\\x94";
++ case 149:
++ return "\\x95";
++ case 150:
++ return "\\x96";
++ case 151:
++ return "\\x97";
++ case 152:
++ return "\\x98";
++ case 153:
++ return "\\x99";
++ case 154:
++ return "\\x9A";
++ case 155:
++ return "\\x9B";
++ case 156:
++ return "\\x9C";
++ case 157:
++ return "\\x9D";
++ case 158:
++ return "\\x9E";
++ case 159:
++ return "\\x9F";
++ case 160:
++ return "\\xA0";
++ case 161:
++ return "\\xA1";
++ case 162:
++ return "\\xA2";
++ case 163:
++ return "\\xA3";
++ case 164:
++ return "\\xA4";
++ case 165:
++ return "\\xA5";
++ case 166:
++ return "\\xA6";
++ case 167:
++ return "\\xA7";
++ case 168:
++ return "\\xA8";
++ case 169:
++ return "\\xA9";
++ case 170:
++ return "\\xAA";
++ case 171:
++ return "\\xAB";
++ case 172:
++ return "\\xAC";
++ case 173:
++ return "\\xAD";
++ case 174:
++ return "\\xAE";
++ case 175:
++ return "\\xAF";
++ case 176:
++ return "\\xB0";
++ case 177:
++ return "\\xB1";
++ case 178:
++ return "\\xB2";
++ case 179:
++ return "\\xB3";
++ case 180:
++ return "\\xB4";
++ case 181:
++ return "\\xB5";
++ case 182:
++ return "\\xB6";
++ case 183:
++ return "\\xB7";
++ case 184:
++ return "\\xB8";
++ case 185:
++ return "\\xB9";
++ case 186:
++ return "\\xBA";
++ case 187:
++ return "\\xBB";
++ case 188:
++ return "\\xBC";
++ case 189:
++ return "\\xBD";
++ case 190:
++ return "\\xBE";
++ case 191:
++ return "\\xBF";
++ case 192:
++ return "\\xC0";
++ case 193:
++ return "\\xC1";
++ case 194:
++ return "\\xC2";
++ case 195:
++ return "\\xC3";
++ case 196:
++ return "\\xC4";
++ case 197:
++ return "\\xC5";
++ case 198:
++ return "\\xC6";
++ case 199:
++ return "\\xC7";
++ case 200:
++ return "\\xC8";
++ case 201:
++ return "\\xC9";
++ case 202:
++ return "\\xCA";
++ case 203:
++ return "\\xCB";
++ case 204:
++ return "\\xCC";
++ case 205:
++ return "\\xCD";
++ case 206:
++ return "\\xCE";
++ case 207:
++ return "\\xCF";
++ case 208:
++ return "\\xD0";
++ case 209:
++ return "\\xD1";
++ case 210:
++ return "\\xD2";
++ case 211:
++ return "\\xD3";
++ case 212:
++ return "\\xD4";
++ case 213:
++ return "\\xD5";
++ case 214:
++ return "\\xD6";
++ case 215:
++ return "\\xD7";
++ case 216:
++ return "\\xD8";
++ case 217:
++ return "\\xD9";
++ case 218:
++ return "\\xDA";
++ case 219:
++ return "\\xDB";
++ case 220:
++ return "\\xDC";
++ case 221:
++ return "\\xDD";
++ case 222:
++ return "\\xDE";
++ case 223:
++ return "\\xDF";
++ case 224:
++ return "\\xE0";
++ case 225:
++ return "\\xE1";
++ case 226:
++ return "\\xE2";
++ case 227:
++ return "\\xE3";
++ case 228:
++ return "\\xE4";
++ case 229:
++ return "\\xE5";
++ case 230:
++ return "\\xE6";
++ case 231:
++ return "\\xE7";
++ case 232:
++ return "\\xE8";
++ case 233:
++ return "\\xE9";
++ case 234:
++ return "\\xEA";
++ case 235:
++ return "\\xEB";
++ case 236:
++ return "\\xEC";
++ case 237:
++ return "\\xED";
++ case 238:
++ return "\\xEE";
++ case 239:
++ return "\\xEF";
++ case 240:
++ return "\\xF0";
++ case 241:
++ return "\\xF1";
++ case 242:
++ return "\\xF2";
++ case 243:
++ return "\\xF3";
++ case 244:
++ return "\\xF4";
++ case 245:
++ return "\\xF5";
++ case 246:
++ return "\\xF6";
++ case 247:
++ return "\\xF7";
++ case 248:
++ return "\\xF8";
++ case 249:
++ return "\\xF9";
++ case 250:
++ return "\\xFA";
++ case 251:
++ return "\\xFB";
++ case 252:
++ return "\\xFC";
++ case 253:
++ return "\\xFD";
++ case 254:
++ return "\\xFE";
++ case 255:
++ return "\\xFF";
++ default:
++ assert(0); /* never gets here */
++ return "dead code";
++ }
++ assert(0); /* never gets here */
++}
++
++#endif /* XML_DTD */
++
++static unsigned long
++getDebugLevel(const char *variableName, unsigned long defaultDebugLevel) {
++ const char *const valueOrNull = getenv(variableName);
++ if (valueOrNull == NULL) {
++ return defaultDebugLevel;
++ }
++ const char *const value = valueOrNull;
++
++ errno = 0;
++ char *afterValue = (char *)value;
++ unsigned long debugLevel = strtoul(value, &afterValue, 10);
++ if ((errno != 0) || (afterValue[0] != '\0')) {
++ errno = 0;
++ return defaultDebugLevel;
++ }
++
++ return debugLevel;
++}
+--
+1.8.3.1
+
diff --git a/backport-CVE-2013-0340-tests-Cover-accounting-of-CDATA-sections.patch b/backport-CVE-2013-0340-tests-Cover-accounting-of-CDATA-sections.patch
new file mode 100644
index 0000000000000000000000000000000000000000..8baaa521c5ad2dd47d66496c14e9e2c79b9f5044
--- /dev/null
+++ b/backport-CVE-2013-0340-tests-Cover-accounting-of-CDATA-sections.patch
@@ -0,0 +1,34 @@
+From 77cfb8f4cd9679cef27ae9bc38e39ac51235af2d Mon Sep 17 00:00:00 2001
+From: Sebastian Pipping
+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 */
+ {" ", 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
++ */
++ {"333\">\n"
++ "]>\n"
++ "&e; \n",
++ NULL, NULL, sizeof(XML_Char) * strlen("111333"),
++ filled_later},
+
+ /* Conditional sections */
+ {"
+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[] = {
++ {" ", NULL, NULL, 0, 0},
++ {" ", NULL, NULL, 0, 0},
++
++ /* Attributes */
++ {"", NULL, NULL, 0, filled_later},
++ {" ", NULL, NULL, 0, 0},
++ {" ", NULL, NULL, 0,
++ filled_later},
++ {" ", NULL, NULL,
++ sizeof(XML_Char) * 5 /* number of predefined entites */, filled_later},
++ {"\n"
++ " \n"
++ " ",
++ NULL, NULL, 0, filled_later},
++
++ /* Text */
++ {"text ", NULL, NULL, 0, filled_later},
++ {"text1 text2 ", NULL, NULL, 0, filled_later},
++ {"&'><" ", NULL, NULL,
++ sizeof(XML_Char) * 5 /* number of predefined entites */, filled_later},
++ {"A) ", NULL, NULL, 0, filled_later},
++
++ /* Prolog */
++ {" ", NULL, NULL, 0, filled_later},
++
++ /* Whitespace */
++ {" ", NULL, NULL, 0, filled_later},
++ {" ", NULL, NULL, 0, filled_later},
++ {" ", NULL, NULL, 0, filled_later},
++
++ /* Comments */
++ {" ", NULL, NULL, 0, filled_later},
++
++ /* Processing instructions */
++ {" ",
++ NULL, NULL, 0, filled_later},
++ {" ",
++ "%e1;", "",
++ 0, filled_later},
++
++ /* CDATA */
++ {" ", NULL, NULL, 0, filled_later},
++
++ /* Conditional sections */
++ {"\n"
++ "\n"
++ "\n"
++ "%import;\n"
++ "]>\n"
++ " \n",
++ "]]>\n"
++ "]]>",
++ NULL, sizeof(XML_Char) * (strlen("INCLUDE") + strlen("IGNORE")),
++ filled_later},
++
++ /* General entities */
++ {"\n"
++ "]>\n"
++ "&nine; ",
++ NULL, NULL, sizeof(XML_Char) * strlen("123456789"), filled_later},
++ {"\n"
++ "]>\n"
++ "",
++ NULL, NULL, sizeof(XML_Char) * strlen("123456789"), filled_later},
++ {"\n"
++ "\n"
++ "]>\n"
++ "&nine2;&nine2;&nine2; ",
++ NULL, NULL,
++ sizeof(XML_Char) * 3 /* calls to &nine2; */ * 2 /* calls to &nine; */
++ * (strlen("&nine;") + strlen("123456789")),
++ filled_later},
++ {"\n"
++ "]>\n"
++ "&five; ",
++ "12345", NULL, 0, filled_later},
++
++ /* Parameter entities */
++ {"\">\n"
++ "%comment;\n"
++ "]>\n"
++ " ",
++ NULL, NULL, sizeof(XML_Char) * strlen(""), filled_later},
++ {"\n"
++ "%ninedef;\n"
++ "]>\n"
++ "&nine; ",
++ NULL, NULL,
++ sizeof(XML_Char)
++ * (strlen("") + strlen("123456789")),
++ filled_later},
++ {"\">\n"
++ "%comment;\">\n"
++ "%comment2;\n"
++ "]>\n"
++ " \n",
++ NULL, NULL,
++ sizeof(XML_Char)
++ * (strlen("%comment;%comment;") + 2 * strlen("")),
++ filled_later},
++ {"\n"
++ " \n"
++ " %five2def;\n"
++ "]>\n"
++ "&five2; ",
++ NULL, NULL, /* from "%five2def;": */
++ sizeof(XML_Char)
++ * (strlen("")
++ + 2 /* calls to "%five;" */ * strlen("12345")
++ + /* from "&five2;": */ strlen("[12345][12345]]]]")),
++ filled_later},
++ {"\n"
++ " ",
++ "'>\n"
++ "%comment;%comment;'>\n"
++ "%comment2;",
++ NULL,
++ sizeof(XML_Char)
++ * (strlen("%comment;%comment;")
++ + 2 /* calls to "%comment;" */ * strlen("")),
++ filled_later},
++ {"\n"
++ " ",
++ "\n"
++ "%e1;'>\n"
++ "%e2;\n",
++ "", sizeof(XML_Char) * strlen(""),
++ filled_later},
++ {
++ "\n"
++ " ",
++ "\n"
++ "",
++ "\n"
++ "hello\n"
++ "xml" /* without trailing newline! */,
++ 0,
++ filled_later,
++ },
++ {
++ "\n"
++ " ",
++ "\n"
++ "",
++ "\n"
++ "hello\n"
++ "xml\n" /* with trailing newline! */,
++ 0,
++ filled_later,
++ },
++ {"\n"
++ " \n",
++ "\n"
++ "\n"
++ "\n"
++ "%e1;\n",
++ "\xEF\xBB\xBF" /* UTF-8 BOM */,
++ strlen("\xEF\xBB\xBF"), filled_later},
++ {"\n"
++ "]>\n"
++ "&five; ",
++ "\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
+
diff --git a/backport-CVE-2013-0340-tests-Cover-billion-laughs-attack-protection-API.patch b/backport-CVE-2013-0340-tests-Cover-billion-laughs-attack-protection-API.patch
new file mode 100644
index 0000000000000000000000000000000000000000..9d9674f3327ed2f55486825b662e9306cf515bec
--- /dev/null
+++ b/backport-CVE-2013-0340-tests-Cover-billion-laughs-attack-protection-API.patch
@@ -0,0 +1,103 @@
+From e9d8f115580c3a25a9579c213f096af623dd92ce Mon Sep 17 00:00:00 2001
+From: Sebastian Pipping
+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 /* ptrdiff_t */
+ #include
+ #include
++#include /* 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
+
diff --git a/backport-CVE-2013-0340-tests-Cover-helper-unsignedCharToPrintable.patch b/backport-CVE-2013-0340-tests-Cover-helper-unsignedCharToPrintable.patch
new file mode 100644
index 0000000000000000000000000000000000000000..44cce0b705354325762eab2e0bbbe41ce192df6c
--- /dev/null
+++ b/backport-CVE-2013-0340-tests-Cover-helper-unsignedCharToPrintable.patch
@@ -0,0 +1,85 @@
+From 5dbc857f47ecd6c37748ce279c6535fbbf526590 Mon Sep 17 00:00:00 2001
+From: Sebastian Pipping
+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
+
diff --git a/backport-CVE-2013-0340-xmlwf-Add-support-for-custom-attack-protection-param.patch b/backport-CVE-2013-0340-xmlwf-Add-support-for-custom-attack-protection-param.patch
new file mode 100644
index 0000000000000000000000000000000000000000..1a814293ad429619be6bf55fb4cc94f4a1613983
--- /dev/null
+++ b/backport-CVE-2013-0340-xmlwf-Add-support-for-custom-attack-protection-param.patch
@@ -0,0 +1,161 @@
+From c6223b3b0f3d8e6a37b5775b44eeded02e9c3ea7 Mon Sep 17 00:00:00 2001
+From: Sebastian Pipping
+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
+ #include
+ #include
++#include /* for isnan */
++#include
+
+ #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
+
diff --git a/backport-CVE-2013-0340-xmlwf-Include-expat_config.h-so-we-can-check-for-mac.patch b/backport-CVE-2013-0340-xmlwf-Include-expat_config.h-so-we-can-check-for-mac.patch
new file mode 100644
index 0000000000000000000000000000000000000000..14b07b6a392381cc718a70e52792aa17c4f82d62
--- /dev/null
+++ b/backport-CVE-2013-0340-xmlwf-Include-expat_config.h-so-we-can-check-for-mac.patch
@@ -0,0 +1,26 @@
+From 65cddaa4e93263fb88261b60f97cf29f1589d038 Mon Sep 17 00:00:00 2001
+From: Sebastian Pipping
+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
++
+ #include
+ #include
+ #include
+--
+1.8.3.1
+
diff --git a/backport-CVE-2013-0340-xmlwf.1-Document-arguments-a-and-b.patch b/backport-CVE-2013-0340-xmlwf.1-Document-arguments-a-and-b.patch
new file mode 100644
index 0000000000000000000000000000000000000000..7c5c780b6e613988d0f0d87b29ae987cee361990
--- /dev/null
+++ b/backport-CVE-2013-0340-xmlwf.1-Document-arguments-a-and-b.patch
@@ -0,0 +1,84 @@
+From bf878495985b81731c620bbac26df79e6c98c9fd Mon Sep 17 00:00:00 2001
+From: Sebastian Pipping
+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 @@
+ Scott">
+ Bronson">
+
+- March 11, 2016">
++ May 4, 2021">
+
+ 1">
+@@ -140,6 +140,50 @@ supports both.
+
+
+
++ -a factor
++
++
++ Sets the maximum tolerated amplification factor
++ for protection against billion laughs attacks (default: 100.0).
++ The amplification factor is calculated as ..
++
++
++ amplification := (direct + indirect) / direct
++
++
++ .. while parsing, whereas
++ <direct> is the number of bytes read
++ from the primary document in parsing and
++ <indirect> is the number of bytes
++ added by expanding entities and reading of external DTD files,
++ combined.
++
++
++ NOTE :
++ If you ever need to increase this value for non-attack payload,
++ please file a bug report.
++
++
++
++
++
++ -b bytes
++
++
++ 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 "activation threshold".
++
++
++ NOTE :
++ If you ever need to increase this value for non-attack payload,
++ please file a bug report.
++
++
++
++
++
+ -c
+
+
+@@ -434,6 +478,7 @@ http://www.xml.com/pub/a/tools/ruwf/check.html
+
+ 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
+
+
+
+--
+1.8.3.1
+
diff --git a/backport-xmlparse.c-Fix-reading-uninitialized-variable-404.patch b/backport-xmlparse.c-Fix-reading-uninitialized-variable-404.patch
new file mode 100644
index 0000000000000000000000000000000000000000..c445112d055053a5035e65b1d8107aebfe58d687
--- /dev/null
+++ b/backport-xmlparse.c-Fix-reading-uninitialized-variable-404.patch
@@ -0,0 +1,56 @@
+From 13bb381d296ef8da09a3f00f5b23724b5deb6a38 Mon Sep 17 00:00:00 2001w
+From: Sebastian Pipping
+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
+
diff --git a/backport-xmlwf-Extract-macro-XMLWF_SHIFT_ARGUMENT.patch b/backport-xmlwf-Extract-macro-XMLWF_SHIFT_ARGUMENT.patch
new file mode 100644
index 0000000000000000000000000000000000000000..a685e806fea50ead872721f859de0010c702eeb0
--- /dev/null
+++ b/backport-xmlwf-Extract-macro-XMLWF_SHIFT_ARGUMENT.patch
@@ -0,0 +1,63 @@
+From 385aeb477bac9538ab2b3d5cf0e76e8c751374be Mon Sep 17 00:00:00 2001
+From: Sebastian Pipping
+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
+
diff --git a/expat.spec b/expat.spec
index 380fd5aabba6bf928db8411a8fabda590dfe9d72..e15ae0cd6d0c6edf23eca09b405e498f07c2d112 100644
--- a/expat.spec
+++ b/expat.spec
@@ -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 - 2.2.9-3
+- fix CVE-2013-0340
+- xmlparse.c: Fix reading uninitialized variable
+
* Sun Jun 28 2020 liuchenguang - 2.2.9-2
- quality enhancement synchronization github patch