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 2021/08/29 12:26:45 UTC

[openoffice] 01/01: Backport changes from libexpat 2.4.0 into ours

This is an automated email from the ASF dual-hosted git repository.

ardovm pushed a commit to branch libexpat
in repository https://gitbox.apache.org/repos/asf/openoffice.git

commit 5055919709f5aaebdd15208a35c55991bc39d195
Author: Arrigo Marchiori <ar...@yahoo.it>
AuthorDate: Sun Aug 29 14:25:43 2021 +0200

    Backport changes from libexpat 2.4.0 into ours
    
    Backport by Carl Marcum
---
 main/expat/expat-2.2.11.patch | 2745 +++++++++++++++++++++++++++++++++++++++++
 main/expat/makefile.mk        |    2 +-
 2 files changed, 2746 insertions(+), 1 deletion(-)

diff --git a/main/expat/expat-2.2.11.patch b/main/expat/expat-2.2.11.patch
new file mode 100644
index 0000000..289362d
--- /dev/null
+++ b/main/expat/expat-2.2.11.patch
@@ -0,0 +1,2745 @@
+--- 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
++            &lt;direct&gt; is the number of bytes read
++              from the primary document in parsing and
++            &lt;indirect&gt; is the number of bytes
++              added by expanding entities and reading of external DTD files,
++              combined.
++          </para>
++          <para>
++            <emphasis>NOTE</emphasis>:
++            If you ever need to increase this value for non-attack payload,
++            please file a bug report.
++          </para>
++        </listitem>
++      </varlistentry>
++
++      <varlistentry>
++        <term><option>-b</option> <replaceable>bytes</replaceable></term>
++        <listitem>
++          <para>
++            Sets the number of output bytes (including amplification)
++            needed to activate protection against billion laughs attacks
++            (default: 8 MiB).
++            This can be thought of as an &quot;activation threshold&quot;.
++          </para>
++          <para>
++            <emphasis>NOTE</emphasis>:
++            If you ever need to increase this value for non-attack payload,
++            please file a bug report.
++          </para>
++        </listitem>
++      </varlistentry>
+ 
+       <varlistentry>
+         <term><option>-c</option></term>
+@@ -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=\"&amp;&apos;&gt;&lt;&quot;\" />", NULL, NULL,
++       sizeof(XML_Char) * 5 /* number of predefined entites */, filled_later},
++      {"<e1 xmlns='https://example.org/'>\n"
++       "  <e2 xmlns=''/>\n"
++       "</e1>",
++       NULL, NULL, 0, filled_later},
++
++      /* Text */
++      {"<e>text</e>", NULL, NULL, 0, filled_later},
++      {"<e1><e2>text1<e3/>text2</e2></e1>", NULL, NULL, 0, filled_later},
++      {"<e>&amp;&apos;&gt;&lt;&quot;</e>", NULL, NULL,
++       sizeof(XML_Char) * 5 /* number of predefined entites */, filled_later},
++      {"<e>&#65;&#41;</e>", NULL, NULL, 0, filled_later},
++
++      /* Prolog */
++      {"<?xml version=\"1.0\"?><root/>", NULL, NULL, 0, filled_later},
++
++      /* Whitespace */
++      {"  <e1>  <e2>  </e2>  </e1>  ", NULL, NULL, 0, filled_later},
++      {"<e1  ><e2  /></e1  >", NULL, NULL, 0, filled_later},
++      {"<e1><e2 k = \"v\"/><e3 k = 'v'/></e1>", NULL, NULL, 0, filled_later},
++
++      /* Comments */
++      {"<!-- Comment --><e><!-- Comment --></e>", NULL, NULL, 0, filled_later},
++
++      /* Processing instructions */
++      {"<?xml-stylesheet type=\"text/xsl\" href=\"https://domain.invalid/\" media=\"all\"?><e/>",
++       NULL, NULL, 0, filled_later},
++      {"<?pi0?><?pi1 ?><?pi2  ?><!DOCTYPE r SYSTEM 'first.ent'><r/>",
++       "<?pi3?><!ENTITY % e1 SYSTEM 'second.ent'><?pi4?>%e1;<?pi5?>", "<?pi6?>",
++       0, filled_later},
++
++      /* CDATA */
++      {"<e><![CDATA[one two three]]></e>", NULL, NULL, 0, filled_later},
++
++      /* Conditional sections */
++      {"<!DOCTYPE r [\n"
++       "<!ENTITY % draft 'INCLUDE'>\n"
++       "<!ENTITY % final 'IGNORE'>\n"
++       "<!ENTITY % import SYSTEM \"first.ent\">\n"
++       "%import;\n"
++       "]>\n"
++       "<r/>\n",
++       "<![%draft;[<!--1-->]]>\n"
++       "<![%final;[<!--22-->]]>",
++       NULL, sizeof(XML_Char) * (strlen("INCLUDE") + strlen("IGNORE")),
++       filled_later},
++
++      /* General entities */
++      {"<!DOCTYPE root [\n"
++       "<!ENTITY nine \"123456789\">\n"
++       "]>\n"
++       "<root>&nine;</root>",
++       NULL, NULL, sizeof(XML_Char) * strlen("123456789"), filled_later},
++      {"<!DOCTYPE root [\n"
++       "<!ENTITY nine \"123456789\">\n"
++       "]>\n"
++       "<root k1=\"&nine;\"/>",
++       NULL, NULL, sizeof(XML_Char) * strlen("123456789"), filled_later},
++      {"<!DOCTYPE root [\n"
++       "<!ENTITY nine \"123456789\">\n"
++       "<!ENTITY nine2 \"&nine;&nine;\">\n"
++       "]>\n"
++       "<root>&nine2;&nine2;&nine2;</root>",
++       NULL, NULL,
++       sizeof(XML_Char) * 3 /* calls to &nine2; */ * 2 /* calls to &nine; */
++           * (strlen("&nine;") + strlen("123456789")),
++       filled_later},
++      {"<!DOCTYPE r [\n"
++       "  <!ENTITY five SYSTEM 'first.ent'>\n"
++       "]>\n"
++       "<r>&five;</r>",
++       "12345", NULL, 0, filled_later},
++
++      /* Parameter entities */
++      {"<!DOCTYPE r [\n"
++       "<!ENTITY % comment \"<!---->\">\n"
++       "%comment;\n"
++       "]>\n"
++       "<r/>",
++       NULL, NULL, sizeof(XML_Char) * strlen("<!---->"), filled_later},
++      {"<!DOCTYPE r [\n"
++       "<!ENTITY % ninedef \"&#60;!ENTITY nine &#34;123456789&#34;&#62;\">\n"
++       "%ninedef;\n"
++       "]>\n"
++       "<r>&nine;</r>",
++       NULL, NULL,
++       sizeof(XML_Char)
++           * (strlen("<!ENTITY nine \"123456789\">") + strlen("123456789")),
++       filled_later},
++      {"<!DOCTYPE r [\n"
++       "<!ENTITY % comment \"<!--1-->\">\n"
++       "<!ENTITY % comment2 \"&#37;comment;<!--22-->&#37;comment;\">\n"
++       "%comment2;\n"
++       "]>\n"
++       "<r/>\n",
++       NULL, NULL,
++       sizeof(XML_Char)
++           * (strlen("%comment;<!--22-->%comment;") + 2 * strlen("<!--1-->")),
++       filled_later},
++      {"<!DOCTYPE r [\n"
++       "  <!ENTITY % five \"12345\">\n"
++       "  <!ENTITY % five2def \"&#60;!ENTITY five2 &#34;[&#37;five;][&#37;five;]]]]&#34;&#62;\">\n"
++       "  %five2def;\n"
++       "]>\n"
++       "<r>&five2;</r>",
++       NULL, NULL, /* from "%five2def;": */
++       sizeof(XML_Char)
++           * (strlen("<!ENTITY five2 \"[%five;][%five;]]]]\">")
++              + 2 /* calls to "%five;" */ * strlen("12345")
++              + /* from "&five2;": */ strlen("[12345][12345]]]]")),
++       filled_later},
++      {"<!DOCTYPE r SYSTEM \"first.ent\">\n"
++       "<r/>",
++       "<!ENTITY % comment '<!--1-->'>\n"
++       "<!ENTITY % comment2 '<!--22-->%comment;<!--22-->%comment;<!--22-->'>\n"
++       "%comment2;",
++       NULL,
++       sizeof(XML_Char)
++           * (strlen("<!--22-->%comment;<!--22-->%comment;<!--22-->")
++              + 2 /* calls to "%comment;" */ * strlen("<!---->")),
++       filled_later},
++      {"<!DOCTYPE r SYSTEM 'first.ent'>\n"
++       "<r/>",
++       "<!ENTITY % e1 PUBLIC 'foo' 'second.ent'>\n"
++       "<!ENTITY % e2 '<!--22-->%e1;<!--22-->'>\n"
++       "%e2;\n",
++       "<!--1-->", sizeof(XML_Char) * strlen("<!--22--><!--1--><!--22-->"),
++       filled_later},
++      {
++          "<!DOCTYPE r SYSTEM 'first.ent'>\n"
++          "<r/>",
++          "<!ENTITY % e1 SYSTEM 'second.ent'>\n"
++          "<!ENTITY % e2 '%e1;'>",
++          "<?xml version='1.0' encoding='utf-8'?>\n"
++          "hello\n"
++          "xml" /* without trailing newline! */,
++          0,
++          filled_later,
++      },
++      {
++          "<!DOCTYPE r SYSTEM 'first.ent'>\n"
++          "<r/>",
++          "<!ENTITY % e1 SYSTEM 'second.ent'>\n"
++          "<!ENTITY % e2 '%e1;'>",
++          "<?xml version='1.0' encoding='utf-8'?>\n"
++          "hello\n"
++          "xml\n" /* with trailing newline! */,
++          0,
++          filled_later,
++      },
++      {"<!DOCTYPE doc SYSTEM 'first.ent'>\n"
++       "<doc></doc>\n",
++       "<!ELEMENT doc EMPTY>\n"
++       "<!ENTITY % e1 SYSTEM 'second.ent'>\n"
++       "<!ENTITY % e2 '%e1;'>\n"
++       "%e1;\n",
++       "\xEF\xBB\xBF<!ATTLIST doc a1 CDATA 'value'>" /* UTF-8 BOM */,
++       strlen("\xEF\xBB\xBF<!ATTLIST doc a1 CDATA 'value'>"), filled_later},
++      {"<!DOCTYPE r [\n"
++       "  <!ENTITY five SYSTEM 'first.ent'>\n"
++       "]>\n"
++       "<r>&five;</r>",
++       "\xEF\xBB\xBF" /* UTF-8 BOM */, NULL, 0, filled_later},
++  };
++
++  const size_t countCases = sizeof(cases) / sizeof(cases[0]);
++  size_t u = 0;
++  for (; u < countCases; u++) {
++    size_t v = 0;
++    for (; v < 2; v++) {
++      const XML_Bool singleBytesWanted = (v == 0) ? XML_FALSE : XML_TRUE;
++      const unsigned long long expectedCountBytesDirect
++          = strlen(cases[u].primaryText);
++      const unsigned long long expectedCountBytesIndirect
++          = (cases[u].firstExternalText ? strlen(cases[u].firstExternalText)
++                                        : 0)
++            + (cases[u].secondExternalText ? strlen(cases[u].secondExternalText)
++                                           : 0)
++            + cases[u].expectedCountBytesIndirectExtra;
++
++      XML_Parser parser = XML_ParserCreate(NULL);
++      XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
++      if (cases[u].firstExternalText) {
++        XML_SetExternalEntityRefHandler(parser,
++                                        accounting_external_entity_ref_handler);
++        XML_SetUserData(parser, (void *)&cases[u]);
++        cases[u].singleBytesWanted = singleBytesWanted;
++      }
++
++      const XmlParseFunction xmlParseFunction
++          = singleBytesWanted ? _XML_Parse_SINGLE_BYTES : XML_Parse;
++
++      enum XML_Status status
++          = xmlParseFunction(parser, cases[u].primaryText,
++                             (int)strlen(cases[u].primaryText), XML_TRUE);
++      if (status != XML_STATUS_OK) {
++        _xml_failure(parser, __FILE__, __LINE__);
++      }
++
++      const unsigned long long actualCountBytesDirect
++          = testingAccountingGetCountBytesDirect(parser);
++      const unsigned long long actualCountBytesIndirect
++          = testingAccountingGetCountBytesIndirect(parser);
++
++      XML_ParserFree(parser);
++
++      if (actualCountBytesDirect != expectedCountBytesDirect) {
++        fprintf(
++            stderr,
++            "Document " EXPAT_FMT_SIZE_T("") " of " EXPAT_FMT_SIZE_T("") ", %s: Expected " EXPAT_FMT_ULL(
++                "") " count direct bytes, got " EXPAT_FMT_ULL("") " instead.\n",
++            u + 1, countCases, singleBytesWanted ? "single bytes" : "chunks",
++            expectedCountBytesDirect, actualCountBytesDirect);
++        fail("Count of direct bytes is off");
++      }
++
++      if (actualCountBytesIndirect != expectedCountBytesIndirect) {
++        fprintf(
++            stderr,
++            "Document " EXPAT_FMT_SIZE_T("") " of " EXPAT_FMT_SIZE_T("") ", %s: Expected " EXPAT_FMT_ULL(
++                "") " count indirect bytes, got " EXPAT_FMT_ULL("") " instead.\n",
++            u + 1, countCases, singleBytesWanted ? "single bytes" : "chunks",
++            expectedCountBytesIndirect, actualCountBytesIndirect);
++        fail("Count of indirect bytes is off");
++      }
++    }
++  }
++}
++END_TEST
++
++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 20fd92b..901ee9a 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
+PATCH_FILES=$(TARFILE_NAME).patch expat-2.2.11.patch
 
 CONFIGURE_DIR=
 .IF "$(OS)"=="WNT"