You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openoffice.apache.org by ar...@apache.org on 2022/03/17 19:39:09 UTC
[openoffice] 01/05: Upgrade expat-2.2.10 into 2.2.12
This is an automated email from the ASF dual-hosted git repository.
ardovm pushed a commit to branch AOO42X
in repository https://gitbox.apache.org/repos/asf/openoffice.git
commit 9bd0941698031618c3596bedcbd88476162e4b6c
Author: Arrigo Marchiori <ar...@yahoo.it>
AuthorDate: Sat Mar 5 12:36:36 2022 +0100
Upgrade expat-2.2.10 into 2.2.12
Also use only one patch file named after the base version
(cherry picked from commit cbf2698a61eb5662bff50e5fce9be9d60c27bb7e)
---
main/expat/expat-2.2.10.patch | 3504 +++++++++++++++++++++++++++++++++++++++++
main/expat/expat-2.2.11.patch | 2745 --------------------------------
main/expat/makefile.mk | 2 +-
3 files changed, 3505 insertions(+), 2746 deletions(-)
diff --git a/main/expat/expat-2.2.10.patch b/main/expat/expat-2.2.10.patch
index ff1d16a..b6413d6 100644
--- a/main/expat/expat-2.2.10.patch
+++ b/main/expat/expat-2.2.10.patch
@@ -81,3 +81,3507 @@
+.INCLUDE : set_wntx64.mk
+.INCLUDE : target.mk
+.INCLUDE : tg_wntx64.mk
+diff -ru misc/expat-2.2.10/Changes misc/build/expat-2.2.10/Changes
+--- misc/expat-2.2.10/Changes 2020-10-03 17:14:57.000000000 +0200
++++ misc/build/expat-2.2.10/Changes 2022-03-05 12:25:27.575396494 +0100
+@@ -2,6 +2,43 @@
+ https://github.com/libexpat/libexpat/labels/help%20wanted
+ If you can help, please get in touch. Thanks!
+
++
++Release 2.2.11 XXX XXXXX XX XXXX
++ Security fixes:
++ #34 #466 CVE-2013-0340/CWE-776 -- Protect against billion laughs attacks
++ (denial-of-service; flavors targeting CPU time or RAM or both,
++ leveraging general entities or parameter entities or both)
++ by tracking and limiting the input amplification factor
++ (<amplification> := (<direct> + <indirect>) / <direct>).
++ By conservative default, amplification up to a factor of 100.0
++ is tolerated and rejection only starts after 8 MiB of output bytes
++ (=<direct> + <indirect>) have been processed.
++ A new error code XML_ERROR_AMPLIFICATION_LIMIT_BREACH signals
++ this condition.
++ 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.
++
++ Special thanks to:
++ Nick Wellnhofer
++ Yury Gribov
++ and
++ Clang LeakSan
++ JetBrains
++
+ Release 2.2.10 Sat October 3 2020
+ Bug fixes:
+ #390 #395 #398 Fix undefined behavior during parsing caused by
+diff -ru misc/expat-2.2.10/CMakeLists.txt misc/build/expat-2.2.10/CMakeLists.txt
+--- misc/expat-2.2.10/CMakeLists.txt 2020-10-03 17:14:57.000000000 +0200
++++ misc/build/expat-2.2.10/CMakeLists.txt 2022-03-05 12:25:27.575396494 +0100
+@@ -448,14 +448,12 @@
+ endif()
+ endfunction()
+
+- add_executable(runtests tests/runtests.c ${test_SRCS})
++ add_executable(runtests tests/runtests.c ${test_SRCS} ${expat_SRCS})
+ set_property(TARGET runtests PROPERTY RUNTIME_OUTPUT_DIRECTORY tests)
+- target_link_libraries(runtests expat)
+ expat_add_test(runtests $<TARGET_FILE:runtests>)
+
+- add_executable(runtestspp tests/runtestspp.cpp ${test_SRCS})
++ add_executable(runtestspp tests/runtestspp.cpp ${test_SRCS} ${expat_SRCS})
+ set_property(TARGET runtestspp PROPERTY RUNTIME_OUTPUT_DIRECTORY tests)
+- target_link_libraries(runtestspp expat)
+ expat_add_test(runtestspp $<TARGET_FILE:runtestspp>)
+ endif()
+
+diff -ru misc/expat-2.2.10/doc/Makefile.in misc/build/expat-2.2.10/doc/Makefile.in
+--- misc/expat-2.2.10/doc/Makefile.in 2020-10-03 17:37:06.000000000 +0200
++++ misc/build/expat-2.2.10/doc/Makefile.in 2022-03-05 12:25:27.575396494 +0100
+@@ -1,7 +1,7 @@
+-# Makefile.in generated by automake 1.16.2 from Makefile.am.
++# Makefile.in generated by automake 1.16.1 from Makefile.am.
+ # @configure_input@
+
+-# Copyright (C) 1994-2020 Free Software Foundation, Inc.
++# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+
+ # This Makefile.in is free software; the Free Software Foundation
+ # gives unlimited permission to copy and/or distribute it,
+@@ -314,7 +314,6 @@
+ prefix = @prefix@
+ program_transform_name = @program_transform_name@
+ psdir = @psdir@
+-runstatedir = @runstatedir@
+ sbindir = @sbindir@
+ sharedstatedir = @sharedstatedir@
+ srcdir = @srcdir@
+diff -ru misc/expat-2.2.10/doc/reference.html misc/build/expat-2.2.10/doc/reference.html
+--- misc/expat-2.2.10/doc/reference.html 2020-09-25 19:47:39.000000000 +0200
++++ misc/build/expat-2.2.10/doc/reference.html 2022-03-05 12:25:27.575396494 +0100
+@@ -120,6 +120,13 @@
+ <li><a href="#XML_GetInputContext">XML_GetInputContext</a></li>
+ </ul>
+ </li>
++ <li>
++ <a href="#billion-laughs">Billion Laughs Attack Protection</a>
++ <ul>
++ <li><a href="#XML_SetBillionLaughsAttackProtectionMaximumAmplification">XML_SetBillionLaughsAttackProtectionMaximumAmplification</a></li>
++ <li><a href="#XML_SetBillionLaughsAttackProtectionActivationThreshold">XML_SetBillionLaughsAttackProtectionActivationThreshold</a></li>
++ </ul>
++ </li>
+ <li><a href="#miscellaneous">Miscellaneous Functions</a>
+ <ul>
+ <li><a href="#XML_SetUserData">XML_SetUserData</a></li>
+@@ -1998,6 +2005,98 @@
+ return NULL.</p>
+ </div>
+
++
++
++<h3><a name="billion-laughs">Billion Laughs Attack Protection</a></h3>
++
++<p>The functions in this section configure the built-in
++ protection against various forms of
++ <a href="https://en.wikipedia.org/wiki/Billion_laughs_attack">billion laughs attacks</a>.</p>
++
++<pre class="fcndec" id="XML_SetBillionLaughsAttackProtectionMaximumAmplification">
++/* Added in Expat 2.2.11. */
++XML_Bool XMLCALL
++XML_SetBillionLaughsAttackProtectionMaximumAmplification(XML_Parser p,
++ float maximumAmplificationFactor);
++</pre>
++<div class="fcndef">
++ <p>
++ Sets the maximum tolerated amplification factor
++ for protection against
++ <a href="https://en.wikipedia.org/wiki/Billion_laughs_attack">billion laughs attacks</a>
++ (default: <code>100.0</code>)
++ of parser <code>p</code> to <code>maximumAmplificationFactor</code>, and
++ returns <code>XML_TRUE</code> upon success and <code>XML_TRUE</code> upon error.
++ </p>
++
++ The amplification factor is calculated as ..
++ <pre>
++ amplification := (direct + indirect) / direct
++ </pre>
++ .. while parsing, whereas
++ <code>direct</code> is the number of bytes read from the primary document in parsing and
++ <code>indirect</code> is the number of bytes added by expanding entities and reading of external DTD files, combined.
++
++ <p>For a call to <code>XML_SetBillionLaughsAttackProtectionMaximumAmplification</code> to succeed:</p>
++ <ul>
++ <li>parser <code>p</code> must be a non-<code>NULL</code> root parser (without any parent parsers) and</li>
++ <li><code>maximumAmplificationFactor</code> must be non-<code>NaN</code> and greater than or equal to <code>1.0</code>.</li>
++ </ul>
++
++ <p>
++ <strong>Note:</strong>
++ If you ever need to increase this value for non-attack payload,
++ please <a href="https://github.com/libexpat/libexpat/issues">file a bug report</a>.
++ </p>
++
++ <p>
++ <strong>Note:</strong>
++ Peak amplifications
++ of factor 15,000 for the entire payload and
++ of factor 30,000 in the middle of parsing
++ have been observed with small benign files in practice.
++
++ So if you do reduce the maximum allowed amplification,
++ please make sure that the activation threshold is still big enough
++ to not end up with undesired false positives (i.e. benign files being rejected).
++ </p>
++</div>
++
++<pre class="fcndec" id="XML_SetBillionLaughsAttackProtectionActivationThreshold">
++/* Added in Expat 2.2.11. */
++XML_Bool XMLCALL
++XML_SetBillionLaughsAttackProtectionActivationThreshold(XML_Parser p,
++ unsigned long long activationThresholdBytes);
++</pre>
++<div class="fcndef">
++ <p>
++ Sets number of output bytes (including amplification from entity expansion and reading DTD files)
++ needed to activate protection against
++ <a href="https://en.wikipedia.org/wiki/Billion_laughs_attack">billion laughs attacks</a>
++ (default: <code>8 MiB</code>)
++ of parser <code>p</code> to <code>activationThresholdBytes</code>, and
++ returns <code>XML_TRUE</code> upon success and <code>XML_TRUE</code> upon error.
++ </p>
++
++ <p>For a call to <code>XML_SetBillionLaughsAttackProtectionActivationThreshold</code> to succeed:</p>
++ <ul>
++ <li>parser <code>p</code> must be a non-<code>NULL</code> root parser (without any parent parsers).</li>
++ </ul>
++
++ <p>
++ <strong>Note:</strong>
++ If you ever need to increase this value for non-attack payload,
++ please <a href="https://github.com/libexpat/libexpat/issues">file a bug report</a>.
++ </p>
++
++ <p>
++ <strong>Note:</strong>
++ Activation thresholds below 4 MiB are known to break support for
++ <a href="https://en.wikipedia.org/wiki/Darwin_Information_Typing_Architecture">DITA</a> 1.3 payload
++ and are hence not recommended.
++ </p>
++</div>
++
+ <h3><a name="miscellaneous">Miscellaneous functions</a></h3>
+
+ <p>The functions in this section either obtain state information from
+diff -ru misc/expat-2.2.10/doc/xmlwf.xml misc/build/expat-2.2.10/doc/xmlwf.xml
+--- misc/expat-2.2.10/doc/xmlwf.xml 2020-09-25 19:47:39.000000000 +0200
++++ misc/build/expat-2.2.10/doc/xmlwf.xml 2022-03-05 12:25:27.575396494 +0100
+@@ -3,7 +3,7 @@
+ <!ENTITY dhfirstname "<firstname>Scott</firstname>">
+ <!ENTITY dhsurname "<surname>Bronson</surname>">
+ <!-- Please adjust the date whenever revising the manpage. -->
+- <!ENTITY dhdate "<date>March 11, 2016</date>">
++ <!ENTITY dhdate "<date>July 18, 2021</date>">
+ <!-- SECTION should be 1-8, maybe w/ subsection other parameters are
+ allowed: see man(7), man(1). -->
+ <!ENTITY dhsection "<manvolnum>1</manvolnum>">
+@@ -138,6 +138,52 @@
+ </para>
+
+ <variablelist>
++
++ <variablelist>
++
++ <varlistentry>
++ <term><option>-a</option> <replaceable>factor</replaceable></term>
++ <listitem>
++ <para>
++ Sets the maximum tolerated amplification factor
++ for protection against billion laughs attacks (default: 100.0).
++ The amplification factor is calculated as ..
++ </para>
++ <literallayout>
++ amplification := (direct + indirect) / direct
++ </literallayout>
++ <para>
++ .. while parsing, whereas
++ <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.
++ </para>
++ <para>
++ <emphasis>NOTE</emphasis>:
++ If you ever need to increase this value for non-attack payload,
++ please file a bug report.
++ </para>
++ </listitem>
++ </varlistentry>
++
++ <varlistentry>
++ <term><option>-b</option> <replaceable>bytes</replaceable></term>
++ <listitem>
++ <para>
++ Sets the number of output bytes (including amplification)
++ needed to activate protection against billion laughs attacks
++ (default: 8 MiB).
++ This can be thought of as an "activation threshold".
++ </para>
++ <para>
++ <emphasis>NOTE</emphasis>:
++ If you ever need to increase this value for non-attack payload,
++ please file a bug report.
++ </para>
++ </listitem>
++ </varlistentry>
+
+ <varlistentry>
+ <term><option>-c</option></term>
+@@ -455,6 +501,7 @@
+ <literallayout>
+ The Expat home page: http://www.libexpat.org/
+ The W3 XML specification: http://www.w3.org/TR/REC-xml
++Billion laughs attack: https://en.wikipedia.org/wiki/Billion_laughs_attack
+ </literallayout>
+
+ </para>
+diff -ru misc/expat-2.2.10/lib/expat.h misc/build/expat-2.2.10/lib/expat.h
+--- misc/expat-2.2.10/lib/expat.h 2020-10-03 17:14:57.000000000 +0200
++++ misc/build/expat-2.2.10/lib/expat.h 2022-03-05 12:25:27.583396678 +0100
+@@ -115,7 +115,10 @@
+ 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.11 */
++ XML_ERROR_NO_BUFFER,
++ XML_ERROR_AMPLIFICATION_LIMIT_BREACH
+ };
+
+ enum XML_Content_Type {
+@@ -997,7 +1000,10 @@
+ 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.11 */
++ 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,12 +1016,25 @@
+ XMLPARSEAPI(const XML_Feature *)
+ XML_GetFeatureList(void);
+
++
++#ifdef XML_DTD
++/* Added in Expat 2.2.11 */
++XMLPARSEAPI(XML_Bool)
++XML_SetBillionLaughsAttackProtectionMaximumAmplification(
++ XML_Parser parser, float maximumAmplificationFactor);
++
++/* Added in Expat 2.2.11 */
++XMLPARSEAPI(XML_Bool)
++XML_SetBillionLaughsAttackProtectionActivationThreshold(
++ XML_Parser parser, unsigned long long activationThresholdBytes);
++#endif
++
+ /* Expat follows the semantic versioning convention.
+ See http://semver.org.
+ */
+ #define XML_MAJOR_VERSION 2
+ #define XML_MINOR_VERSION 2
+-#define XML_MICRO_VERSION 10
++#define XML_MICRO_VERSION 12
+
+ #ifdef __cplusplus
+ }
+diff -ru misc/expat-2.2.10/lib/internal.h misc/build/expat-2.2.10/lib/internal.h
+--- misc/expat-2.2.10/lib/internal.h 2020-09-25 19:47:39.000000000 +0200
++++ misc/build/expat-2.2.10/lib/internal.h 2022-03-05 12:25:27.575396494 +0100
+@@ -101,10 +101,47 @@
+ # endif
+ #endif
+
++#include <limits.h> // ULONG_MAX
++
++#if defined(_WIN32) && ! defined(__USE_MINGW_ANSI_STDIO)
++# define EXPAT_FMT_ULL(midpart) "%" midpart "I64u"
++# 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"
++# 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"
++# 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
++
++
+ #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 */
++
++#include "expat.h" // so we can use type XML_Parser below
++
+ #ifdef __cplusplus
+ extern "C" {
+ #endif
+@@ -117,6 +154,11 @@
+ void
+ _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);
++const char *unsignedCharToPrintable(unsigned char c);
++#endif
+
+ #ifdef __cplusplus
+ }
+diff -ru misc/expat-2.2.10/lib/libexpat.def misc/build/expat-2.2.10/lib/libexpat.def
+--- misc/expat-2.2.10/lib/libexpat.def 2020-09-25 19:47:39.000000000 +0200
++++ misc/build/expat-2.2.10/lib/libexpat.def 2022-03-05 12:25:27.575396494 +0100
+@@ -76,3 +76,6 @@
+ XML_SetHashSalt @67
+ ; added with version 2.2.5
+ _INTERNAL_trim_to_complete_utf8_characters @68
++; added with version 2.2.11
++ XML_SetBillionLaughsAttackProtectionActivationThreshold @69
++ XML_SetBillionLaughsAttackProtectionMaximumAmplification @70
+\ No newline at end of file
+diff -ru misc/expat-2.2.10/lib/libexpatw.def misc/build/expat-2.2.10/lib/libexpatw.def
+--- misc/expat-2.2.10/lib/libexpatw.def 2020-09-25 19:47:39.000000000 +0200
++++ misc/build/expat-2.2.10/lib/libexpatw.def 2022-03-05 12:25:27.579396586 +0100
+@@ -76,3 +76,6 @@
+ XML_SetHashSalt @67
+ ; added with version 2.2.5
+ _INTERNAL_trim_to_complete_utf8_characters @68
++; added with version 2.2.11
++ XML_SetBillionLaughsAttackProtectionActivationThreshold @69
++ XML_SetBillionLaughsAttackProtectionMaximumAmplification @70
+\ No newline at end of file
+diff -ru misc/expat-2.2.10/lib/Makefile.in misc/build/expat-2.2.10/lib/Makefile.in
+--- misc/expat-2.2.10/lib/Makefile.in 2020-10-03 17:37:06.000000000 +0200
++++ misc/build/expat-2.2.10/lib/Makefile.in 2022-03-05 12:25:27.579396586 +0100
+@@ -1,7 +1,7 @@
+-# Makefile.in generated by automake 1.16.2 from Makefile.am.
++# Makefile.in generated by automake 1.16.1 from Makefile.am.
+ # @configure_input@
+
+-# Copyright (C) 1994-2020 Free Software Foundation, Inc.
++# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+
+ # This Makefile.in is free software; the Free Software Foundation
+ # gives unlimited permission to copy and/or distribute it,
+@@ -372,7 +372,6 @@
+ prefix = @prefix@
+ program_transform_name = @program_transform_name@
+ psdir = @psdir@
+-runstatedir = @runstatedir@
+ sbindir = @sbindir@
+ sharedstatedir = @sharedstatedir@
+ srcdir = @srcdir@
+diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
+--- misc/expat-2.2.10/lib/xmlparse.c 2020-10-03 17:14:57.000000000 +0200
++++ misc/build/expat-2.2.10/lib/xmlparse.c 2022-03-05 12:25:27.583396678 +0100
+@@ -47,6 +47,7 @@
+ #include <limits.h> /* UINT_MAX */
+ #include <stdio.h> /* fprintf */
+ #include <stdlib.h> /* getenv, rand_s */
++#include <math.h> /* 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 */
+@@ -73,6 +74,10 @@
+
+ #ifdef _WIN32
+ # include "winconfig.h"
++#include <float.h>
++#ifndef isnan
++#define isnan _isnan
++#endif
+ #elif defined(HAVE_EXPAT_CONFIG_H)
+ # include <expat_config.h>
+ #endif /* ndef _WIN32 */
+@@ -382,6 +387,31 @@
+ 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,16 +442,18 @@
+ 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);
++ 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,
+@@ -431,7 +463,8 @@
+ 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 +473,18 @@
+ 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 +548,34 @@
+
+ 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);
++#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 +689,10 @@
+ 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)))
+@@ -640,6 +708,7 @@
+ XML_ParserCreateNS(const XML_Char *encodingName, XML_Char nsSep) {
+ XML_Char tmp[2];
+ *tmp = nsSep;
++ tmp[1] = 0;
+ return XML_ParserCreate_MM(encodingName, NULL, tmp);
+ }
+
+@@ -809,9 +878,8 @@
+
+ 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,
++ 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));
+ }
+ return entropy;
+@@ -855,7 +923,7 @@
+ return ENTROPY_DEBUG("fallback(4)", entropy * 2147483647);
+ } else {
+ return ENTROPY_DEBUG("fallback(8)",
+- entropy * (unsigned long)2305843009213693951ULL);
++ entropy * (unsigned long long)2305843009213693951ULL);
+ }
+ #endif
+ }
+@@ -1073,6 +1141,18 @@
+ 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 */
+@@ -1255,6 +1335,7 @@
+ if (parser->m_ns) {
+ XML_Char tmp[2];
+ *tmp = parser->m_namespaceSeparator;
++ tmp[1] = 0;
+ parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd);
+ } else {
+ parser = parserCreate(encodingName, &parser->m_mem, NULL, newDtd);
+@@ -1893,6 +1974,12 @@
+ parser->m_errorCode = XML_ERROR_FINISHED;
+ return XML_STATUS_ERROR;
+ case XML_INITIALIZED:
++ /* Has someone called XML_GetBuffer successfully before? */
++ if (! parser->m_bufferPtr) {
++ parser->m_errorCode = XML_ERROR_NO_BUFFER;
++ return XML_STATUS_ERROR;
++ }
++
+ if (parser->m_parentParser == NULL && ! startParsing(parser)) {
+ parser->m_errorCode = XML_ERROR_NO_MEMORY;
+ return XML_STATUS_ERROR;
+@@ -1971,6 +2058,11 @@
+ keep = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer);
+ if (keep > XML_CONTEXT_BYTES)
+ keep = XML_CONTEXT_BYTES;
++ /* Detect and prevent integer overflow */
++ if (keep > INT_MAX - neededSize) {
++ parser->m_errorCode = XML_ERROR_NO_MEMORY;
++ return NULL;
++ }
+ neededSize += keep;
+ #endif /* defined XML_CONTEXT_BYTES */
+ if (neededSize
+@@ -2337,6 +2429,13 @@
+ /* 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.11. */
++ case XML_ERROR_NO_BUFFER:
++ return XML_L(
++ "a successful prior call to function XML_GetBuffer is required");
++ case XML_ERROR_AMPLIFICATION_LIMIT_BREACH:
++ return XML_L(
++ "limit on input amplification factor (from DTD and entities) breached");
+ }
+ return NULL;
+ }
+@@ -2373,41 +2472,75 @@
+
+ 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
++#ifdef XML_DTD
++ /* Added in Expat 2.2.11. */
++ {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}};
++ {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
+@@ -2419,6 +2552,7 @@
+ while (tag) {
+ int bufSize;
+ int nameLen = sizeof(XML_Char) * (tag->name.strLen + 1);
++ size_t rawNameLen;
+ char *rawNameBuf = tag->buf + nameLen;
+ /* Stop if already stored. Since m_tagStack is a stack, we can stop
+ at the first entry that has already been copied; everything
+@@ -2430,7 +2564,11 @@
+ /* For re-use purposes we need to ensure that the
+ size of tag->buf is a multiple of sizeof(XML_Char).
+ */
+- bufSize = nameLen + ROUND_UP(tag->rawNameLength, sizeof(XML_Char));
++ rawNameLen = ROUND_UP(tag->rawNameLength, sizeof(XML_Char));
++ /* Detect and prevent integer overflow. */
++ if (rawNameLen > (size_t)INT_MAX - nameLen)
++ return XML_FALSE;
++ bufSize = nameLen + (int)rawNameLen;
+ if (bufSize > tag->bufEnd - tag->buf) {
+ char *temp = (char *)REALLOC(parser, tag->buf, bufSize);
+ if (temp == NULL)
+@@ -2460,9 +2598,9 @@
+ 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;
+@@ -2487,6 +2625,14 @@
+ 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
+@@ -2524,6 +2670,10 @@
+ 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) {
+@@ -2565,7 +2715,8 @@
+ 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;
+@@ -2576,7 +2727,7 @@
+ 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;
+
+@@ -2594,6 +2745,17 @@
+ 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:
+@@ -2649,6 +2811,14 @@
+ 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)
+@@ -2767,7 +2937,7 @@
+ }
+ 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)
+@@ -2791,7 +2961,8 @@
+ 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;
+@@ -2926,7 +3097,7 @@
+ /* 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) {
+@@ -3055,7 +3226,8 @@
+ */
+ 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;
+@@ -3087,13 +3259,38 @@
+
+ /* get the attributes from the tokenizer */
+ n = XmlGetAttributes(enc, attStr, parser->m_attsSize, parser->m_atts);
++
++ /* Detect and prevent integer overflow */
++ if (n > INT_MAX - nDefaultAtts) {
++ return XML_ERROR_NO_MEMORY;
++ }
++
+ if (n + nDefaultAtts > parser->m_attsSize) {
+ int oldAttsSize = parser->m_attsSize;
+ ATTRIBUTE *temp;
+ #ifdef XML_ATTR_INFO
+ XML_AttrInfo *temp2;
+ #endif
++
++ /* Detect and prevent integer overflow */
++ if ((nDefaultAtts > INT_MAX - INIT_ATTS_SIZE)
++ || (n > INT_MAX - (nDefaultAtts + INIT_ATTS_SIZE))) {
++ return XML_ERROR_NO_MEMORY;
++ }
++
+ parser->m_attsSize = n + nDefaultAtts + INIT_ATTS_SIZE;
++
++ /* Detect and prevent integer overflow.
++ * The preprocessor guard addresses the "always false" warning
++ * from -Wtype-limits on platforms where
++ * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
++#if UINT_MAX >= SIZE_MAX
++ if ((unsigned)parser->m_attsSize > (size_t)(-1) / sizeof(ATTRIBUTE)) {
++ parser->m_attsSize = oldAttsSize;
++ return XML_ERROR_NO_MEMORY;
++ }
++#endif
++
+ temp = (ATTRIBUTE *)REALLOC(parser, (void *)parser->m_atts,
+ parser->m_attsSize * sizeof(ATTRIBUTE));
+ if (temp == NULL) {
+@@ -3102,6 +3299,17 @@
+ }
+ parser->m_atts = temp;
+ #ifdef XML_ATTR_INFO
++ /* Detect and prevent integer overflow.
++ * The preprocessor guard addresses the "always false" warning
++ * from -Wtype-limits on platforms where
++ * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
++# if UINT_MAX >= SIZE_MAX
++ if ((unsigned)parser->m_attsSize > (size_t)(-1) / sizeof(XML_AttrInfo)) {
++ parser->m_attsSize = oldAttsSize;
++ return XML_ERROR_NO_MEMORY;
++ }
++# endif
++
+ temp2 = (XML_AttrInfo *)REALLOC(parser, (void *)parser->m_attInfo,
+ parser->m_attsSize * sizeof(XML_AttrInfo));
+ if (temp2 == NULL) {
+@@ -3165,7 +3373,7 @@
+ /* 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);
+@@ -3240,8 +3448,16 @@
+ if (nPrefixes) {
+ int j; /* hash table index */
+ unsigned long version = parser->m_nsAttsVersion;
+- int nsAttsSize = (int)1 << parser->m_nsAttsPower;
+- unsigned char oldNsAttsPower = parser->m_nsAttsPower;
++ unsigned int nsAttsSize;
++ unsigned char oldNsAttsPower;
++
++ /* Detect and prevent invalid shift */
++ if (parser->m_nsAttsPower >= sizeof(unsigned int) * 8 /* bits per byte */) {
++ return XML_ERROR_NO_MEMORY;
++ }
++
++ nsAttsSize = 1u << parser->m_nsAttsPower;
++ oldNsAttsPower = parser->m_nsAttsPower;
+ /* size of hash table must be at least 2 * (# of prefixed attributes) */
+ if ((nPrefixes << 1)
+ >> parser->m_nsAttsPower) { /* true for m_nsAttsPower = 0 */
+@@ -3251,7 +3467,28 @@
+ ;
+ if (parser->m_nsAttsPower < 3)
+ parser->m_nsAttsPower = 3;
+- nsAttsSize = (int)1 << parser->m_nsAttsPower;
++
++ /* Detect and prevent invalid shift */
++ if (parser->m_nsAttsPower >= sizeof(nsAttsSize) * 8 /* bits per byte */) {
++ /* Restore actual size of memory in m_nsAtts */
++ parser->m_nsAttsPower = oldNsAttsPower;
++ return XML_ERROR_NO_MEMORY;
++ }
++
++ nsAttsSize = 1u << parser->m_nsAttsPower;
++
++ /* Detect and prevent integer overflow.
++ * The preprocessor guard addresses the "always false" warning
++ * from -Wtype-limits on platforms where
++ * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
++#if UINT_MAX >= SIZE_MAX
++ if (nsAttsSize > (size_t)(-1) / sizeof(NS_ATT)) {
++ /* Restore actual size of memory in m_nsAtts */
++ parser->m_nsAttsPower = oldNsAttsPower;
++ return XML_ERROR_NO_MEMORY;
++ }
++#endif
++
+ temp = (NS_ATT *)REALLOC(parser, parser->m_nsAtts,
+ nsAttsSize * sizeof(NS_ATT));
+ if (! temp) {
+@@ -3409,9 +3646,31 @@
+ tagNamePtr->prefixLen = prefixLen;
+ for (i = 0; localPart[i++];)
+ ; /* i includes null terminator */
++
++ /* Detect and prevent integer overflow */
++ if (binding->uriLen > INT_MAX - prefixLen
++ || i > INT_MAX - (binding->uriLen + prefixLen)) {
++ return XML_ERROR_NO_MEMORY;
++ }
++
+ n = i + binding->uriLen + prefixLen;
+ if (n > binding->uriAlloc) {
+ TAG *p;
++
++ /* Detect and prevent integer overflow */
++ if (n > INT_MAX - EXPAND_SPARE) {
++ return XML_ERROR_NO_MEMORY;
++ }
++ /* Detect and prevent integer overflow.
++ * The preprocessor guard addresses the "always false" warning
++ * from -Wtype-limits on platforms where
++ * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
++#if UINT_MAX >= SIZE_MAX
++ if ((unsigned)(n + EXPAND_SPARE) > (size_t)(-1) / sizeof(XML_Char)) {
++ return XML_ERROR_NO_MEMORY;
++ }
++#endif
++
+ uri = (XML_Char *)MALLOC(parser, (n + EXPAND_SPARE) * sizeof(XML_Char));
+ if (! uri)
+ return XML_ERROR_NO_MEMORY;
+@@ -3491,6 +3750,17 @@
+ if (! mustBeXML && isXMLNS
+ && (len > xmlnsLen || uri[len] != xmlnsNamespace[len]))
+ isXMLNS = XML_FALSE;
++
++ // NOTE: While Expat does not validate namespace URIs against RFC 3986,
++ // we have to at least make sure that the XML processor on top of
++ // Expat (that is splitting tag names by namespace separator into
++ // 2- or 3-tuples (uri-local or uri-local-prefix)) cannot be confused
++ // by an attacker putting additional namespace separator characters
++ // into namespace declarations. That would be ambiguous and not to
++ // be expected.
++ if (parser->m_ns && (uri[len] == parser->m_namespaceSeparator)) {
++ return XML_ERROR_SYNTAX;
++ }
+ }
+ isXML = isXML && len == xmlLen;
+ isXMLNS = isXMLNS && len == xmlnsLen;
+@@ -3507,7 +3777,24 @@
+ if (parser->m_freeBindingList) {
+ b = parser->m_freeBindingList;
+ if (len > b->uriAlloc) {
+- XML_Char *temp = (XML_Char *)REALLOC(
++
++ XML_Char *temp;
++ /* Detect and prevent integer overflow */
++ if (len > INT_MAX - EXPAND_SPARE) {
++ return XML_ERROR_NO_MEMORY;
++ }
++
++ /* Detect and prevent integer overflow.
++ * The preprocessor guard addresses the "always false" warning
++ * from -Wtype-limits on platforms where
++ * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
++#if UINT_MAX >= SIZE_MAX
++ if ((unsigned)(len + EXPAND_SPARE) > (size_t)(-1) / sizeof(XML_Char)) {
++ return XML_ERROR_NO_MEMORY;
++ }
++#endif
++
++ temp = (XML_Char *)REALLOC(
+ parser, b->uri, sizeof(XML_Char) * (len + EXPAND_SPARE));
+ if (temp == NULL)
+ return XML_ERROR_NO_MEMORY;
+@@ -3519,6 +3806,21 @@
+ b = (BINDING *)MALLOC(parser, sizeof(BINDING));
+ if (! b)
+ return XML_ERROR_NO_MEMORY;
++
++ /* Detect and prevent integer overflow */
++ if (len > INT_MAX - EXPAND_SPARE) {
++ return XML_ERROR_NO_MEMORY;
++ }
++ /* Detect and prevent integer overflow.
++ * The preprocessor guard addresses the "always false" warning
++ * from -Wtype-limits on platforms where
++ * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
++#if UINT_MAX >= SIZE_MAX
++ if ((unsigned)(len + EXPAND_SPARE) > (size_t)(-1) / sizeof(XML_Char)) {
++ return XML_ERROR_NO_MEMORY;
++ }
++#endif
++
+ b->uri
+ = (XML_Char *)MALLOC(parser, sizeof(XML_Char) * (len + EXPAND_SPARE));
+ if (! b->uri) {
+@@ -3556,7 +3858,7 @@
+ const char **endPtr) {
+ enum XML_Error result
+ = doCdataSection(parser, parser->m_encoding, &start, end, endPtr,
+- (XML_Bool)! parser->m_parsingStatus.finalBuffer);
++ (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_ACCOUNT_DIRECT);
+ if (result != XML_ERROR_NONE)
+ return result;
+ if (start) {
+@@ -3576,7 +3878,8 @@
+ */
+ 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;
+@@ -3594,6 +3897,12 @@
+ for (;;) {
+ const char *next = s; /* in case of XML_TOK_NONE or XML_TOK_PARTIAL */
+ int tok = XmlCdataSectionTok(enc, s, end, &next);
++#ifdef XML_DTD
++ if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, account)) {
++ accountingOnAbort(parser);
++ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
++ }
++#endif
+ *eventEndPP = next;
+ switch (tok) {
+ case XML_TOK_CDATA_SECT_CLOSE:
+@@ -3738,6 +4047,13 @@
+ *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:
+@@ -3822,6 +4138,15 @@
+ 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)) {
+@@ -3971,6 +4296,10 @@
+
+ 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) {
+@@ -3989,7 +4318,8 @@
+ 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);
+@@ -4016,6 +4346,14 @@
+ */
+ 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;
+ }
+@@ -4058,16 +4396,24 @@
+ }
+ /* 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
+@@ -4080,6 +4426,9 @@
+
+ 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;
+@@ -4097,7 +4446,7 @@
+ 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;
+ }
+@@ -4111,13 +4460,14 @@
+ 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 */
+@@ -4144,6 +4494,10 @@
+ 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;
+
+@@ -4208,6 +4562,19 @@
+ }
+ }
+ 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);
+@@ -4483,7 +4850,8 @@
+ 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);
+@@ -4516,8 +4884,9 @@
+ 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
+@@ -4776,7 +5145,13 @@
+ if (parser->m_prologState.level >= parser->m_groupSize) {
+ if (parser->m_groupSize) {
+ {
+- char *const new_connector = (char *)REALLOC(
++ char * new_connector;
++ /* Detect and prevent integer overflow */
++ if (parser->m_groupSize > (unsigned int)(-1) / 2u) {
++ return XML_ERROR_NO_MEMORY;
++ }
++
++ new_connector = (char *)REALLOC(
+ parser, parser->m_groupConnector, parser->m_groupSize *= 2);
+ if (new_connector == NULL) {
+ parser->m_groupSize /= 2;
+@@ -4786,7 +5161,18 @@
+ }
+
+ if (dtd->scaffIndex) {
+- int *const new_scaff_index = (int *)REALLOC(
++ int *new_scaff_index;
++ /* Detect and prevent integer overflow.
++ * The preprocessor guard addresses the "always false" warning
++ * from -Wtype-limits on platforms where
++ * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
++#if UINT_MAX >= SIZE_MAX
++ if (parser->m_groupSize > (size_t)(-1) / sizeof(int)) {
++ return XML_ERROR_NO_MEMORY;
++ }
++#endif
++
++ new_scaff_index = (int *)REALLOC(
+ parser, dtd->scaffIndex, parser->m_groupSize * sizeof(int));
+ if (new_scaff_index == NULL)
+ return XML_ERROR_NO_MEMORY;
+@@ -4907,12 +5293,15 @@
+ 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) {
+@@ -4991,7 +5380,7 @@
+ if (dtd->in_eldecl) {
+ ELEMENT_TYPE *el;
+ const XML_Char *name;
+- int nameLen;
++ size_t nameLen;
+ const char *nxt
+ = (quant == XML_CQUANT_NONE ? next : next - enc->minBytesPerChar);
+ int myindex = nextScaffoldPart(parser);
+@@ -5007,7 +5396,13 @@
+ nameLen = 0;
+ for (; name[nameLen++];)
+ ;
+- dtd->contentStringLen += nameLen;
++
++ /* Detect and prevent integer overflow */
++ if (nameLen > UINT_MAX - dtd->contentStringLen) {
++ return XML_ERROR_NO_MEMORY;
++ }
++
++ dtd->contentStringLen += (unsigned)nameLen;
+ if (parser->m_elementDeclHandler)
+ handleDefault = XML_FALSE;
+ }
+@@ -5110,6 +5505,13 @@
+ 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 */
+@@ -5183,6 +5585,9 @@
+ 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;
+@@ -5201,17 +5606,22 @@
+ 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 */
+@@ -5244,12 +5654,13 @@
+ 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;
+@@ -5258,6 +5669,9 @@
+ entity->processed = (int)(next - (const 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 */
+@@ -5271,7 +5685,8 @@
+ 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 */
+ {
+@@ -5279,7 +5694,8 @@
+ /* 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);
+ }
+ }
+
+@@ -5294,9 +5710,10 @@
+
+ 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)
+@@ -5308,11 +5725,23 @@
+
+ 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;
++ 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)) {
++ accountingOnAbort(parser);
++ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
++ }
++#endif
+ switch (tok) {
+ case XML_TOK_NONE:
+ return XML_ERROR_NONE;
+@@ -5372,6 +5801,14 @@
+ 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;
+@@ -5449,9 +5886,16 @@
+ enum XML_Error result;
+ const XML_Char *textEnd = entity->textPtr + entity->textLen;
+ entity->open = XML_TRUE;
++#ifdef XML_DTD
++ entityTrackingOnOpen(parser, entity, __LINE__);
++#endif
+ result = appendAttributeValue(parser, parser->m_internalEncoding,
+ isCdata, (const char *)entity->textPtr,
+- (const char *)textEnd, pool);
++ (const char *)textEnd, pool,
++ XML_ACCOUNT_ENTITY_EXPANSION);
++#ifdef XML_DTD
++ entityTrackingOnClose(parser, entity, __LINE__);
++#endif
+ entity->open = XML_FALSE;
+ if (result)
+ return result;
+@@ -5481,13 +5925,16 @@
+
+ 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
+@@ -5498,8 +5945,19 @@
+ }
+
+ 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
++ 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
+@@ -5535,13 +5993,16 @@
+ 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;
+@@ -5549,9 +6010,12 @@
+ dtd->keepProcessing = dtd->standalone;
+ } else {
+ entity->open = XML_TRUE;
++ entityTrackingOnOpen(parser, entity, __LINE__);
+ result = storeEntityValue(
+ parser, parser->m_internalEncoding, (const char *)entity->textPtr,
+- (const char *)(entity->textPtr + entity->textLen));
++ (const char *)(entity->textPtr + entity->textLen),
++ XML_ACCOUNT_ENTITY_EXPANSION);
++ entityTrackingOnClose(parser, entity, __LINE__);
+ entity->open = XML_FALSE;
+ if (result)
+ goto endEntityValue;
+@@ -5784,7 +6248,25 @@
+ }
+ } else {
+ DEFAULT_ATTRIBUTE *temp;
+- int count = type->allocDefaultAtts * 2;
++ int count;
++
++ /* Detect and prevent integer overflow */
++ if (type->allocDefaultAtts > INT_MAX / 2) {
++ return 0;
++ }
++
++ count = type->allocDefaultAtts * 2;
++
++ /* Detect and prevent integer overflow.
++ * The preprocessor guard addresses the "always false" warning
++ * from -Wtype-limits on platforms where
++ * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
++#if UINT_MAX >= SIZE_MAX
++ if ((unsigned)count > (size_t)(-1) / sizeof(DEFAULT_ATTRIBUTE)) {
++ return 0;
++ }
++#endif
++
+ temp = (DEFAULT_ATTRIBUTE *)REALLOC(parser, type->defaultAtts,
+ (count * sizeof(DEFAULT_ATTRIBUTE)));
+ if (temp == NULL)
+@@ -6435,10 +6917,26 @@
+ /* check for overflow (table is half full) */
+ if (table->used >> (table->power - 1)) {
+ unsigned char newPower = table->power + 1;
+- size_t newSize = (size_t)1 << newPower;
+- unsigned long newMask = (unsigned long)newSize - 1;
+- size_t tsize = newSize * sizeof(NAMED *);
+- NAMED **newV = (NAMED **)table->mem->malloc_fcn(tsize);
++ size_t newSize;
++ unsigned long newMask;
++ size_t tsize;
++ NAMED **newV;
++
++ /* Detect and prevent invalid shift */
++ if (newPower >= sizeof(unsigned long) * 8 /* bits per byte */) {
++ return NULL;
++ }
++
++ newSize = (size_t)1 << newPower;
++ newMask = (unsigned long)newSize - 1;
++
++ /* Detect and prevent integer overflow */
++ if (newSize > (size_t)(-1) / sizeof(NAMED *)) {
++ return NULL;
++ }
++
++ tsize = newSize * sizeof(NAMED *);
++ newV = (NAMED **)table->mem->malloc_fcn(tsize);
+ if (! newV)
+ return NULL;
+ memset(newV, 0, tsize);
+@@ -6786,6 +7284,20 @@
+ if (dtd->scaffCount >= dtd->scaffSize) {
+ CONTENT_SCAFFOLD *temp;
+ if (dtd->scaffold) {
++ /* Detect and prevent integer overflow */
++ if (dtd->scaffSize > UINT_MAX / 2u) {
++ return -1;
++ }
++ /* Detect and prevent integer overflow.
++ * The preprocessor guard addresses the "always false" warning
++ * from -Wtype-limits on platforms where
++ * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
++#if UINT_MAX >= SIZE_MAX
++ if (dtd->scaffSize > (size_t)(-1) / 2u / sizeof(CONTENT_SCAFFOLD)) {
++ return -1;
++ }
++#endif
++
+ temp = (CONTENT_SCAFFOLD *)REALLOC(
+ parser, dtd->scaffold, dtd->scaffSize * 2 * sizeof(CONTENT_SCAFFOLD));
+ if (temp == NULL)
+@@ -6817,55 +7329,130 @@
+ return next;
+ }
+
+-static void
+-build_node(XML_Parser parser, int src_node, XML_Content *dest,
+- XML_Content **contpos, XML_Char **strpos) {
+- DTD *const dtd = parser->m_dtd; /* save one level of indirection */
+- dest->type = dtd->scaffold[src_node].type;
+- dest->quant = dtd->scaffold[src_node].quant;
+- if (dest->type == XML_CTYPE_NAME) {
+- const XML_Char *src;
+- dest->name = *strpos;
+- src = dtd->scaffold[src_node].name;
+- for (;;) {
+- *(*strpos)++ = *src;
+- if (! *src)
+- break;
+- src++;
+- }
+- dest->numchildren = 0;
+- dest->children = NULL;
+- } else {
+- unsigned int i;
+- int cn;
+- dest->numchildren = dtd->scaffold[src_node].childcnt;
+- dest->children = *contpos;
+- *contpos += dest->numchildren;
+- for (i = 0, cn = dtd->scaffold[src_node].firstchild; i < dest->numchildren;
+- i++, cn = dtd->scaffold[cn].nextsib) {
+- build_node(parser, cn, &(dest->children[i]), contpos, strpos);
+- }
+- dest->name = NULL;
+- }
+-}
+-
+ static XML_Content *
+ build_model(XML_Parser parser) {
+ DTD *const dtd = parser->m_dtd; /* save one level of indirection */
+ XML_Content *ret;
+- XML_Content *cpos;
+- XML_Char *str;
+- int allocsize = (dtd->scaffCount * sizeof(XML_Content)
+- + (dtd->contentStringLen * sizeof(XML_Char)));
++ XML_Char *str; /* the current string writing location */
++ size_t allocsize;
++ XML_Content *dest; /* tree node writing location, moves upwards */
++ XML_Content *destLimit;
++ XML_Content *jobDest; /* next free writing location in target array */
++
++ /* Detect and prevent integer overflow.
++ * The preprocessor guard addresses the "always false" warning
++ * from -Wtype-limits on platforms where
++ * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
++#if UINT_MAX >= SIZE_MAX
++ if (dtd->scaffCount > (size_t)(-1) / sizeof(XML_Content)) {
++ return NULL;
++ }
++ if (dtd->contentStringLen > (size_t)(-1) / sizeof(XML_Char)) {
++ return NULL;
++ }
++#endif
++ if (dtd->scaffCount * sizeof(XML_Content)
++ > (size_t)(-1) - dtd->contentStringLen * sizeof(XML_Char)) {
++ return NULL;
++ }
++
++ allocsize = (dtd->scaffCount * sizeof(XML_Content)
++ + (dtd->contentStringLen * sizeof(XML_Char)));
+
+ ret = (XML_Content *)MALLOC(parser, allocsize);
+ if (! ret)
+ return NULL;
+
+- str = (XML_Char *)(&ret[dtd->scaffCount]);
+- cpos = &ret[1];
++ /* What follows is an iterative implementation (of what was previously done
++ * recursively in a dedicated function called "build_node". The old recursive
++ * build_node could be forced into stack exhaustion from input as small as a
++ * few megabyte, and so that was a security issue. Hence, a function call
++ * stack is avoided now by resolving recursion.)
++ *
++ * The iterative approach works as follows:
++ *
++ * - We have two writing pointers, both walking up the result array; one does
++ * the work, the other creates "jobs" for its colleague to do, and leads
++ * the way:
++ *
++ * - The faster one, pointer jobDest, always leads and writes "what job
++ * to do" by the other, once they reach that place in the
++ * array: leader "jobDest" stores the source node array index (relative
++ * to array dtd->scaffold) in field "numchildren".
++ *
++ * - The slower one, pointer dest, looks at the value stored in the
++ * "numchildren" field (which actually holds a source node array index
++ * at that time) and puts the real data from dtd->scaffold in.
++ *
++ * - Before the loop starts, jobDest writes source array index 0
++ * (where the root node is located) so that dest will have something to do
++ * when it starts operation.
++ *
++ * - Whenever nodes with children are encountered, jobDest appends
++ * them as new jobs, in order. As a result, tree node siblings are
++ * adjacent in the resulting array, for example:
++ *
++ * [0] root, has two children
++ * [1] first child of 0, has three children
++ * [3] first child of 1, does not have children
++ * [4] second child of 1, does not have children
++ * [5] third child of 1, does not have children
++ * [2] second child of 0, does not have children
++ *
++ * Or (the same data) presented in flat array view:
++ *
++ * [0] root, has two children
++ *
++ * [1] first child of 0, has three children
++ * [2] second child of 0, does not have children
++ *
++ * [3] first child of 1, does not have children
++ * [4] second child of 1, does not have children
++ * [5] third child of 1, does not have children
++ *
++ * - The algorithm repeats until all target array indices have been processed.
++ */
++ dest = ret; /* tree node writing location, moves upwards */
++ destLimit = &ret[dtd->scaffCount];
++ jobDest = ret; /* next free writing location in target array */
++ str = (XML_Char *)&ret[dtd->scaffCount];
++
++ /* Add the starting job, the root node (index 0) of the source tree */
++ (jobDest++)->numchildren = 0;
++
++ for (; dest < destLimit; dest++) {
++ /* Retrieve source tree array index from job storage */
++ const int src_node = (int)dest->numchildren;
++
++ /* Convert item */
++ dest->type = dtd->scaffold[src_node].type;
++ dest->quant = dtd->scaffold[src_node].quant;
++ if (dest->type == XML_CTYPE_NAME) {
++ const XML_Char *src;
++ dest->name = str;
++ src = dtd->scaffold[src_node].name;
++ for (;;) {
++ *str++ = *src;
++ if (! *src)
++ break;
++ src++;
++ }
++ dest->numchildren = 0;
++ dest->children = NULL;
++ } else {
++ unsigned int i;
++ int cn;
++ dest->name = NULL;
++ dest->numchildren = dtd->scaffold[src_node].childcnt;
++ dest->children = jobDest;
++
++ /* Append scaffold indices of children to array */
++ for (i = 0, cn = dtd->scaffold[src_node].firstchild;
++ i < dest->numchildren; i++, cn = dtd->scaffold[cn].nextsib)
++ (jobDest++)->numchildren = (unsigned int)cn;
++ }
++ }
+
+- build_node(parser, 0, ret, &cpos, &str);
+ return ret;
+ }
+
+@@ -6894,7 +7481,7 @@
+
+ static XML_Char *
+ copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) {
+- int charsRequired = 0;
++ size_t charsRequired = 0;
+ XML_Char *result;
+
+ /* First determine how long the string is */
+@@ -6912,3 +7499,766 @@
+ 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);
++ float amplificationFactor;
++ assert(! rootParser->m_parentParser);
++
++ if (rootParser->m_accounting.debugLevel < 1) {
++ return;
++ }
++
++ 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) {
++ const char ellipis[] = "[..]";
++ const size_t ellipsisLength = sizeof(ellipis) /* because compile-time */ - 1;
++ const unsigned int contextLength = 10;
++ const char *walker = before;
++
++ 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, "");
++
++ /* Note: Performance is of no concern here */
++ 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) {
++ unsigned int levelsAwayFromRootParser;
++ XML_Parser rootParser;
++ int isDirect;
++ ptrdiff_t bytesMore;
++ XmlBigCount * additionTarget;
++ XmlBigCount countBytesOutput;
++ float amplificationFactor;
++ XML_Bool tolerated;
++ /* Note: We need to check the token type *first* to be sure that
++ * we can even access variable <after>, safely.
++ * E.g. for XML_TOK_NONE <after> 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 */
++
++ rootParser
++ = getRootParserOf(originParser, &levelsAwayFromRootParser);
++ assert(! rootParser->m_parentParser);
++
++ isDirect
++ = (account == XML_ACCOUNT_DIRECT) && (originParser == rootParser);
++ bytesMore = after - before;
++
++ 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;
++
++ countBytesOutput
++ = rootParser->m_accounting.countBytesDirect
++ + rootParser->m_accounting.countBytesIndirect;
++ amplificationFactor
++ = accountingGetCurrentAmplification(rootParser);
++ 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;
++}
++
++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) {
++ const char * entityName;
++ assert(! rootParser->m_parentParser);
++ if (rootParser->m_entity_stats.debugLevel < 1)
++ return;
++
++# if defined(XML_UNICODE)
++ entityName = "[..]";
++# else
++ 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;
++}
++
++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);
++ const char * value;
++ char *afterValue;
++ unsigned long debugLevel;
++ if (valueOrNull == NULL) {
++ return defaultDebugLevel;
++ }
++ value = valueOrNull;
++
++ errno = 0;
++ afterValue = (char *)value;
++ debugLevel = strtoul(value, &afterValue, 10);
++ if ((errno != 0) || (afterValue[0] != '\0')) {
++ errno = 0;
++ return defaultDebugLevel;
++ }
++
++ return debugLevel;
++}
+diff -ru misc/expat-2.2.10/lib/xmltok.c misc/build/expat-2.2.10/lib/xmltok.c
+--- misc/expat-2.2.10/lib/xmltok.c 2020-09-25 19:47:39.000000000 +0200
++++ misc/build/expat-2.2.10/lib/xmltok.c 2022-03-05 12:25:27.583396678 +0100
+@@ -269,8 +269,14 @@
+
+ #define IS_NAME_CHAR(enc, p, n) (AS_NORMAL_ENCODING(enc)->isName##n(enc, p))
+ #define IS_NMSTRT_CHAR(enc, p, n) (AS_NORMAL_ENCODING(enc)->isNmstrt##n(enc, p))
+-#define IS_INVALID_CHAR(enc, p, n) \
+- (AS_NORMAL_ENCODING(enc)->isInvalid##n(enc, p))
++#ifdef XML_MIN_SIZE
++# define IS_INVALID_CHAR(enc, p, n) \
++ (AS_NORMAL_ENCODING(enc)->isInvalid##n \
++ && AS_NORMAL_ENCODING(enc)->isInvalid##n(enc, p))
++#else
++# define IS_INVALID_CHAR(enc, p, n) \
++ (AS_NORMAL_ENCODING(enc)->isInvalid##n(enc, p))
++#endif
+
+ #ifdef XML_MIN_SIZE
+ # define IS_NAME_CHAR_MINBPC(enc, p) \
+diff -ru misc/expat-2.2.10/lib/xmltok_impl.c misc/build/expat-2.2.10/lib/xmltok_impl.c
+--- misc/expat-2.2.10/lib/xmltok_impl.c 2020-09-25 19:47:39.000000000 +0200
++++ misc/build/expat-2.2.10/lib/xmltok_impl.c 2022-03-05 12:25:27.583396678 +0100
+@@ -61,7 +61,7 @@
+ case BT_LEAD##n: \
+ if (end - ptr < n) \
+ return XML_TOK_PARTIAL_CHAR; \
+- if (! IS_NAME_CHAR(enc, ptr, n)) { \
++ if (IS_INVALID_CHAR(enc, ptr, n) || ! IS_NAME_CHAR(enc, ptr, n)) { \
+ *nextTokPtr = ptr; \
+ return XML_TOK_INVALID; \
+ } \
+@@ -90,7 +90,7 @@
+ case BT_LEAD##n: \
+ if (end - ptr < n) \
+ return XML_TOK_PARTIAL_CHAR; \
+- if (! IS_NMSTRT_CHAR(enc, ptr, n)) { \
++ if (IS_INVALID_CHAR(enc, ptr, n) || ! IS_NMSTRT_CHAR(enc, ptr, n)) { \
+ *nextTokPtr = ptr; \
+ return XML_TOK_INVALID; \
+ } \
+@@ -1134,6 +1134,10 @@
+ case BT_LEAD##n: \
+ if (end - ptr < n) \
+ return XML_TOK_PARTIAL_CHAR; \
++ if (IS_INVALID_CHAR(enc, ptr, n)) { \
++ *nextTokPtr = ptr; \
++ return XML_TOK_INVALID; \
++ } \
+ if (IS_NMSTRT_CHAR(enc, ptr, n)) { \
+ ptr += n; \
+ tok = XML_TOK_NAME; \
+diff -ru misc/expat-2.2.10/lib/xmltok_ns.c misc/build/expat-2.2.10/lib/xmltok_ns.c
+--- misc/expat-2.2.10/lib/xmltok_ns.c 2020-09-25 19:47:39.000000000 +0200
++++ misc/build/expat-2.2.10/lib/xmltok_ns.c 2022-03-05 12:25:27.583396678 +0100
+@@ -89,7 +89,7 @@
+ static const ENCODING *
+ NS(findEncoding)(const ENCODING *enc, const char *ptr, const char *end) {
+ # define ENCODING_MAX 128
+- char buf[ENCODING_MAX];
++ char buf[ENCODING_MAX] = "";
+ char *p = buf;
+ int i;
+ XmlUtf8Convert(enc, &ptr, end, &p, p + ENCODING_MAX - 1);
+diff -ru misc/expat-2.2.10/tests/benchmark/Makefile.in misc/build/expat-2.2.10/tests/benchmark/Makefile.in
+--- misc/expat-2.2.10/tests/benchmark/Makefile.in 2020-10-03 17:37:06.000000000 +0200
++++ misc/build/expat-2.2.10/tests/benchmark/Makefile.in 2022-03-05 12:25:27.579396586 +0100
+@@ -1,7 +1,7 @@
+-# Makefile.in generated by automake 1.16.2 from Makefile.am.
++# Makefile.in generated by automake 1.16.1 from Makefile.am.
+ # @configure_input@
+
+-# Copyright (C) 1994-2020 Free Software Foundation, Inc.
++# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+
+ # This Makefile.in is free software; the Free Software Foundation
+ # gives unlimited permission to copy and/or distribute it,
+@@ -335,7 +335,6 @@
+ prefix = @prefix@
+ program_transform_name = @program_transform_name@
+ psdir = @psdir@
+-runstatedir = @runstatedir@
+ sbindir = @sbindir@
+ sharedstatedir = @sharedstatedir@
+ srcdir = @srcdir@
+diff -ru misc/expat-2.2.10/tests/Makefile.in misc/build/expat-2.2.10/tests/Makefile.in
+--- misc/expat-2.2.10/tests/Makefile.in 2020-10-03 17:37:06.000000000 +0200
++++ misc/build/expat-2.2.10/tests/Makefile.in 2022-03-05 12:25:27.579396586 +0100
+@@ -1,7 +1,7 @@
+-# Makefile.in generated by automake 1.16.2 from Makefile.am.
++# Makefile.in generated by automake 1.16.1 from Makefile.am.
+ # @configure_input@
+
+-# Copyright (C) 1994-2020 Free Software Foundation, Inc.
++# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+
+ # This Makefile.in is free software; the Free Software Foundation
+ # gives unlimited permission to copy and/or distribute it,
+@@ -616,7 +616,6 @@
+ prefix = @prefix@
+ program_transform_name = @program_transform_name@
+ psdir = @psdir@
+-runstatedir = @runstatedir@
+ sbindir = @sbindir@
+ sharedstatedir = @sharedstatedir@
+ srcdir = @srcdir@
+diff -ru misc/expat-2.2.10/tests/minicheck.c misc/build/expat-2.2.10/tests/minicheck.c
+--- misc/expat-2.2.10/tests/minicheck.c 2020-09-25 19:47:39.000000000 +0200
++++ misc/build/expat-2.2.10/tests/minicheck.c 2022-03-05 12:25:27.587396769 +0100
+@@ -141,11 +141,18 @@
+ }
+
+ static void
+-add_failure(SRunner *runner, int verbosity) {
+- runner->nfailures++;
++handle_success(int verbosity) {
+ if (verbosity >= CK_VERBOSE) {
+- printf("%s:%d: %s\n", _check_current_filename, _check_current_lineno,
+- _check_current_function);
++ printf("PASS: %s\n", _check_current_function);
++ }
++}
++
++static void
++handle_failure(SRunner *runner, int verbosity, const char *phase_info) {
++ runner->nfailures++;
++ if (verbosity != CK_SILENT) {
++ printf("FAIL: %s (%s at %s:%d)\n", _check_current_function, phase_info,
++ _check_current_filename, _check_current_lineno);
+ }
+ }
+
+@@ -164,14 +171,14 @@
+ if (tc->setup != NULL) {
+ /* setup */
+ if (setjmp(env)) {
+- add_failure(runner, verbosity);
++ handle_failure(runner, verbosity, "during setup");
+ continue;
+ }
+ tc->setup();
+ }
+ /* test */
+ if (setjmp(env)) {
+- add_failure(runner, verbosity);
++ handle_failure(runner, verbosity, "during actual test");
+ continue;
+ }
+ (tc->tests[i])();
+@@ -179,15 +186,17 @@
+ /* teardown */
+ if (tc->teardown != NULL) {
+ if (setjmp(env)) {
+- add_failure(runner, verbosity);
++ handle_failure(runner, verbosity, "during teardown");
+ continue;
+ }
+ tc->teardown();
+ }
++
++ handle_success(verbosity);
+ }
+ tc = tc->next_tcase;
+ }
+- if (verbosity) {
++ if (verbosity != CK_SILENT) {
+ int passed = runner->nchecks - runner->nfailures;
+ double percentage = ((double)passed) / runner->nchecks;
+ int display = (int)(percentage * 100);
+@@ -203,8 +212,8 @@
+ it is.
+ */
+ UNUSED_P(condition);
+- UNUSED_P(file);
+- UNUSED_P(line);
++ _check_current_filename = file;
++ _check_current_lineno = line;
+ if (msg != NULL) {
+ const int has_newline = (msg[strlen(msg) - 1] == '\n');
+ fprintf(stderr, "ERROR: %s%s", msg, has_newline ? "" : "\n");
+diff -ru misc/expat-2.2.10/tests/runtests.c misc/build/expat-2.2.10/tests/runtests.c
+--- misc/expat-2.2.10/tests/runtests.c 2020-10-03 17:14:57.000000000 +0200
++++ misc/build/expat-2.2.10/tests/runtests.c 2022-03-05 12:25:27.583396678 +0100
+@@ -45,6 +45,7 @@
+ #include <stddef.h> /* ptrdiff_t */
+ #include <ctype.h>
+ #include <limits.h>
++#include <math.h> /* NAN, INFINITY, isnan */
+
+ #if defined(_WIN32) && defined(_MSC_VER) && (_MSC_VER < 1600)
+ /* For vs2003/7.1 up to vs2008/9.0; _MSC_VER 1600 is vs2010/10.0 */
+@@ -72,7 +73,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"
+@@ -11231,6 +11232,381 @@
+ }
+ END_TEST
+
++#if defined(XML_DTD)
++typedef enum XML_Status (*XmlParseFunction)(XML_Parser, const char *, int, int);
++
++struct AccountingTestCase {
++ const char *primaryText;
++ const char *firstExternalText; /* often NULL */
++ const char *secondExternalText; /* often NULL */
++ const unsigned long long expectedCountBytesIndirectExtra;
++ XML_Bool singleBytesWanted;
++};
++
++static int
++accounting_external_entity_ref_handler(XML_Parser parser,
++ const XML_Char *context,
++ const XML_Char *base,
++ const XML_Char *systemId,
++ const XML_Char *publicId) {
++ UNUSED_P(context);
++ UNUSED_P(base);
++ UNUSED_P(publicId);
++
++ const struct AccountingTestCase *const testCase
++ = (const struct AccountingTestCase *)XML_GetUserData(parser);
++
++ const char *externalText = NULL;
++ if (xcstrcmp(systemId, XCS("first.ent")) == 0) {
++ externalText = testCase->firstExternalText;
++ } else if (xcstrcmp(systemId, XCS("second.ent")) == 0) {
++ externalText = testCase->secondExternalText;
++ } else {
++ assert(! "systemId is neither \"first.ent\" nor \"second.ent\"");
++ }
++ assert(externalText);
++
++ XML_Parser entParser = XML_ExternalEntityParserCreate(parser, context, 0);
++ assert(entParser);
++
++ const XmlParseFunction xmlParseFunction
++ = testCase->singleBytesWanted ? _XML_Parse_SINGLE_BYTES : XML_Parse;
++
++ const enum XML_Status status = xmlParseFunction(
++ entParser, externalText, (int)strlen(externalText), XML_TRUE);
++
++ XML_ParserFree(entParser);
++ return status;
++}
++
++START_TEST(test_accounting_precision) {
++ const XML_Bool filled_later = XML_TRUE; /* value is arbitrary */
++ struct AccountingTestCase cases[] = {
++ {"<e/>", NULL, NULL, 0, 0},
++ {"<e></e>", NULL, NULL, 0, 0},
++
++ /* Attributes */
++ {"<e k1=\"v2\" k2=\"v2\"/>", NULL, NULL, 0, filled_later},
++ {"<e k1=\"v2\" k2=\"v2\"></e>", NULL, NULL, 0, 0},
++ {"<p:e xmlns:p=\"https://domain.invalid/\" />", NULL, NULL, 0,
++ filled_later},
++ {"<e k=\"&'><"\" />", NULL, NULL,
++ sizeof(XML_Char) * 5 /* number of predefined entites */, filled_later},
++ {"<e1 xmlns='https://example.org/'>\n"
++ " <e2 xmlns=''/>\n"
++ "</e1>",
++ NULL, NULL, 0, filled_later},
++
++ /* Text */
++ {"<e>text</e>", NULL, NULL, 0, filled_later},
++ {"<e1><e2>text1<e3/>text2</e2></e1>", NULL, NULL, 0, filled_later},
++ {"<e>&'><"</e>", NULL, NULL,
++ sizeof(XML_Char) * 5 /* number of predefined entites */, filled_later},
++ {"<e>A)</e>", NULL, NULL, 0, filled_later},
++
++ /* Prolog */
++ {"<?xml version=\"1.0\"?><root/>", NULL, NULL, 0, filled_later},
++
++ /* Whitespace */
++ {" <e1> <e2> </e2> </e1> ", NULL, NULL, 0, filled_later},
++ {"<e1 ><e2 /></e1 >", NULL, NULL, 0, filled_later},
++ {"<e1><e2 k = \"v\"/><e3 k = 'v'/></e1>", NULL, NULL, 0, filled_later},
++
++ /* Comments */
++ {"<!-- Comment --><e><!-- Comment --></e>", NULL, NULL, 0, filled_later},
++
++ /* Processing instructions */
++ {"<?xml-stylesheet type=\"text/xsl\" href=\"https://domain.invalid/\" media=\"all\"?><e/>",
++ NULL, NULL, 0, filled_later},
++ {"<?pi0?><?pi1 ?><?pi2 ?><!DOCTYPE r SYSTEM 'first.ent'><r/>",
++ "<?pi3?><!ENTITY % e1 SYSTEM 'second.ent'><?pi4?>%e1;<?pi5?>", "<?pi6?>",
++ 0, filled_later},
++
++ /* CDATA */
++ {"<e><![CDATA[one two three]]></e>", NULL, NULL, 0, filled_later},
++
++ /* Conditional sections */
++ {"<!DOCTYPE r [\n"
++ "<!ENTITY % draft 'INCLUDE'>\n"
++ "<!ENTITY % final 'IGNORE'>\n"
++ "<!ENTITY % import SYSTEM \"first.ent\">\n"
++ "%import;\n"
++ "]>\n"
++ "<r/>\n",
++ "<![%draft;[<!--1-->]]>\n"
++ "<![%final;[<!--22-->]]>",
++ NULL, sizeof(XML_Char) * (strlen("INCLUDE") + strlen("IGNORE")),
++ filled_later},
++
++ /* General entities */
++ {"<!DOCTYPE root [\n"
++ "<!ENTITY nine \"123456789\">\n"
++ "]>\n"
++ "<root>&nine;</root>",
++ NULL, NULL, sizeof(XML_Char) * strlen("123456789"), filled_later},
++ {"<!DOCTYPE root [\n"
++ "<!ENTITY nine \"123456789\">\n"
++ "]>\n"
++ "<root k1=\"&nine;\"/>",
++ NULL, NULL, sizeof(XML_Char) * strlen("123456789"), filled_later},
++ {"<!DOCTYPE root [\n"
++ "<!ENTITY nine \"123456789\">\n"
++ "<!ENTITY nine2 \"&nine;&nine;\">\n"
++ "]>\n"
++ "<root>&nine2;&nine2;&nine2;</root>",
++ NULL, NULL,
++ sizeof(XML_Char) * 3 /* calls to &nine2; */ * 2 /* calls to &nine; */
++ * (strlen("&nine;") + strlen("123456789")),
++ filled_later},
++ {"<!DOCTYPE r [\n"
++ " <!ENTITY five SYSTEM 'first.ent'>\n"
++ "]>\n"
++ "<r>&five;</r>",
++ "12345", NULL, 0, filled_later},
++
++ /* Parameter entities */
++ {"<!DOCTYPE r [\n"
++ "<!ENTITY % comment \"<!---->\">\n"
++ "%comment;\n"
++ "]>\n"
++ "<r/>",
++ NULL, NULL, sizeof(XML_Char) * strlen("<!---->"), filled_later},
++ {"<!DOCTYPE r [\n"
++ "<!ENTITY % ninedef \"<!ENTITY nine "123456789">\">\n"
++ "%ninedef;\n"
++ "]>\n"
++ "<r>&nine;</r>",
++ NULL, NULL,
++ sizeof(XML_Char)
++ * (strlen("<!ENTITY nine \"123456789\">") + strlen("123456789")),
++ filled_later},
++ {"<!DOCTYPE r [\n"
++ "<!ENTITY % comment \"<!--1-->\">\n"
++ "<!ENTITY % comment2 \"%comment;<!--22-->%comment;\">\n"
++ "%comment2;\n"
++ "]>\n"
++ "<r/>\n",
++ NULL, NULL,
++ sizeof(XML_Char)
++ * (strlen("%comment;<!--22-->%comment;") + 2 * strlen("<!--1-->")),
++ filled_later},
++ {"<!DOCTYPE r [\n"
++ " <!ENTITY % five \"12345\">\n"
++ " <!ENTITY % five2def \"<!ENTITY five2 "[%five;][%five;]]]]">\">\n"
++ " %five2def;\n"
++ "]>\n"
++ "<r>&five2;</r>",
++ NULL, NULL, /* from "%five2def;": */
++ sizeof(XML_Char)
++ * (strlen("<!ENTITY five2 \"[%five;][%five;]]]]\">")
++ + 2 /* calls to "%five;" */ * strlen("12345")
++ + /* from "&five2;": */ strlen("[12345][12345]]]]")),
++ filled_later},
++ {"<!DOCTYPE r SYSTEM \"first.ent\">\n"
++ "<r/>",
++ "<!ENTITY % comment '<!--1-->'>\n"
++ "<!ENTITY % comment2 '<!--22-->%comment;<!--22-->%comment;<!--22-->'>\n"
++ "%comment2;",
++ NULL,
++ sizeof(XML_Char)
++ * (strlen("<!--22-->%comment;<!--22-->%comment;<!--22-->")
++ + 2 /* calls to "%comment;" */ * strlen("<!---->")),
++ filled_later},
++ {"<!DOCTYPE r SYSTEM 'first.ent'>\n"
++ "<r/>",
++ "<!ENTITY % e1 PUBLIC 'foo' 'second.ent'>\n"
++ "<!ENTITY % e2 '<!--22-->%e1;<!--22-->'>\n"
++ "%e2;\n",
++ "<!--1-->", sizeof(XML_Char) * strlen("<!--22--><!--1--><!--22-->"),
++ filled_later},
++ {
++ "<!DOCTYPE r SYSTEM 'first.ent'>\n"
++ "<r/>",
++ "<!ENTITY % e1 SYSTEM 'second.ent'>\n"
++ "<!ENTITY % e2 '%e1;'>",
++ "<?xml version='1.0' encoding='utf-8'?>\n"
++ "hello\n"
++ "xml" /* without trailing newline! */,
++ 0,
++ filled_later,
++ },
++ {
++ "<!DOCTYPE r SYSTEM 'first.ent'>\n"
++ "<r/>",
++ "<!ENTITY % e1 SYSTEM 'second.ent'>\n"
++ "<!ENTITY % e2 '%e1;'>",
++ "<?xml version='1.0' encoding='utf-8'?>\n"
++ "hello\n"
++ "xml\n" /* with trailing newline! */,
++ 0,
++ filled_later,
++ },
++ {"<!DOCTYPE doc SYSTEM 'first.ent'>\n"
++ "<doc></doc>\n",
++ "<!ELEMENT doc EMPTY>\n"
++ "<!ENTITY % e1 SYSTEM 'second.ent'>\n"
++ "<!ENTITY % e2 '%e1;'>\n"
++ "%e1;\n",
++ "\xEF\xBB\xBF<!ATTLIST doc a1 CDATA 'value'>" /* UTF-8 BOM */,
++ strlen("\xEF\xBB\xBF<!ATTLIST doc a1 CDATA 'value'>"), filled_later},
++ {"<!DOCTYPE r [\n"
++ " <!ENTITY five SYSTEM 'first.ent'>\n"
++ "]>\n"
++ "<r>&five;</r>",
++ "\xEF\xBB\xBF" /* UTF-8 BOM */, NULL, 0, filled_later},
++ };
++
++ const size_t countCases = sizeof(cases) / sizeof(cases[0]);
++ size_t u = 0;
++ for (; u < countCases; u++) {
++ size_t v = 0;
++ for (; v < 2; v++) {
++ const XML_Bool singleBytesWanted = (v == 0) ? XML_FALSE : XML_TRUE;
++ const unsigned long long expectedCountBytesDirect
++ = strlen(cases[u].primaryText);
++ const unsigned long long expectedCountBytesIndirect
++ = (cases[u].firstExternalText ? strlen(cases[u].firstExternalText)
++ : 0)
++ + (cases[u].secondExternalText ? strlen(cases[u].secondExternalText)
++ : 0)
++ + cases[u].expectedCountBytesIndirectExtra;
++
++ XML_Parser parser = XML_ParserCreate(NULL);
++ XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
++ if (cases[u].firstExternalText) {
++ XML_SetExternalEntityRefHandler(parser,
++ accounting_external_entity_ref_handler);
++ XML_SetUserData(parser, (void *)&cases[u]);
++ cases[u].singleBytesWanted = singleBytesWanted;
++ }
++
++ const XmlParseFunction xmlParseFunction
++ = singleBytesWanted ? _XML_Parse_SINGLE_BYTES : XML_Parse;
++
++ enum XML_Status status
++ = xmlParseFunction(parser, cases[u].primaryText,
++ (int)strlen(cases[u].primaryText), XML_TRUE);
++ if (status != XML_STATUS_OK) {
++ _xml_failure(parser, __FILE__, __LINE__);
++ }
++
++ const unsigned long long actualCountBytesDirect
++ = testingAccountingGetCountBytesDirect(parser);
++ const unsigned long long actualCountBytesIndirect
++ = testingAccountingGetCountBytesIndirect(parser);
++
++ XML_ParserFree(parser);
++
++ if (actualCountBytesDirect != expectedCountBytesDirect) {
++ fprintf(
++ stderr,
++ "Document " EXPAT_FMT_SIZE_T("") " of " EXPAT_FMT_SIZE_T("") ", %s: Expected " EXPAT_FMT_ULL(
++ "") " count direct bytes, got " EXPAT_FMT_ULL("") " instead.\n",
++ u + 1, countCases, singleBytesWanted ? "single bytes" : "chunks",
++ expectedCountBytesDirect, actualCountBytesDirect);
++ fail("Count of direct bytes is off");
++ }
++
++ if (actualCountBytesIndirect != expectedCountBytesIndirect) {
++ fprintf(
++ stderr,
++ "Document " EXPAT_FMT_SIZE_T("") " of " EXPAT_FMT_SIZE_T("") ", %s: Expected " EXPAT_FMT_ULL(
++ "") " count indirect bytes, got " EXPAT_FMT_ULL("") " instead.\n",
++ u + 1, countCases, singleBytesWanted ? "single bytes" : "chunks",
++ expectedCountBytesIndirect, actualCountBytesIndirect);
++ fail("Count of indirect bytes is off");
++ }
++ }
++ }
++}
++END_TEST
++
++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
++
++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 *
+ make_suite(void) {
+ Suite *s = suite_create("basic");
+@@ -11239,6 +11615,9 @@
+ 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);
+@@ -11603,6 +11982,13 @@
+ 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);
++ 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;
+ }
+
+diff -ru misc/expat-2.2.10/xmlwf/Makefile.in misc/build/expat-2.2.10/xmlwf/Makefile.in
+--- misc/expat-2.2.10/xmlwf/Makefile.in 2020-10-03 17:37:06.000000000 +0200
++++ misc/build/expat-2.2.10/xmlwf/Makefile.in 2022-03-05 12:25:27.583396678 +0100
+@@ -1,7 +1,7 @@
+-# Makefile.in generated by automake 1.16.2 from Makefile.am.
++# Makefile.in generated by automake 1.16.1 from Makefile.am.
+ # @configure_input@
+
+-# Copyright (C) 1994-2020 Free Software Foundation, Inc.
++# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+
+ # This Makefile.in is free software; the Free Software Foundation
+ # gives unlimited permission to copy and/or distribute it,
+@@ -344,7 +344,6 @@
+ prefix = @prefix@
+ program_transform_name = @program_transform_name@
+ psdir = @psdir@
+-runstatedir = @runstatedir@
+ sbindir = @sbindir@
+ sharedstatedir = @sharedstatedir@
+ srcdir = @srcdir@
+diff -ru misc/expat-2.2.10/xmlwf/xmltchar.h misc/build/expat-2.2.10/xmlwf/xmltchar.h
+--- misc/expat-2.2.10/xmlwf/xmltchar.h 2020-09-25 19:47:39.000000000 +0200
++++ misc/build/expat-2.2.10/xmlwf/xmltchar.h 2022-03-05 12:25:27.583396678 +0100
+@@ -54,6 +54,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
+@@ -71,4 +73,6 @@
+ # define tmain main
+ # define tremove remove
+ # define tchar char
++# define tcstof strtof
++# define tcstoull strtoull
+ #endif /* not XML_UNICODE */
+diff -ru misc/expat-2.2.10/xmlwf/xmlwf.c misc/build/expat-2.2.10/xmlwf/xmlwf.c
+--- misc/expat-2.2.10/xmlwf/xmlwf.c 2020-09-25 19:47:39.000000000 +0200
++++ misc/build/expat-2.2.10/xmlwf/xmlwf.c 2022-03-05 12:25:27.583396678 +0100
+@@ -30,11 +30,15 @@
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
++#include <expat_config.h>
++
+ #include <assert.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <stddef.h>
+ #include <string.h>
++#include <math.h> /* for isnan */
++#include <errno.h>
+
+ #include "expat.h"
+ #include "codepage.h"
+@@ -50,6 +54,14 @@
+ # include <wchar.h>
+ #endif
+
++enum ExitCode {
++ XMLWF_EXIT_SUCCESS = 0,
++ XMLWF_EXIT_INTERNAL_ERROR = 1,
++ XMLWF_EXIT_NOT_WELLFORMED = 2,
++ XMLWF_EXIT_OUTPUT_ERROR = 3,
++ XMLWF_EXIT_USAGE_ERROR = 4,
++};
++
+ /* Structures for handler user data */
+ typedef struct NotationList {
+ struct NotationList *next;
+@@ -875,6 +887,12 @@
+ 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")
+@@ -891,6 +909,19 @@
+ 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], XMLWF_EXIT_USAGE_ERROR); \
++ constCharStarTarget = argv[i]; \
++ } else { \
++ constCharStarTarget = argv[i] + j + 1; \
++ } \
++ i++; \
++ j = 0; \
++ }
++
+ int
+ tmain(int argc, XML_Char **argv) {
+ int i, j;
+@@ -902,6 +933,11 @@
+ 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;
+@@ -990,6 +1026,49 @@
+ 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(XMLWF_EXIT_USAGE_ERROR);
++ }
++#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(XMLWF_EXIT_USAGE_ERROR);
++ }
++ attackThresholdGiven = XML_TRUE;
++#ifndef XML_DTD
++ ftprintf(stderr, T("Warning: Given attack threshold ignored") T(
++ ", xmlwf has been compiled without DTD support.\n"));
++#endif
++ break;
++ }
+ case T('\0'):
+ if (j > 1) {
+ i++;
+@@ -1020,6 +1099,19 @@
+ 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 -ru misc/expat-2.2.10/xmlwf/xmlwf_helpgen.py misc/build/expat-2.2.10/xmlwf/xmlwf_helpgen.py
+--- misc/expat-2.2.10/xmlwf/xmlwf_helpgen.py 2020-09-25 19:47:39.000000000 +0200
++++ misc/build/expat-2.2.10/xmlwf/xmlwf_helpgen.py 2022-03-05 12:25:27.583396678 +0100
+@@ -57,6 +57,14 @@
+ 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='file to process (default: STDIN)')
+
+ info = parser.add_argument_group('info arguments')
diff --git a/main/expat/expat-2.2.11.patch b/main/expat/expat-2.2.11.patch
deleted file mode 100644
index 289362d..0000000
--- a/main/expat/expat-2.2.11.patch
+++ /dev/null
@@ -1,2745 +0,0 @@
---- misc/expat-2.2.10/Changes 2020-10-03 11:14:57.000000000 -0400
-+++ misc/build/expat-2.2.10/Changes 2021-07-18 17:21:48.000000000 -0400
-@@ -2,6 +2,43 @@
- https://github.com/libexpat/libexpat/labels/help%20wanted
- If you can help, please get in touch. Thanks!
-
-+
-+Release 2.2.11 XXX XXXXX XX XXXX
-+ Security fixes:
-+ #34 #466 CVE-2013-0340/CWE-776 -- Protect against billion laughs attacks
-+ (denial-of-service; flavors targeting CPU time or RAM or both,
-+ leveraging general entities or parameter entities or both)
-+ by tracking and limiting the input amplification factor
-+ (<amplification> := (<direct> + <indirect>) / <direct>).
-+ By conservative default, amplification up to a factor of 100.0
-+ is tolerated and rejection only starts after 8 MiB of output bytes
-+ (=<direct> + <indirect>) have been processed.
-+ A new error code XML_ERROR_AMPLIFICATION_LIMIT_BREACH signals
-+ this condition.
-+ 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.
-+
-+ Special thanks to:
-+ Nick Wellnhofer
-+ Yury Gribov
-+ and
-+ Clang LeakSan
-+ JetBrains
-+
- Release 2.2.10 Sat October 3 2020
- Bug fixes:
- #390 #395 #398 Fix undefined behavior during parsing caused by
-diff -ru misc/expat-2.2.10/CMakeLists.txt misc/build/expat-2.2.10/CMakeLists.txt
---- misc/expat-2.2.10/CMakeLists.txt 2020-10-03 11:14:57.000000000 -0400
-+++ misc/build/expat-2.2.10/CMakeLists.txt 2021-08-25 18:35:36.000000000 -0400
-@@ -448,14 +448,12 @@
- endif()
- endfunction()
-
-- add_executable(runtests tests/runtests.c ${test_SRCS})
-+ add_executable(runtests tests/runtests.c ${test_SRCS} ${expat_SRCS})
- set_property(TARGET runtests PROPERTY RUNTIME_OUTPUT_DIRECTORY tests)
-- target_link_libraries(runtests expat)
- expat_add_test(runtests $<TARGET_FILE:runtests>)
-
-- add_executable(runtestspp tests/runtestspp.cpp ${test_SRCS})
-+ add_executable(runtestspp tests/runtestspp.cpp ${test_SRCS} ${expat_SRCS})
- set_property(TARGET runtestspp PROPERTY RUNTIME_OUTPUT_DIRECTORY tests)
-- target_link_libraries(runtestspp expat)
- expat_add_test(runtestspp $<TARGET_FILE:runtestspp>)
- endif()
-
-diff -ru misc/expat-2.2.10/doc/Makefile.in misc/build/expat-2.2.10/doc/Makefile.in
---- misc/expat-2.2.10/doc/Makefile.in 2020-10-03 11:37:06.000000000 -0400
-+++ misc/build/expat-2.2.10/doc/Makefile.in 2021-07-18 18:17:02.000000000 -0400
-@@ -1,7 +1,7 @@
--# Makefile.in generated by automake 1.16.2 from Makefile.am.
-+# Makefile.in generated by automake 1.16.1 from Makefile.am.
- # @configure_input@
-
--# Copyright (C) 1994-2020 Free Software Foundation, Inc.
-+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
-
- # This Makefile.in is free software; the Free Software Foundation
- # gives unlimited permission to copy and/or distribute it,
-@@ -314,7 +314,6 @@
- prefix = @prefix@
- program_transform_name = @program_transform_name@
- psdir = @psdir@
--runstatedir = @runstatedir@
- sbindir = @sbindir@
- sharedstatedir = @sharedstatedir@
- srcdir = @srcdir@
-diff -ru misc/expat-2.2.10/doc/reference.html misc/build/expat-2.2.10/doc/reference.html
---- misc/expat-2.2.10/doc/reference.html 2020-09-25 13:47:39.000000000 -0400
-+++ misc/build/expat-2.2.10/doc/reference.html 2021-07-18 17:21:48.000000000 -0400
-@@ -120,6 +120,13 @@
- <li><a href="#XML_GetInputContext">XML_GetInputContext</a></li>
- </ul>
- </li>
-+ <li>
-+ <a href="#billion-laughs">Billion Laughs Attack Protection</a>
-+ <ul>
-+ <li><a href="#XML_SetBillionLaughsAttackProtectionMaximumAmplification">XML_SetBillionLaughsAttackProtectionMaximumAmplification</a></li>
-+ <li><a href="#XML_SetBillionLaughsAttackProtectionActivationThreshold">XML_SetBillionLaughsAttackProtectionActivationThreshold</a></li>
-+ </ul>
-+ </li>
- <li><a href="#miscellaneous">Miscellaneous Functions</a>
- <ul>
- <li><a href="#XML_SetUserData">XML_SetUserData</a></li>
-@@ -1998,6 +2005,98 @@
- return NULL.</p>
- </div>
-
-+
-+
-+<h3><a name="billion-laughs">Billion Laughs Attack Protection</a></h3>
-+
-+<p>The functions in this section configure the built-in
-+ protection against various forms of
-+ <a href="https://en.wikipedia.org/wiki/Billion_laughs_attack">billion laughs attacks</a>.</p>
-+
-+<pre class="fcndec" id="XML_SetBillionLaughsAttackProtectionMaximumAmplification">
-+/* Added in Expat 2.2.11. */
-+XML_Bool XMLCALL
-+XML_SetBillionLaughsAttackProtectionMaximumAmplification(XML_Parser p,
-+ float maximumAmplificationFactor);
-+</pre>
-+<div class="fcndef">
-+ <p>
-+ Sets the maximum tolerated amplification factor
-+ for protection against
-+ <a href="https://en.wikipedia.org/wiki/Billion_laughs_attack">billion laughs attacks</a>
-+ (default: <code>100.0</code>)
-+ of parser <code>p</code> to <code>maximumAmplificationFactor</code>, and
-+ returns <code>XML_TRUE</code> upon success and <code>XML_TRUE</code> upon error.
-+ </p>
-+
-+ The amplification factor is calculated as ..
-+ <pre>
-+ amplification := (direct + indirect) / direct
-+ </pre>
-+ .. while parsing, whereas
-+ <code>direct</code> is the number of bytes read from the primary document in parsing and
-+ <code>indirect</code> is the number of bytes added by expanding entities and reading of external DTD files, combined.
-+
-+ <p>For a call to <code>XML_SetBillionLaughsAttackProtectionMaximumAmplification</code> to succeed:</p>
-+ <ul>
-+ <li>parser <code>p</code> must be a non-<code>NULL</code> root parser (without any parent parsers) and</li>
-+ <li><code>maximumAmplificationFactor</code> must be non-<code>NaN</code> and greater than or equal to <code>1.0</code>.</li>
-+ </ul>
-+
-+ <p>
-+ <strong>Note:</strong>
-+ If you ever need to increase this value for non-attack payload,
-+ please <a href="https://github.com/libexpat/libexpat/issues">file a bug report</a>.
-+ </p>
-+
-+ <p>
-+ <strong>Note:</strong>
-+ Peak amplifications
-+ of factor 15,000 for the entire payload and
-+ of factor 30,000 in the middle of parsing
-+ have been observed with small benign files in practice.
-+
-+ So if you do reduce the maximum allowed amplification,
-+ please make sure that the activation threshold is still big enough
-+ to not end up with undesired false positives (i.e. benign files being rejected).
-+ </p>
-+</div>
-+
-+<pre class="fcndec" id="XML_SetBillionLaughsAttackProtectionActivationThreshold">
-+/* Added in Expat 2.2.11. */
-+XML_Bool XMLCALL
-+XML_SetBillionLaughsAttackProtectionActivationThreshold(XML_Parser p,
-+ unsigned long long activationThresholdBytes);
-+</pre>
-+<div class="fcndef">
-+ <p>
-+ Sets number of output bytes (including amplification from entity expansion and reading DTD files)
-+ needed to activate protection against
-+ <a href="https://en.wikipedia.org/wiki/Billion_laughs_attack">billion laughs attacks</a>
-+ (default: <code>8 MiB</code>)
-+ of parser <code>p</code> to <code>activationThresholdBytes</code>, and
-+ returns <code>XML_TRUE</code> upon success and <code>XML_TRUE</code> upon error.
-+ </p>
-+
-+ <p>For a call to <code>XML_SetBillionLaughsAttackProtectionActivationThreshold</code> to succeed:</p>
-+ <ul>
-+ <li>parser <code>p</code> must be a non-<code>NULL</code> root parser (without any parent parsers).</li>
-+ </ul>
-+
-+ <p>
-+ <strong>Note:</strong>
-+ If you ever need to increase this value for non-attack payload,
-+ please <a href="https://github.com/libexpat/libexpat/issues">file a bug report</a>.
-+ </p>
-+
-+ <p>
-+ <strong>Note:</strong>
-+ Activation thresholds below 4 MiB are known to break support for
-+ <a href="https://en.wikipedia.org/wiki/Darwin_Information_Typing_Architecture">DITA</a> 1.3 payload
-+ and are hence not recommended.
-+ </p>
-+</div>
-+
- <h3><a name="miscellaneous">Miscellaneous functions</a></h3>
-
- <p>The functions in this section either obtain state information from
-Only in misc/expat-2.2.10/doc: xmlwf.1
-diff -ru misc/expat-2.2.10/doc/xmlwf.xml misc/build/expat-2.2.10/doc/xmlwf.xml
---- misc/expat-2.2.10/doc/xmlwf.xml 2020-09-25 13:47:39.000000000 -0400
-+++ misc/build/expat-2.2.10/doc/xmlwf.xml 2021-07-18 17:21:48.000000000 -0400
-@@ -3,7 +3,7 @@
- <!ENTITY dhfirstname "<firstname>Scott</firstname>">
- <!ENTITY dhsurname "<surname>Bronson</surname>">
- <!-- Please adjust the date whenever revising the manpage. -->
-- <!ENTITY dhdate "<date>March 11, 2016</date>">
-+ <!ENTITY dhdate "<date>July 18, 2021</date>">
- <!-- SECTION should be 1-8, maybe w/ subsection other parameters are
- allowed: see man(7), man(1). -->
- <!ENTITY dhsection "<manvolnum>1</manvolnum>">
-@@ -138,6 +138,52 @@
- </para>
-
- <variablelist>
-+
-+ <variablelist>
-+
-+ <varlistentry>
-+ <term><option>-a</option> <replaceable>factor</replaceable></term>
-+ <listitem>
-+ <para>
-+ Sets the maximum tolerated amplification factor
-+ for protection against billion laughs attacks (default: 100.0).
-+ The amplification factor is calculated as ..
-+ </para>
-+ <literallayout>
-+ amplification := (direct + indirect) / direct
-+ </literallayout>
-+ <para>
-+ .. while parsing, whereas
-+ <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.
-+ </para>
-+ <para>
-+ <emphasis>NOTE</emphasis>:
-+ If you ever need to increase this value for non-attack payload,
-+ please file a bug report.
-+ </para>
-+ </listitem>
-+ </varlistentry>
-+
-+ <varlistentry>
-+ <term><option>-b</option> <replaceable>bytes</replaceable></term>
-+ <listitem>
-+ <para>
-+ Sets the number of output bytes (including amplification)
-+ needed to activate protection against billion laughs attacks
-+ (default: 8 MiB).
-+ This can be thought of as an "activation threshold".
-+ </para>
-+ <para>
-+ <emphasis>NOTE</emphasis>:
-+ If you ever need to increase this value for non-attack payload,
-+ please file a bug report.
-+ </para>
-+ </listitem>
-+ </varlistentry>
-
- <varlistentry>
- <term><option>-c</option></term>
-@@ -455,6 +501,7 @@
- <literallayout>
- The Expat home page: http://www.libexpat.org/
- The W3 XML specification: http://www.w3.org/TR/REC-xml
-+Billion laughs attack: https://en.wikipedia.org/wiki/Billion_laughs_attack
- </literallayout>
-
- </para>
-diff -ru misc/expat-2.2.10/lib/expat.h misc/build/expat-2.2.10/lib/expat.h
---- misc/expat-2.2.10/lib/expat.h 2020-10-03 11:14:57.000000000 -0400
-+++ misc/build/expat-2.2.10/lib/expat.h 2021-07-18 17:21:48.000000000 -0400
-@@ -115,7 +115,10 @@
- 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.11 */
-+ XML_ERROR_NO_BUFFER,
-+ XML_ERROR_AMPLIFICATION_LIMIT_BREACH
- };
-
- enum XML_Content_Type {
-@@ -997,7 +1000,10 @@
- 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.11 */
-+ 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 +1016,19 @@
- XMLPARSEAPI(const XML_Feature *)
- XML_GetFeatureList(void);
-
-+
-+#ifdef XML_DTD
-+/* Added in Expat 2.2.11 */
-+XMLPARSEAPI(XML_Bool)
-+XML_SetBillionLaughsAttackProtectionMaximumAmplification(
-+ XML_Parser parser, float maximumAmplificationFactor);
-+
-+/* Added in Expat 2.2.11 */
-+XMLPARSEAPI(XML_Bool)
-+XML_SetBillionLaughsAttackProtectionActivationThreshold(
-+ XML_Parser parser, unsigned long long activationThresholdBytes);
-+#endif
-+
- /* Expat follows the semantic versioning convention.
- See http://semver.org.
- */
-diff -ru misc/expat-2.2.10/lib/internal.h misc/build/expat-2.2.10/lib/internal.h
---- misc/expat-2.2.10/lib/internal.h 2020-09-25 13:47:39.000000000 -0400
-+++ misc/build/expat-2.2.10/lib/internal.h 2021-07-18 17:21:48.000000000 -0400
-@@ -101,10 +101,47 @@
- # endif
- #endif
-
-+#include <limits.h> // ULONG_MAX
-+
-+#if defined(_WIN32) && ! defined(__USE_MINGW_ANSI_STDIO)
-+# define EXPAT_FMT_ULL(midpart) "%" midpart "I64u"
-+# 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"
-+# 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"
-+# 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
-+
-+
- #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 */
-+
-+#include "expat.h" // so we can use type XML_Parser below
-+
- #ifdef __cplusplus
- extern "C" {
- #endif
-@@ -117,6 +154,11 @@
- void
- _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);
-+const char *unsignedCharToPrintable(unsigned char c);
-+#endif
-
- #ifdef __cplusplus
- }
-diff -ru misc/expat-2.2.10/lib/libexpat.def misc/build/expat-2.2.10/lib/libexpat.def
---- misc/expat-2.2.10/lib/libexpat.def 2020-09-25 13:47:39.000000000 -0400
-+++ misc/build/expat-2.2.10/lib/libexpat.def 2021-07-18 17:21:48.000000000 -0400
-@@ -76,3 +76,6 @@
- XML_SetHashSalt @67
- ; added with version 2.2.5
- _INTERNAL_trim_to_complete_utf8_characters @68
-+; added with version 2.2.11
-+ XML_SetBillionLaughsAttackProtectionActivationThreshold @69
-+ XML_SetBillionLaughsAttackProtectionMaximumAmplification @70
-\ No newline at end of file
-diff -ru misc/expat-2.2.10/lib/libexpatw.def misc/build/expat-2.2.10/lib/libexpatw.def
---- misc/expat-2.2.10/lib/libexpatw.def 2020-09-25 13:47:39.000000000 -0400
-+++ misc/build/expat-2.2.10/lib/libexpatw.def 2021-07-18 17:21:48.000000000 -0400
-@@ -76,3 +76,6 @@
- XML_SetHashSalt @67
- ; added with version 2.2.5
- _INTERNAL_trim_to_complete_utf8_characters @68
-+; added with version 2.2.11
-+ XML_SetBillionLaughsAttackProtectionActivationThreshold @69
-+ XML_SetBillionLaughsAttackProtectionMaximumAmplification @70
-\ No newline at end of file
-Only in misc/build/expat-2.2.10/lib: Makefile
-diff -ru misc/expat-2.2.10/lib/Makefile.in misc/build/expat-2.2.10/lib/Makefile.in
---- misc/expat-2.2.10/lib/Makefile.in 2020-10-03 11:37:06.000000000 -0400
-+++ misc/build/expat-2.2.10/lib/Makefile.in 2021-07-18 18:17:02.000000000 -0400
-@@ -1,7 +1,7 @@
--# Makefile.in generated by automake 1.16.2 from Makefile.am.
-+# Makefile.in generated by automake 1.16.1 from Makefile.am.
- # @configure_input@
-
--# Copyright (C) 1994-2020 Free Software Foundation, Inc.
-+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
-
- # This Makefile.in is free software; the Free Software Foundation
- # gives unlimited permission to copy and/or distribute it,
-@@ -372,7 +372,6 @@
- prefix = @prefix@
- program_transform_name = @program_transform_name@
- psdir = @psdir@
--runstatedir = @runstatedir@
- sbindir = @sbindir@
- sharedstatedir = @sharedstatedir@
- srcdir = @srcdir@
-diff -ru misc/expat-2.2.10/lib/xmlparse.c misc/build/expat-2.2.10/lib/xmlparse.c
---- misc/expat-2.2.10/lib/xmlparse.c 2020-10-03 11:14:57.000000000 -0400
-+++ misc/build/expat-2.2.10/lib/xmlparse.c 2021-08-28 18:28:18.000000000 -0400
-@@ -47,6 +47,7 @@
- #include <limits.h> /* UINT_MAX */
- #include <stdio.h> /* fprintf */
- #include <stdlib.h> /* getenv, rand_s */
-+#include <math.h> /* 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 */
-@@ -73,6 +74,10 @@
-
- #ifdef _WIN32
- # include "winconfig.h"
-+#include <float.h>
-+#ifndef isnan
-+#define isnan _isnan
-+#endif
- #elif defined(HAVE_EXPAT_CONFIG_H)
- # include <expat_config.h>
- #endif /* ndef _WIN32 */
-@@ -382,6 +387,31 @@
- 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 +442,14 @@
- 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 +462,8 @@
- 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 +472,18 @@
- 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 +547,34 @@
-
- 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);
-+#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 +688,10 @@
- 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)))
-@@ -809,9 +876,8 @@
-
- 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,
-+ 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));
- }
- return entropy;
-@@ -855,7 +921,7 @@
- return ENTROPY_DEBUG("fallback(4)", entropy * 2147483647);
- } else {
- return ENTROPY_DEBUG("fallback(8)",
-- entropy * (unsigned long)2305843009213693951ULL);
-+ entropy * (unsigned long long)2305843009213693951ULL);
- }
- #endif
- }
-@@ -1073,6 +1139,18 @@
- 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 */
-@@ -2337,6 +2415,13 @@
- /* 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.11. */
-+ case XML_ERROR_NO_BUFFER:
-+ return XML_L(
-+ "a successful prior call to function XML_GetBuffer is required");
-+ case XML_ERROR_AMPLIFICATION_LIMIT_BREACH:
-+ return XML_L(
-+ "limit on input amplification factor (from DTD and entities) breached");
- }
- return NULL;
- }
-@@ -2373,41 +2458,75 @@
-
- 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.11. */
-+ {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
-@@ -2460,9 +2579,9 @@
- 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;
-@@ -2487,6 +2606,14 @@
- 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
-@@ -2524,6 +2651,10 @@
- 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) {
-@@ -2565,7 +2696,8 @@
- 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;
-@@ -2576,7 +2708,7 @@
- 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;
-
-@@ -2594,6 +2726,17 @@
- 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:
-@@ -2649,6 +2792,14 @@
- 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)
-@@ -2767,7 +2918,7 @@
- }
- 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)
-@@ -2791,7 +2942,8 @@
- 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;
-@@ -3055,7 +3207,8 @@
- */
- 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;
-@@ -3165,7 +3318,7 @@
- /* 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);
-@@ -3594,6 +3747,13 @@
- for (;;) {
- const char *next = s; /* in case of XML_TOK_NONE or XML_TOK_PARTIAL */
- int tok = XmlCdataSectionTok(enc, s, end, &next);
-+#ifdef XML_DTD
-+ if (! 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:
-@@ -3738,6 +3898,13 @@
- *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:
-@@ -3822,6 +3989,15 @@
- 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)) {
-@@ -3971,6 +4147,10 @@
-
- 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) {
-@@ -3989,7 +4169,8 @@
- 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);
-@@ -4016,6 +4197,14 @@
- */
- 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;
- }
-@@ -4058,16 +4247,24 @@
- }
- /* 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
-@@ -4080,6 +4277,9 @@
-
- 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;
-@@ -4097,7 +4297,7 @@
- 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;
- }
-@@ -4111,13 +4311,14 @@
- 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 */
-@@ -4144,6 +4345,10 @@
- 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;
-
-@@ -4208,6 +4413,19 @@
- }
- }
- 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);
-@@ -4483,7 +4701,8 @@
- 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);
-@@ -4516,8 +4735,9 @@
- 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
-@@ -4907,12 +5127,15 @@
- 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) {
-@@ -5110,6 +5333,13 @@
- 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 */
-@@ -5183,6 +5413,9 @@
- 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;
-@@ -5201,17 +5434,22 @@
- 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 */
-@@ -5244,12 +5482,13 @@
- 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;
-@@ -5258,6 +5497,9 @@
- entity->processed = (int)(next - (const 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 */
-@@ -5271,7 +5513,8 @@
- 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 */
- {
-@@ -5279,7 +5522,8 @@
- /* 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);
- }
- }
-
-@@ -5294,9 +5538,10 @@
-
- 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)
-@@ -5308,11 +5553,23 @@
-
- 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;
-+ 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)) {
-+ accountingOnAbort(parser);
-+ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
-+ }
-+#endif
- switch (tok) {
- case XML_TOK_NONE:
- return XML_ERROR_NONE;
-@@ -5372,6 +5629,14 @@
- 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;
-@@ -5449,9 +5714,16 @@
- enum XML_Error result;
- const XML_Char *textEnd = entity->textPtr + entity->textLen;
- entity->open = XML_TRUE;
-+#ifdef XML_DTD
-+ entityTrackingOnOpen(parser, entity, __LINE__);
-+#endif
- result = appendAttributeValue(parser, parser->m_internalEncoding,
- isCdata, (const char *)entity->textPtr,
-- (const char *)textEnd, pool);
-+ (const char *)textEnd, pool,
-+ XML_ACCOUNT_ENTITY_EXPANSION);
-+#ifdef XML_DTD
-+ entityTrackingOnClose(parser, entity, __LINE__);
-+#endif
- entity->open = XML_FALSE;
- if (result)
- return result;
-@@ -5481,13 +5753,16 @@
-
- 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
-@@ -5498,8 +5773,19 @@
- }
-
- 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
-+ 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
-@@ -5535,13 +5821,16 @@
- 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;
-@@ -5549,9 +5838,12 @@
- dtd->keepProcessing = dtd->standalone;
- } else {
- entity->open = XML_TRUE;
-+ entityTrackingOnOpen(parser, entity, __LINE__);
- result = storeEntityValue(
- parser, parser->m_internalEncoding, (const char *)entity->textPtr,
-- (const char *)(entity->textPtr + entity->textLen));
-+ (const char *)(entity->textPtr + entity->textLen),
-+ XML_ACCOUNT_ENTITY_EXPANSION);
-+ entityTrackingOnClose(parser, entity, __LINE__);
- entity->open = XML_FALSE;
- if (result)
- goto endEntityValue;
-@@ -6912,3 +7204,766 @@
- 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);
-+ float amplificationFactor;
-+ assert(! rootParser->m_parentParser);
-+
-+ if (rootParser->m_accounting.debugLevel < 1) {
-+ return;
-+ }
-+
-+ 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) {
-+ const char ellipis[] = "[..]";
-+ const size_t ellipsisLength = sizeof(ellipis) /* because compile-time */ - 1;
-+ const unsigned int contextLength = 10;
-+ const char *walker = before;
-+
-+ 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, "");
-+
-+ /* Note: Performance is of no concern here */
-+ 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) {
-+ unsigned int levelsAwayFromRootParser;
-+ XML_Parser rootParser;
-+ int isDirect;
-+ ptrdiff_t bytesMore;
-+ XmlBigCount * additionTarget;
-+ XmlBigCount countBytesOutput;
-+ float amplificationFactor;
-+ XML_Bool tolerated;
-+ /* Note: We need to check the token type *first* to be sure that
-+ * we can even access variable <after>, safely.
-+ * E.g. for XML_TOK_NONE <after> 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 */
-+
-+ rootParser
-+ = getRootParserOf(originParser, &levelsAwayFromRootParser);
-+ assert(! rootParser->m_parentParser);
-+
-+ isDirect
-+ = (account == XML_ACCOUNT_DIRECT) && (originParser == rootParser);
-+ bytesMore = after - before;
-+
-+ 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;
-+
-+ countBytesOutput
-+ = rootParser->m_accounting.countBytesDirect
-+ + rootParser->m_accounting.countBytesIndirect;
-+ amplificationFactor
-+ = accountingGetCurrentAmplification(rootParser);
-+ 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;
-+}
-+
-+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) {
-+ const char * entityName;
-+ assert(! rootParser->m_parentParser);
-+ if (rootParser->m_entity_stats.debugLevel < 1)
-+ return;
-+
-+# if defined(XML_UNICODE)
-+ entityName = "[..]";
-+# else
-+ 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;
-+}
-+
-+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);
-+ const char * value;
-+ char *afterValue;
-+ unsigned long debugLevel;
-+ if (valueOrNull == NULL) {
-+ return defaultDebugLevel;
-+ }
-+ value = valueOrNull;
-+
-+ errno = 0;
-+ afterValue = (char *)value;
-+ debugLevel = strtoul(value, &afterValue, 10);
-+ if ((errno != 0) || (afterValue[0] != '\0')) {
-+ errno = 0;
-+ return defaultDebugLevel;
-+ }
-+
-+ return debugLevel;
-+}
-diff -ru misc/expat-2.2.10/tests/benchmark/Makefile.in misc/build/expat-2.2.10/tests/benchmark/Makefile.in
---- misc/expat-2.2.10/tests/benchmark/Makefile.in 2020-10-03 11:37:06.000000000 -0400
-+++ misc/build/expat-2.2.10/tests/benchmark/Makefile.in 2021-07-18 18:17:02.000000000 -0400
-@@ -1,7 +1,7 @@
--# Makefile.in generated by automake 1.16.2 from Makefile.am.
-+# Makefile.in generated by automake 1.16.1 from Makefile.am.
- # @configure_input@
-
--# Copyright (C) 1994-2020 Free Software Foundation, Inc.
-+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
-
- # This Makefile.in is free software; the Free Software Foundation
- # gives unlimited permission to copy and/or distribute it,
-@@ -335,7 +335,6 @@
- prefix = @prefix@
- program_transform_name = @program_transform_name@
- psdir = @psdir@
--runstatedir = @runstatedir@
- sbindir = @sbindir@
- sharedstatedir = @sharedstatedir@
- srcdir = @srcdir@
-diff -ru misc/expat-2.2.10/tests/Makefile.in misc/build/expat-2.2.10/tests/Makefile.in
---- misc/expat-2.2.10/tests/Makefile.in 2020-10-03 11:37:06.000000000 -0400
-+++ misc/build/expat-2.2.10/tests/Makefile.in 2021-07-18 18:17:02.000000000 -0400
-@@ -1,7 +1,7 @@
--# Makefile.in generated by automake 1.16.2 from Makefile.am.
-+# Makefile.in generated by automake 1.16.1 from Makefile.am.
- # @configure_input@
-
--# Copyright (C) 1994-2020 Free Software Foundation, Inc.
-+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
-
- # This Makefile.in is free software; the Free Software Foundation
- # gives unlimited permission to copy and/or distribute it,
-@@ -616,7 +616,6 @@
- prefix = @prefix@
- program_transform_name = @program_transform_name@
- psdir = @psdir@
--runstatedir = @runstatedir@
- sbindir = @sbindir@
- sharedstatedir = @sharedstatedir@
- srcdir = @srcdir@
-diff -ru misc/expat-2.2.10/tests/runtests.c misc/build/expat-2.2.10/tests/runtests.c
---- misc/expat-2.2.10/tests/runtests.c 2020-10-03 11:14:57.000000000 -0400
-+++ misc/build/expat-2.2.10/tests/runtests.c 2021-07-18 17:27:08.000000000 -0400
-@@ -45,6 +45,7 @@
- #include <stddef.h> /* ptrdiff_t */
- #include <ctype.h>
- #include <limits.h>
-+#include <math.h> /* NAN, INFINITY, isnan */
-
- #if defined(_WIN32) && defined(_MSC_VER) && (_MSC_VER < 1600)
- /* For vs2003/7.1 up to vs2008/9.0; _MSC_VER 1600 is vs2010/10.0 */
-@@ -72,7 +73,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"
-@@ -11231,6 +11232,381 @@
- }
- END_TEST
-
-+#if defined(XML_DTD)
-+typedef enum XML_Status (*XmlParseFunction)(XML_Parser, const char *, int, int);
-+
-+struct AccountingTestCase {
-+ const char *primaryText;
-+ const char *firstExternalText; /* often NULL */
-+ const char *secondExternalText; /* often NULL */
-+ const unsigned long long expectedCountBytesIndirectExtra;
-+ XML_Bool singleBytesWanted;
-+};
-+
-+static int
-+accounting_external_entity_ref_handler(XML_Parser parser,
-+ const XML_Char *context,
-+ const XML_Char *base,
-+ const XML_Char *systemId,
-+ const XML_Char *publicId) {
-+ UNUSED_P(context);
-+ UNUSED_P(base);
-+ UNUSED_P(publicId);
-+
-+ const struct AccountingTestCase *const testCase
-+ = (const struct AccountingTestCase *)XML_GetUserData(parser);
-+
-+ const char *externalText = NULL;
-+ if (xcstrcmp(systemId, XCS("first.ent")) == 0) {
-+ externalText = testCase->firstExternalText;
-+ } else if (xcstrcmp(systemId, XCS("second.ent")) == 0) {
-+ externalText = testCase->secondExternalText;
-+ } else {
-+ assert(! "systemId is neither \"first.ent\" nor \"second.ent\"");
-+ }
-+ assert(externalText);
-+
-+ XML_Parser entParser = XML_ExternalEntityParserCreate(parser, context, 0);
-+ assert(entParser);
-+
-+ const XmlParseFunction xmlParseFunction
-+ = testCase->singleBytesWanted ? _XML_Parse_SINGLE_BYTES : XML_Parse;
-+
-+ const enum XML_Status status = xmlParseFunction(
-+ entParser, externalText, (int)strlen(externalText), XML_TRUE);
-+
-+ XML_ParserFree(entParser);
-+ return status;
-+}
-+
-+START_TEST(test_accounting_precision) {
-+ const XML_Bool filled_later = XML_TRUE; /* value is arbitrary */
-+ struct AccountingTestCase cases[] = {
-+ {"<e/>", NULL, NULL, 0, 0},
-+ {"<e></e>", NULL, NULL, 0, 0},
-+
-+ /* Attributes */
-+ {"<e k1=\"v2\" k2=\"v2\"/>", NULL, NULL, 0, filled_later},
-+ {"<e k1=\"v2\" k2=\"v2\"></e>", NULL, NULL, 0, 0},
-+ {"<p:e xmlns:p=\"https://domain.invalid/\" />", NULL, NULL, 0,
-+ filled_later},
-+ {"<e k=\"&'><"\" />", NULL, NULL,
-+ sizeof(XML_Char) * 5 /* number of predefined entites */, filled_later},
-+ {"<e1 xmlns='https://example.org/'>\n"
-+ " <e2 xmlns=''/>\n"
-+ "</e1>",
-+ NULL, NULL, 0, filled_later},
-+
-+ /* Text */
-+ {"<e>text</e>", NULL, NULL, 0, filled_later},
-+ {"<e1><e2>text1<e3/>text2</e2></e1>", NULL, NULL, 0, filled_later},
-+ {"<e>&'><"</e>", NULL, NULL,
-+ sizeof(XML_Char) * 5 /* number of predefined entites */, filled_later},
-+ {"<e>A)</e>", NULL, NULL, 0, filled_later},
-+
-+ /* Prolog */
-+ {"<?xml version=\"1.0\"?><root/>", NULL, NULL, 0, filled_later},
-+
-+ /* Whitespace */
-+ {" <e1> <e2> </e2> </e1> ", NULL, NULL, 0, filled_later},
-+ {"<e1 ><e2 /></e1 >", NULL, NULL, 0, filled_later},
-+ {"<e1><e2 k = \"v\"/><e3 k = 'v'/></e1>", NULL, NULL, 0, filled_later},
-+
-+ /* Comments */
-+ {"<!-- Comment --><e><!-- Comment --></e>", NULL, NULL, 0, filled_later},
-+
-+ /* Processing instructions */
-+ {"<?xml-stylesheet type=\"text/xsl\" href=\"https://domain.invalid/\" media=\"all\"?><e/>",
-+ NULL, NULL, 0, filled_later},
-+ {"<?pi0?><?pi1 ?><?pi2 ?><!DOCTYPE r SYSTEM 'first.ent'><r/>",
-+ "<?pi3?><!ENTITY % e1 SYSTEM 'second.ent'><?pi4?>%e1;<?pi5?>", "<?pi6?>",
-+ 0, filled_later},
-+
-+ /* CDATA */
-+ {"<e><![CDATA[one two three]]></e>", NULL, NULL, 0, filled_later},
-+
-+ /* Conditional sections */
-+ {"<!DOCTYPE r [\n"
-+ "<!ENTITY % draft 'INCLUDE'>\n"
-+ "<!ENTITY % final 'IGNORE'>\n"
-+ "<!ENTITY % import SYSTEM \"first.ent\">\n"
-+ "%import;\n"
-+ "]>\n"
-+ "<r/>\n",
-+ "<![%draft;[<!--1-->]]>\n"
-+ "<![%final;[<!--22-->]]>",
-+ NULL, sizeof(XML_Char) * (strlen("INCLUDE") + strlen("IGNORE")),
-+ filled_later},
-+
-+ /* General entities */
-+ {"<!DOCTYPE root [\n"
-+ "<!ENTITY nine \"123456789\">\n"
-+ "]>\n"
-+ "<root>&nine;</root>",
-+ NULL, NULL, sizeof(XML_Char) * strlen("123456789"), filled_later},
-+ {"<!DOCTYPE root [\n"
-+ "<!ENTITY nine \"123456789\">\n"
-+ "]>\n"
-+ "<root k1=\"&nine;\"/>",
-+ NULL, NULL, sizeof(XML_Char) * strlen("123456789"), filled_later},
-+ {"<!DOCTYPE root [\n"
-+ "<!ENTITY nine \"123456789\">\n"
-+ "<!ENTITY nine2 \"&nine;&nine;\">\n"
-+ "]>\n"
-+ "<root>&nine2;&nine2;&nine2;</root>",
-+ NULL, NULL,
-+ sizeof(XML_Char) * 3 /* calls to &nine2; */ * 2 /* calls to &nine; */
-+ * (strlen("&nine;") + strlen("123456789")),
-+ filled_later},
-+ {"<!DOCTYPE r [\n"
-+ " <!ENTITY five SYSTEM 'first.ent'>\n"
-+ "]>\n"
-+ "<r>&five;</r>",
-+ "12345", NULL, 0, filled_later},
-+
-+ /* Parameter entities */
-+ {"<!DOCTYPE r [\n"
-+ "<!ENTITY % comment \"<!---->\">\n"
-+ "%comment;\n"
-+ "]>\n"
-+ "<r/>",
-+ NULL, NULL, sizeof(XML_Char) * strlen("<!---->"), filled_later},
-+ {"<!DOCTYPE r [\n"
-+ "<!ENTITY % ninedef \"<!ENTITY nine "123456789">\">\n"
-+ "%ninedef;\n"
-+ "]>\n"
-+ "<r>&nine;</r>",
-+ NULL, NULL,
-+ sizeof(XML_Char)
-+ * (strlen("<!ENTITY nine \"123456789\">") + strlen("123456789")),
-+ filled_later},
-+ {"<!DOCTYPE r [\n"
-+ "<!ENTITY % comment \"<!--1-->\">\n"
-+ "<!ENTITY % comment2 \"%comment;<!--22-->%comment;\">\n"
-+ "%comment2;\n"
-+ "]>\n"
-+ "<r/>\n",
-+ NULL, NULL,
-+ sizeof(XML_Char)
-+ * (strlen("%comment;<!--22-->%comment;") + 2 * strlen("<!--1-->")),
-+ filled_later},
-+ {"<!DOCTYPE r [\n"
-+ " <!ENTITY % five \"12345\">\n"
-+ " <!ENTITY % five2def \"<!ENTITY five2 "[%five;][%five;]]]]">\">\n"
-+ " %five2def;\n"
-+ "]>\n"
-+ "<r>&five2;</r>",
-+ NULL, NULL, /* from "%five2def;": */
-+ sizeof(XML_Char)
-+ * (strlen("<!ENTITY five2 \"[%five;][%five;]]]]\">")
-+ + 2 /* calls to "%five;" */ * strlen("12345")
-+ + /* from "&five2;": */ strlen("[12345][12345]]]]")),
-+ filled_later},
-+ {"<!DOCTYPE r SYSTEM \"first.ent\">\n"
-+ "<r/>",
-+ "<!ENTITY % comment '<!--1-->'>\n"
-+ "<!ENTITY % comment2 '<!--22-->%comment;<!--22-->%comment;<!--22-->'>\n"
-+ "%comment2;",
-+ NULL,
-+ sizeof(XML_Char)
-+ * (strlen("<!--22-->%comment;<!--22-->%comment;<!--22-->")
-+ + 2 /* calls to "%comment;" */ * strlen("<!---->")),
-+ filled_later},
-+ {"<!DOCTYPE r SYSTEM 'first.ent'>\n"
-+ "<r/>",
-+ "<!ENTITY % e1 PUBLIC 'foo' 'second.ent'>\n"
-+ "<!ENTITY % e2 '<!--22-->%e1;<!--22-->'>\n"
-+ "%e2;\n",
-+ "<!--1-->", sizeof(XML_Char) * strlen("<!--22--><!--1--><!--22-->"),
-+ filled_later},
-+ {
-+ "<!DOCTYPE r SYSTEM 'first.ent'>\n"
-+ "<r/>",
-+ "<!ENTITY % e1 SYSTEM 'second.ent'>\n"
-+ "<!ENTITY % e2 '%e1;'>",
-+ "<?xml version='1.0' encoding='utf-8'?>\n"
-+ "hello\n"
-+ "xml" /* without trailing newline! */,
-+ 0,
-+ filled_later,
-+ },
-+ {
-+ "<!DOCTYPE r SYSTEM 'first.ent'>\n"
-+ "<r/>",
-+ "<!ENTITY % e1 SYSTEM 'second.ent'>\n"
-+ "<!ENTITY % e2 '%e1;'>",
-+ "<?xml version='1.0' encoding='utf-8'?>\n"
-+ "hello\n"
-+ "xml\n" /* with trailing newline! */,
-+ 0,
-+ filled_later,
-+ },
-+ {"<!DOCTYPE doc SYSTEM 'first.ent'>\n"
-+ "<doc></doc>\n",
-+ "<!ELEMENT doc EMPTY>\n"
-+ "<!ENTITY % e1 SYSTEM 'second.ent'>\n"
-+ "<!ENTITY % e2 '%e1;'>\n"
-+ "%e1;\n",
-+ "\xEF\xBB\xBF<!ATTLIST doc a1 CDATA 'value'>" /* UTF-8 BOM */,
-+ strlen("\xEF\xBB\xBF<!ATTLIST doc a1 CDATA 'value'>"), filled_later},
-+ {"<!DOCTYPE r [\n"
-+ " <!ENTITY five SYSTEM 'first.ent'>\n"
-+ "]>\n"
-+ "<r>&five;</r>",
-+ "\xEF\xBB\xBF" /* UTF-8 BOM */, NULL, 0, filled_later},
-+ };
-+
-+ const size_t countCases = sizeof(cases) / sizeof(cases[0]);
-+ size_t u = 0;
-+ for (; u < countCases; u++) {
-+ size_t v = 0;
-+ for (; v < 2; v++) {
-+ const XML_Bool singleBytesWanted = (v == 0) ? XML_FALSE : XML_TRUE;
-+ const unsigned long long expectedCountBytesDirect
-+ = strlen(cases[u].primaryText);
-+ const unsigned long long expectedCountBytesIndirect
-+ = (cases[u].firstExternalText ? strlen(cases[u].firstExternalText)
-+ : 0)
-+ + (cases[u].secondExternalText ? strlen(cases[u].secondExternalText)
-+ : 0)
-+ + cases[u].expectedCountBytesIndirectExtra;
-+
-+ XML_Parser parser = XML_ParserCreate(NULL);
-+ XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
-+ if (cases[u].firstExternalText) {
-+ XML_SetExternalEntityRefHandler(parser,
-+ accounting_external_entity_ref_handler);
-+ XML_SetUserData(parser, (void *)&cases[u]);
-+ cases[u].singleBytesWanted = singleBytesWanted;
-+ }
-+
-+ const XmlParseFunction xmlParseFunction
-+ = singleBytesWanted ? _XML_Parse_SINGLE_BYTES : XML_Parse;
-+
-+ enum XML_Status status
-+ = xmlParseFunction(parser, cases[u].primaryText,
-+ (int)strlen(cases[u].primaryText), XML_TRUE);
-+ if (status != XML_STATUS_OK) {
-+ _xml_failure(parser, __FILE__, __LINE__);
-+ }
-+
-+ const unsigned long long actualCountBytesDirect
-+ = testingAccountingGetCountBytesDirect(parser);
-+ const unsigned long long actualCountBytesIndirect
-+ = testingAccountingGetCountBytesIndirect(parser);
-+
-+ XML_ParserFree(parser);
-+
-+ if (actualCountBytesDirect != expectedCountBytesDirect) {
-+ fprintf(
-+ stderr,
-+ "Document " EXPAT_FMT_SIZE_T("") " of " EXPAT_FMT_SIZE_T("") ", %s: Expected " EXPAT_FMT_ULL(
-+ "") " count direct bytes, got " EXPAT_FMT_ULL("") " instead.\n",
-+ u + 1, countCases, singleBytesWanted ? "single bytes" : "chunks",
-+ expectedCountBytesDirect, actualCountBytesDirect);
-+ fail("Count of direct bytes is off");
-+ }
-+
-+ if (actualCountBytesIndirect != expectedCountBytesIndirect) {
-+ fprintf(
-+ stderr,
-+ "Document " EXPAT_FMT_SIZE_T("") " of " EXPAT_FMT_SIZE_T("") ", %s: Expected " EXPAT_FMT_ULL(
-+ "") " count indirect bytes, got " EXPAT_FMT_ULL("") " instead.\n",
-+ u + 1, countCases, singleBytesWanted ? "single bytes" : "chunks",
-+ expectedCountBytesIndirect, actualCountBytesIndirect);
-+ fail("Count of indirect bytes is off");
-+ }
-+ }
-+ }
-+}
-+END_TEST
-+
-+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
-+
-+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 *
- make_suite(void) {
- Suite *s = suite_create("basic");
-@@ -11239,6 +11615,9 @@
- 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);
-@@ -11603,6 +11982,13 @@
- 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);
-+ 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;
- }
-
-diff -ru misc/expat-2.2.10/xmlwf/Makefile.in misc/build/expat-2.2.10/xmlwf/Makefile.in
---- misc/expat-2.2.10/xmlwf/Makefile.in 2020-10-03 11:37:06.000000000 -0400
-+++ misc/build/expat-2.2.10/xmlwf/Makefile.in 2021-07-18 18:17:02.000000000 -0400
-@@ -1,7 +1,7 @@
--# Makefile.in generated by automake 1.16.2 from Makefile.am.
-+# Makefile.in generated by automake 1.16.1 from Makefile.am.
- # @configure_input@
-
--# Copyright (C) 1994-2020 Free Software Foundation, Inc.
-+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
-
- # This Makefile.in is free software; the Free Software Foundation
- # gives unlimited permission to copy and/or distribute it,
-@@ -344,7 +344,6 @@
- prefix = @prefix@
- program_transform_name = @program_transform_name@
- psdir = @psdir@
--runstatedir = @runstatedir@
- sbindir = @sbindir@
- sharedstatedir = @sharedstatedir@
- srcdir = @srcdir@
-diff -ru misc/expat-2.2.10/xmlwf/xmltchar.h misc/build/expat-2.2.10/xmlwf/xmltchar.h
---- misc/expat-2.2.10/xmlwf/xmltchar.h 2020-09-25 13:47:39.000000000 -0400
-+++ misc/build/expat-2.2.10/xmlwf/xmltchar.h 2021-07-18 17:21:48.000000000 -0400
-@@ -54,6 +54,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
-@@ -71,4 +73,6 @@
- # define tmain main
- # define tremove remove
- # define tchar char
-+# define tcstof strtof
-+# define tcstoull strtoull
- #endif /* not XML_UNICODE */
-diff -ru misc/expat-2.2.10/xmlwf/xmlwf.c misc/build/expat-2.2.10/xmlwf/xmlwf.c
---- misc/expat-2.2.10/xmlwf/xmlwf.c 2020-09-25 13:47:39.000000000 -0400
-+++ misc/build/expat-2.2.10/xmlwf/xmlwf.c 2021-08-25 18:35:36.000000000 -0400
-@@ -30,11 +30,15 @@
- USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-+#include <expat_config.h>
-+
- #include <assert.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <stddef.h>
- #include <string.h>
-+#include <math.h> /* for isnan */
-+#include <errno.h>
-
- #include "expat.h"
- #include "codepage.h"
-@@ -50,6 +54,14 @@
- # include <wchar.h>
- #endif
-
-+enum ExitCode {
-+ XMLWF_EXIT_SUCCESS = 0,
-+ XMLWF_EXIT_INTERNAL_ERROR = 1,
-+ XMLWF_EXIT_NOT_WELLFORMED = 2,
-+ XMLWF_EXIT_OUTPUT_ERROR = 3,
-+ XMLWF_EXIT_USAGE_ERROR = 4,
-+};
-+
- /* Structures for handler user data */
- typedef struct NotationList {
- struct NotationList *next;
-@@ -875,6 +887,12 @@
- 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")
-@@ -891,6 +909,19 @@
- 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], XMLWF_EXIT_USAGE_ERROR); \
-+ constCharStarTarget = argv[i]; \
-+ } else { \
-+ constCharStarTarget = argv[i] + j + 1; \
-+ } \
-+ i++; \
-+ j = 0; \
-+ }
-+
- int
- tmain(int argc, XML_Char **argv) {
- int i, j;
-@@ -902,6 +933,11 @@
- 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;
-@@ -990,6 +1026,49 @@
- 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(XMLWF_EXIT_USAGE_ERROR);
-+ }
-+#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(XMLWF_EXIT_USAGE_ERROR);
-+ }
-+ attackThresholdGiven = XML_TRUE;
-+#ifndef XML_DTD
-+ ftprintf(stderr, T("Warning: Given attack threshold ignored") T(
-+ ", xmlwf has been compiled without DTD support.\n"));
-+#endif
-+ break;
-+ }
- case T('\0'):
- if (j > 1) {
- i++;
-@@ -1020,6 +1099,19 @@
- 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 -ru misc/expat-2.2.10/xmlwf/xmlwf_helpgen.py misc/build/expat-2.2.10/xmlwf/xmlwf_helpgen.py
---- misc/expat-2.2.10/xmlwf/xmlwf_helpgen.py 2020-09-25 13:47:39.000000000 -0400
-+++ misc/build/expat-2.2.10/xmlwf/xmlwf_helpgen.py 2021-07-18 17:21:48.000000000 -0400
-@@ -57,6 +57,14 @@
- 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='file to process (default: STDIN)')
-
- info = parser.add_argument_group('info arguments')
diff --git a/main/expat/makefile.mk b/main/expat/makefile.mk
index 901ee9a..20fd92b 100644
--- a/main/expat/makefile.mk
+++ b/main/expat/makefile.mk
@@ -41,7 +41,7 @@ all:
TARFILE_NAME=expat-2.2.10
TARFILE_MD5=9d60de01cc0126dfd11121b04838e154
ADDITIONAL_FILES=lib$/makefile.mk
-PATCH_FILES=$(TARFILE_NAME).patch expat-2.2.11.patch
+PATCH_FILES=$(TARFILE_NAME).patch
CONFIGURE_DIR=
.IF "$(OS)"=="WNT"