Update libexpat to 2.4.1. This fixes CVE-2013-0340. Relevant for
authorbluhm <bluhm@openbsd.org>
Wed, 26 May 2021 19:14:32 +0000 (19:14 +0000)
committerbluhm <bluhm@openbsd.org>
Wed, 26 May 2021 19:14:32 +0000 (19:14 +0000)
OpenBSD are security fixes #34 #466 #484 and other changes #467
#473 #483.  A new error number in a public header requires a major
library bump.  Two functions have been added to API.
OK tb@

38 files changed:
lib/libexpat/Changes
lib/libexpat/Makefile
lib/libexpat/README.md
lib/libexpat/doc/reference.html
lib/libexpat/doc/style.css
lib/libexpat/examples/elements.c
lib/libexpat/examples/outline.c
lib/libexpat/lib/ascii.h
lib/libexpat/lib/asciitab.h
lib/libexpat/lib/expat.h
lib/libexpat/lib/expat_external.h
lib/libexpat/lib/iasciitab.h
lib/libexpat/lib/internal.h
lib/libexpat/lib/latin1tab.h
lib/libexpat/lib/nametab.h
lib/libexpat/lib/utf8tab.h
lib/libexpat/lib/winconfig.h
lib/libexpat/lib/xmlparse.c
lib/libexpat/lib/xmlrole.c
lib/libexpat/lib/xmlrole.h
lib/libexpat/lib/xmltok.c
lib/libexpat/lib/xmltok.h
lib/libexpat/lib/xmltok_impl.c
lib/libexpat/lib/xmltok_impl.h
lib/libexpat/lib/xmltok_ns.c
lib/libexpat/shlib_version
lib/libexpat/tests/benchmark/benchmark.c
lib/libexpat/tests/chardata.c
lib/libexpat/tests/chardata.h
lib/libexpat/tests/memcheck.c
lib/libexpat/tests/memcheck.h
lib/libexpat/tests/minicheck.c
lib/libexpat/tests/minicheck.h
lib/libexpat/tests/runtests.c
lib/libexpat/tests/runtestspp.cpp
lib/libexpat/tests/structdata.c
lib/libexpat/tests/structdata.h
lib/libexpat/tests/xmltest.sh

index edd485c..f765789 100644 (file)
@@ -2,6 +2,97 @@ NOTE: We are looking for help with a few things:
       https://github.com/libexpat/libexpat/labels/help%20wanted
       If you can help, please get in touch.  Thanks!
 
+Release 2.4.1 Sun May 23 2021
+        Bug fixes:
+       #488 #490  Autotools: Fix installed header expat_config.h for multilib
+                    systems; regression introduced in 2.4.0 by pull request #486
+
+        Other changes:
+       #491 #492  Version info bumped from 9:0:8 to 9:1:8;
+                    see https://verbump.de/ for what these numbers do
+
+        Special thanks to:
+            Gentoo's QA check "multilib_check_headers"
+
+Release 2.4.0 Sun May 23 2021
+        Security fixes:
+   #34 #466 #484  CVE-2013-0340/CWE-776 -- Protect against billion laughs attacks
+                    (denial-of-service; flavors targeting CPU time or RAM or both,
+                    leveraging general entities or parameter entities or both)
+                    by tracking and limiting the input amplification factor
+                    (<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.
+                    The fix adds the following to the API:
+                    - A new error code XML_ERROR_AMPLIFICATION_LIMIT_BREACH to
+                      signals this specific condition.
+                    - Two new API functions ..
+                      - XML_SetBillionLaughsAttackProtectionMaximumAmplification and
+                      - XML_SetBillionLaughsAttackProtectionActivationThreshold
+                      .. to further tighten billion laughs protection parameters
+                      when desired.  Please see file "doc/reference.html" for details.
+                      If you ever need to increase the defaults for non-attack XML
+                      payload, please file a bug report with libexpat.
+                    - Two new XML_FEATURE_* constants ..
+                      - that can be queried using the XML_GetFeatureList function, and
+                      - that are shown in "xmlwf -v" output.
+                    - Two new environment variable 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.
+                    - Two new command line arguments "-a FACTOR" and "-b BYTES"
+                      for xmlwf 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.
+
+        Bug fixes:
+       #332 #470  For (non-default) compilation with -DEXPAT_MIN_SIZE=ON (CMake)
+                    or CPPFLAGS=-DXML_MIN_SIZE (GNU Autotools): Fix segfault
+                    for UTF-16 payloads containing CDATA sections.
+       #485 #486  Autotools: Fix generated CMake files for non-64bit and
+                    non-Linux platforms (e.g. macOS and MinGW in particular)
+                    that were introduced with release 2.3.0
+
+        Other changes:
+       #468 #469  xmlwf: Improve help output and the xmlwf man page
+            #463  xmlwf: Improve maintainability through some refactoring
+            #477  xmlwf: Fix man page DocBook validity
+       #458 #459  CMake: Support absolute paths for both CMAKE_INSTALL_LIBDIR
+                    and CMAKE_INSTALL_INCLUDEDIR
+       #471 #481  CMake: Add support for standard variable BUILD_SHARED_LIBS
+            #457  Unexpose symbol _INTERNAL_trim_to_complete_utf8_characters
+            #467  Resolve macro HAVE_EXPAT_CONFIG_H
+            #472  Delete unused legacy helper file "conftools/PrintPath"
+       #473 #483  Improve attribution
+  #464 #465 #477  doc/reference.html: Fix XHTML validity
+       #475 #478  doc/reference.html: Replace the 90s look by OK.css
+            #479  Version info bumped from 8:0:7 to 9:0:8
+                    due to addition of new symbols and error codes;
+                    see https://verbump.de/ for what these numbers do
+
+        Infrastructure:
+            #456  CI: Enable periodic runs
+            #457  CI: Start covering the list of exported symbols
+            #474  CI: Isolate coverage task
+       #476 #482  CI: Adapt to breaking changes in image "ubuntu-18.04"
+            #477  CI: Cover well-formedness and DocBook/XHTML validity
+                    of doc/reference.html and doc/xmlwf.xml
+
+        Special thanks to:
+            Dimitry Andric
+            Eero Helenius
+            Nick Wellnhofer
+            Rhodri James
+            Tomas Korbar
+            Yury Gribov
+                 and
+            Clang LeakSan
+            JetBrains
+            OSS-Fuzz
+
 Release 2.3.0 Thu March 25 2021
         Bug fixes:
             #438  When calling XML_ParseBuffer without a prior successful call to
index 8bb75e7..25b0384 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: Makefile,v 1.15 2019/06/29 00:33:59 bluhm Exp $
+#      $OpenBSD: Makefile,v 1.16 2021/05/26 19:14:32 bluhm Exp $
 
 .PATH: ${.CURDIR}/lib
 
@@ -6,7 +6,7 @@
 
 LIB=   expat
 SRCS=  xmlparse.c xmltok.c xmlrole.c
-CFLAGS+=-I${.CURDIR} -DHAVE_EXPAT_CONFIG_H
+CFLAGS+=-I${.CURDIR}
 .if ${COMPILER_VERSION:L} != "gcc3"
 CFLAGS+=-fvisibility=hidden -DXML_ENABLE_VISIBILITY=1
 .endif
index 6e4b422..251dc8a 100644 (file)
@@ -1,12 +1,14 @@
 [![Run Linux Travis CI tasks](https://github.com/libexpat/libexpat/actions/workflows/linux.yml/badge.svg)](https://github.com/libexpat/libexpat/actions/workflows/linux.yml)
 [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/libexpat/libexpat?svg=true)](https://ci.appveyor.com/project/libexpat/libexpat)
 [![Packaging status](https://repology.org/badge/tiny-repos/expat.svg)](https://repology.org/metapackage/expat/versions)
+[![Downloads SourceForge](https://img.shields.io/sourceforge/dt/expat?label=Downloads%20SourceForge)](https://sourceforge.net/projects/expat/files/)
+[![Downloads GitHub](https://img.shields.io/github/downloads/libexpat/libexpat/total?label=Downloads%20GitHub)](https://github.com/libexpat/libexpat/releases)
 
 
-# Expat, Release 2.3.0
+# Expat, Release 2.4.1
 
 This is Expat, a C library for parsing XML, started by
-[James Clark](https://en.wikipedia.org/wiki/James_Clark_(programmer)) in 1997.
+[James Clark](https://en.wikipedia.org/wiki/James_Clark_%28programmer%29) in 1997.
 Expat is a stream-oriented XML parser.  This means that you register
 handlers with the parser before starting the parse.  These handlers
 are called when the parser discovers the associated structures in the
@@ -20,7 +22,7 @@ Expat supports the following compilers:
 - Microsoft Visual Studio >=15.0/2017 (rolling `${today} minus 5 years`)
 
 Windows users can use the
-[`expat-win32bin-*.*.*.exe` installer download](https://github.com/libexpat/libexpat/releases),
+[`expat-win32bin-*.*.*.{exe,zip}` download](https://github.com/libexpat/libexpat/releases),
 which includes both pre-compiled libraries and executables, and source code for
 developers.
 
@@ -40,10 +42,10 @@ There are two ways of using libexpat with CMake:
 
 This approach leverages CMake's own [module `FindEXPAT`](https://cmake.org/cmake/help/latest/module/FindEXPAT.html).
 
-Notice the uppercase `EXPAT` in the following example:
+Notice the *uppercase* `EXPAT` in the following example:
 
 ```cmake
-cmake_minimum_required(VERSION 3.0)
+cmake_minimum_required(VERSION 3.0)  # or 3.10, see below
 
 project(hello VERSION 1.0.0)
 
@@ -53,22 +55,27 @@ add_executable(hello
     hello.c
 )
 
-if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.10")
-    target_link_libraries(hello PUBLIC EXPAT::EXPAT)
-else()
-    target_include_directories(hello PRIVATE ${EXPAT_INCLUDE_DIRS})
-    target_link_libraries(hello PUBLIC ${EXPAT_LIBRARIES})
-endif()
+# a) for CMake >=3.10 (see CMake's FindEXPAT docs)
+target_link_libraries(hello PUBLIC EXPAT::EXPAT)
+
+# b) for CMake >=3.0
+target_include_directories(hello PRIVATE ${EXPAT_INCLUDE_DIRS})
+target_link_libraries(hello PUBLIC ${EXPAT_LIBRARIES})
 ```
 
 ### b) Config Mode
 
-This approach requires files from
-libexpat >=2.2.8 where packaging uses the CMake build system
+This approach requires files from…
+
+- libexpat >=2.2.8 where packaging uses the CMake build system
+or
+- libexpat >=2.3.0 where packaging uses the GNU Autotools build system
+  on Linux
 or
-libexpat >=2.3.0 where packaging uses the GNU Autotools build system.
+- libexpat >=2.4.0 where packaging uses the GNU Autotools build system
+  on macOS or MinGW.
 
-Notice the lowercase `expat` in the following example:
+Notice the *lowercase* `expat` in the following example:
 
 ```cmake
 cmake_minimum_required(VERSION 3.0)
@@ -85,7 +92,7 @@ target_link_libraries(hello PUBLIC expat::expat)
 ```
 
 
-## Buildung from a Git Clone
+## Building from a Git Clone
 
 If you are building Expat from a check-out from the
 [Git repository](https://github.com/libexpat/libexpat/),
@@ -101,7 +108,7 @@ Once this has been done, follow the same instructions as for building
 from a source distribution.
 
 
-## Buildung from a Source Distribution
+## Building from a Source Distribution
 
 ### a) Building with the configure script (i.e. GNU Autotools)
 
index 1e4780e..309cb24 100644 (file)
@@ -3,26 +3,54 @@
                       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 <html>
 <head>
-<!-- Copyright 1999,2000 Clark Cooper <coopercc@netheaven.com>
-     All rights reserved.
-     This is free software. You may distribute or modify according to
-     the terms of the MIT/X License -->
+<!--
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2000-2004 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2002-2012 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2017-2021 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017      Jakub Wilk <jwilk@jwilk.net>
+   Copyright (c) 2021      Tomas Korbar <tkorbar@redhat.com>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+-->
   <title>Expat XML Parser</title>
   <meta name="author" content="Clark Cooper, coopercc@netheaven.com" />
   <meta http-equiv="Content-Style-Type" content="text/css" />
+  <link href="ok.min.css" rel="stylesheet" type="text/css" />
   <link href="style.css" rel="stylesheet" type="text/css" />
 </head>
 <body>
-  <table cellspacing="0" cellpadding="0" width="100%">
-    <tr>
-      <td class="corner"><img src="expat.png" alt="(Expat logo)" /></td>
-      <td class="banner"><h1>The Expat XML Parser</h1></td>
-    </tr>
-    <tr>
-      <td class="releaseno">Release 2.0.1</td>
-      <td></td>
-    </tr>
-  </table>
+  <div>
+    <h1>
+      The Expat XML Parser
+      <small>Release 2.4.1</small>
+    </h1>
+  </div>
 <div class="content">
 
 <p>Expat is a library, written in C, for parsing XML documents. It's
@@ -120,6 +148,13 @@ interface.</p>
       <li><a href="#XML_GetInputContext">XML_GetInputContext</a></li>
     </ul>
     </li>
+    <li>
+      <a href="#billion-laughs">Billion Laughs Attack Protection</a>
+      <ul>
+        <li><a href="#XML_SetBillionLaughsAttackProtectionMaximumAmplification">XML_SetBillionLaughsAttackProtectionMaximumAmplification</a></li>
+        <li><a href="#XML_SetBillionLaughsAttackProtectionActivationThreshold">XML_SetBillionLaughsAttackProtectionActivationThreshold</a></li>
+      </ul>
+    </li>
     <li><a href="#miscellaneous">Miscellaneous Functions</a>
     <ul>
       <li><a href="#XML_SetUserData">XML_SetUserData</a></li>
@@ -900,7 +935,8 @@ whether the parse can be resumed in the future.</p>
 
 <h3><a name="creation">Parser Creation</a></h3>
 
-<pre class="fcndec" id="XML_ParserCreate">
+<h4 id="XML_ParserCreate">XML_ParserCreate</h4>
+<pre class="fcndec">
 XML_Parser XMLCALL
 XML_ParserCreate(const XML_Char *encoding);
 </pre>
@@ -917,7 +953,8 @@ encoding declaration. There are four built-in encodings:
 Any other value will invoke a call to the UnknownEncodingHandler.
 </div>
 
-<pre class="fcndec" id="XML_ParserCreateNS">
+<h4 id="XML_ParserCreateNS">XML_ParserCreateNS</h4>
+<pre class="fcndec">
 XML_Parser XMLCALL
 XML_ParserCreateNS(const XML_Char *encoding,
                    XML_Char sep);
@@ -936,7 +973,8 @@ the local part will be concatenated without any separator - this is intended
 to support RDF processors. It is a programming error to use the null separator
 with <a href= "#XML_SetReturnNSTriplet">namespace triplets</a>.</div>
 
-<pre class="fcndec" id="XML_ParserCreate_MM">
+<h4 id="XML_ParserCreate_MM">XML_ParserCreate_MM</h4>
+<pre class="fcndec">
 XML_Parser XMLCALL
 XML_ParserCreate_MM(const XML_Char *encoding,
                     const XML_Memory_Handling_Suite *ms,
@@ -958,7 +996,8 @@ and the character pointed at by sep is used as the separator between
 the namespace URI and the local part of the name.</p>
 </div>
 
-<pre class="fcndec" id="XML_ExternalEntityParserCreate">
+<h4 id="XML_ExternalEntityParserCreate">XML_ExternalEntityParserCreate</h4>
+<pre class="fcndec">
 XML_Parser XMLCALL
 XML_ExternalEntityParserCreate(XML_Parser p,
                                const XML_Char *context,
@@ -974,7 +1013,8 @@ changing functions on this parser (unless you want it to act
 differently than the parent parser).
 </div>
 
-<pre class="fcndec" id="XML_ParserFree">
+<h4 id="XML_ParserFree">XML_ParserFree</h4>
+<pre class="fcndec">
 void XMLCALL
 XML_ParserFree(XML_Parser p);
 </pre>
@@ -983,7 +1023,8 @@ Free memory used by the parser. Your application is responsible for
 freeing any memory associated with <a href="#userdata">user data</a>.
 </div>
 
-<pre class="fcndec" id="XML_ParserReset">
+<h4 id="XML_ParserReset">XML_ParserReset</h4>
+<pre class="fcndec">
 XML_Bool XMLCALL
 XML_ParserReset(XML_Parser p,
                 const XML_Char *encoding);
@@ -1014,7 +1055,7 @@ if they apply to the parser created by
 <code><a href= "#XML_ExternalEntityParserCreate"
 >XML_ExternalEntityParserCreate</a></code>.</p>
 
-<p>Note: the <code>len</code> argument passed to these functions
+<p>Note: The <code>len</code> argument passed to these functions
 should be considerably less than the maximum value for an integer,
 as it could create an integer overflow situation if the added
 lengths of a buffer and the unprocessed portion of the previous buffer
@@ -1022,7 +1063,8 @@ exceed the maximum integer value. Input data at the end of a buffer
 will remain unprocessed if it is part of an XML token for which the
 end is not part of that buffer.</p>
 
-<pre class="fcndec" id="XML_Parse">
+<h4 id="XML_Parse">XML_Parse</h4>
+<pre class="fcndec">
 enum XML_Status XMLCALL
 XML_Parse(XML_Parser p,
           const char *s,
@@ -1049,7 +1091,8 @@ If a parse error occurred, it returns <code>XML_STATUS_ERROR</code>.
 Otherwise it returns <code>XML_STATUS_OK</code> value.
 </div>
 
-<pre class="fcndec" id="XML_ParseBuffer">
+<h4 id="XML_ParseBuffer">XML_ParseBuffer</h4>
+<pre class="fcndec">
 enum XML_Status XMLCALL
 XML_ParseBuffer(XML_Parser p,
                 int len,
@@ -1063,7 +1106,8 @@ buffer from Expat with the <code><a href= "#XML_GetBuffer"
 copying of the input.
 </div>
 
-<pre class="fcndec" id="XML_GetBuffer">
+<h4 id="XML_GetBuffer">XML_GetBuffer</h4>
+<pre class="fcndec">
 void * XMLCALL
 XML_GetBuffer(XML_Parser p,
               int len);
@@ -1098,7 +1142,8 @@ for (;;) {
 </pre>
 </div>
 
-<pre class="fcndec" id="XML_StopParser">
+<h4 id="XML_StopParser">XML_StopParser</h4>
+<pre class="fcndec">
 enum XML_Status XMLCALL
 XML_StopParser(XML_Parser p,
                XML_Bool resumable);
@@ -1111,7 +1156,7 @@ XML_StopParser(XML_Parser p,
 call-back handler, except when aborting (when <code>resumable</code>
 is <code>XML_FALSE</code>) an already suspended parser.  Some
 call-backs may still follow because they would otherwise get
-lost, including
+lost, including</p>
 <ul>
   <li> the end element handler for empty elements when stopped in the
        start element handler,</li>
@@ -1120,7 +1165,7 @@ lost, including
   <li> the character data handler when stopped in the character data handler
        while making multiple call-backs on a contiguous chunk of characters,</li>
 </ul>
-and possibly others.</p>
+<p>and possibly others.</p>
 
 <p>This can be called from most handlers, including DTD related
 call-backs, except when parsing an external parameter entity and
@@ -1166,7 +1211,8 @@ implementation of that handler to call <code><a href=
 <p>New in Expat 1.95.8.</p>
 </div>
 
-<pre class="fcndec" id="XML_ResumeParser">
+<h4 id="XML_ResumeParser">XML_ResumeParser</h4>
+<pre class="fcndec">
 enum XML_Status XMLCALL
 XML_ResumeParser(XML_Parser p);
 </pre>
@@ -1191,7 +1237,8 @@ appropriate moment.</p>
 <p>New in Expat 1.95.8.</p>
 </div>
 
-<pre class="fcndec" id="XML_GetParsingStatus">
+<h4 id="XML_GetParsingStatus">XML_GetParsingStatus</h4>
+<pre class="fcndec">
 void XMLCALL
 XML_GetParsingStatus(XML_Parser p,
                      XML_ParsingStatus *status);
@@ -1240,7 +1287,8 @@ Note that you'll receive them in this form independent of the original
 encoding of the document.</p>
 
 <div class="handler">
-<pre class="setter" id="XML_SetStartElementHandler">
+<h4 id="XML_SetStartElementHandler">XML_SetStartElementHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetStartElementHandler(XML_Parser p,
                            XML_StartElementHandler start);
@@ -1261,7 +1309,8 @@ by a null pointer.</p>
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetEndElementHandler">
+<h4 id="XML_SetEndElementHandler">XML_SetEndElementHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetEndElementHandler(XML_Parser p,
                          XML_EndElementHandler);
@@ -1276,7 +1325,8 @@ generates a call to both start and end handlers.</p>
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetElementHandler">
+<h4 id="XML_SetElementHandler">XML_SetElementHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetElementHandler(XML_Parser p,
                       XML_StartElementHandler start,
@@ -1286,7 +1336,8 @@ XML_SetElementHandler(XML_Parser p,
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetCharacterDataHandler">
+<h4 id="XML_SetCharacterDataHandler">XML_SetCharacterDataHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetCharacterDataHandler(XML_Parser p,
                             XML_CharacterDataHandler charhndl)
@@ -1309,7 +1360,8 @@ will continue calling back until the end of the block is reached.</p>
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetProcessingInstructionHandler">
+<h4 id="XML_SetProcessingInstructionHandler">XML_SetProcessingInstructionHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetProcessingInstructionHandler(XML_Parser p,
                                     XML_ProcessingInstructionHandler proc)
@@ -1327,7 +1379,8 @@ it after skipping all whitespace after the initial word.</p>
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetCommentHandler">
+<h4 id="XML_SetCommentHandler">XML_SetCommentHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetCommentHandler(XML_Parser p,
                       XML_CommentHandler cmnt)
@@ -1342,7 +1395,8 @@ delimiters.</p>
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetStartCdataSectionHandler">
+<h4 id="XML_SetStartCdataSectionHandler">XML_SetStartCdataSectionHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetStartCdataSectionHandler(XML_Parser p,
                                 XML_StartCdataSectionHandler start);
@@ -1355,7 +1409,8 @@ typedef void
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetEndCdataSectionHandler">
+<h4 id="XML_SetEndCdataSectionHandler">XML_SetEndCdataSectionHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetEndCdataSectionHandler(XML_Parser p,
                               XML_EndCdataSectionHandler end);
@@ -1368,7 +1423,8 @@ typedef void
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetCdataSectionHandler">
+<h4 id="XML_SetCdataSectionHandler">XML_SetCdataSectionHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetCdataSectionHandler(XML_Parser p,
                            XML_StartCdataSectionHandler start,
@@ -1378,7 +1434,8 @@ XML_SetCdataSectionHandler(XML_Parser p,
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetDefaultHandler">
+<h4 id="XML_SetDefaultHandler">XML_SetDefaultHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetDefaultHandler(XML_Parser p,
                       XML_DefaultHandler hndl)
@@ -1409,7 +1466,8 @@ href="#XML_DefaultCurrent">XML_DefaultCurrent</a></code>.</p>
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetDefaultHandlerExpand">
+<h4 id="XML_SetDefaultHandlerExpand">XML_SetDefaultHandlerExpand</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetDefaultHandlerExpand(XML_Parser p,
                             XML_DefaultHandler hndl)
@@ -1429,7 +1487,8 @@ href="#XML_DefaultCurrent">XML_DefaultCurrent</a></code>.</p>
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetExternalEntityRefHandler">
+<h4 id="XML_SetExternalEntityRefHandler">XML_SetExternalEntityRefHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetExternalEntityRefHandler(XML_Parser p,
                                 XML_ExternalEntityRefHandler hndl)
@@ -1482,7 +1541,8 @@ parser, the body of the external entity can be recursively parsed.</p>
 information into global or static variables.</p>
 </div>
 
-<pre class="fcndec" id="XML_SetExternalEntityRefHandlerArg">
+<h4 id="XML_SetExternalEntityRefHandlerArg">XML_SetExternalEntityRefHandlerArg</h4>
+<pre class="fcndec">
 void XMLCALL
 XML_SetExternalEntityRefHandlerArg(XML_Parser p,
                                    void *arg)
@@ -1508,7 +1568,8 @@ properly.</p>
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetSkippedEntityHandler">
+<h4 id="XML_SetSkippedEntityHandler">XML_SetSkippedEntityHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetSkippedEntityHandler(XML_Parser p,
                             XML_SkippedEntityHandler handler)
@@ -1528,14 +1589,15 @@ typedef void
           has been called.</li>
 </ol>
 <p>The <code>is_parameter_entity</code> argument will be non-zero for
-a parameter entity and zero for a general entity.</p> <p>Note: skipped
+a parameter entity and zero for a general entity.</p> <p>Note: Skipped
 parameter entities in declarations and skipped general entities in
 attribute values cannot be reported, because the event would be out of
 sync with the reporting of the declarations or attribute values</p>
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetUnknownEncodingHandler">
+<h4 id="XML_SetUnknownEncodingHandler">XML_SetUnknownEncodingHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetUnknownEncodingHandler(XML_Parser p,
                               XML_UnknownEncodingHandler enchandler,
@@ -1584,7 +1646,8 @@ parser when it is finished with the encoding. It may be NULL.</p>
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetStartNamespaceDeclHandler">
+<h4 id="XML_SetStartNamespaceDeclHandler">XML_SetStartNamespaceDeclHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetStartNamespaceDeclHandler(XML_Parser p,
                                 XML_StartNamespaceDeclHandler start);
@@ -1602,7 +1665,8 @@ in that start tag.</p>
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetEndNamespaceDeclHandler">
+<h4 id="XML_SetEndNamespaceDeclHandler">XML_SetEndNamespaceDeclHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetEndNamespaceDeclHandler(XML_Parser p,
                               XML_EndNamespaceDeclHandler end);
@@ -1619,7 +1683,8 @@ namespace was declared.</p>
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetNamespaceDeclHandler">
+<h4 id="XML_SetNamespaceDeclHandler">XML_SetNamespaceDeclHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetNamespaceDeclHandler(XML_Parser p,
                             XML_StartNamespaceDeclHandler start,
@@ -1629,7 +1694,8 @@ XML_SetNamespaceDeclHandler(XML_Parser p,
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetXmlDeclHandler">
+<h4 id="XML_SetXmlDeclHandler">XML_SetXmlDeclHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetXmlDeclHandler(XML_Parser p,
                      XML_XmlDeclHandler xmldecl);
@@ -1652,7 +1718,8 @@ that it was given as yes.</p>
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetStartDoctypeDeclHandler">
+<h4 id="XML_SetStartDoctypeDeclHandler">XML_SetStartDoctypeDeclHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetStartDoctypeDeclHandler(XML_Parser p,
                               XML_StartDoctypeDeclHandler start);
@@ -1672,7 +1739,8 @@ will be non-zero if the DOCTYPE declaration has an internal subset.</p>
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetEndDoctypeDeclHandler">
+<h4 id="XML_SetEndDoctypeDeclHandler">XML_SetEndDoctypeDeclHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetEndDoctypeDeclHandler(XML_Parser p,
                             XML_EndDoctypeDeclHandler end);
@@ -1686,7 +1754,8 @@ after parsing any external subset.</p>
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetDoctypeDeclHandler">
+<h4 id="XML_SetDoctypeDeclHandler">XML_SetDoctypeDeclHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetDoctypeDeclHandler(XML_Parser p,
                          XML_StartDoctypeDeclHandler start,
@@ -1696,7 +1765,8 @@ XML_SetDoctypeDeclHandler(XML_Parser p,
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetElementDeclHandler">
+<h4 id="XML_SetElementDeclHandler">XML_SetElementDeclHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetElementDeclHandler(XML_Parser p,
                          XML_ElementDeclHandler eldecl);
@@ -1768,7 +1838,8 @@ or sequence and <code>children</code> points to the nodes.</p>
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetAttlistDeclHandler">
+<h4 id="XML_SetAttlistDeclHandler">XML_SetAttlistDeclHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetAttlistDeclHandler(XML_Parser p,
                           XML_AttlistDeclHandler attdecl);
@@ -1801,7 +1872,8 @@ in the <code>dflt</code> parameter.</p>
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetEntityDeclHandler">
+<h4 id="XML_SetEntityDeclHandler">XML_SetEntityDeclHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetEntityDeclHandler(XML_Parser p,
                         XML_EntityDeclHandler handler);
@@ -1835,7 +1907,8 @@ declarations.</p>
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetUnparsedEntityDeclHandler">
+<h4 id="XML_SetUnparsedEntityDeclHandler">XML_SetUnparsedEntityDeclHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetUnparsedEntityDeclHandler(XML_Parser p,
                                  XML_UnparsedEntityDeclHandler h)
@@ -1861,7 +1934,8 @@ compatibility.  Use instead <a href= "#XML_SetEntityDeclHandler"
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetNotationDeclHandler">
+<h4 id="XML_SetNotationDeclHandler">XML_SetNotationDeclHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetNotationDeclHandler(XML_Parser p,
                            XML_NotationDeclHandler h)
@@ -1878,7 +1952,8 @@ typedef void
 </div>
 
 <div class="handler">
-<pre class="setter" id="XML_SetNotStandaloneHandler">
+<h4 id="XML_SetNotStandaloneHandler">XML_SetNotStandaloneHandler</h4>
+<pre class="setter">
 void XMLCALL
 XML_SetNotStandaloneHandler(XML_Parser p,
                             XML_NotStandaloneHandler h)
@@ -1913,7 +1988,8 @@ events.</p>
 DTD.  In other words, they usually return bogus information when
 called from within a DTD declaration handler.</p>
 
-<pre class="fcndec" id="XML_GetErrorCode">
+<h4 id="XML_GetErrorCode">XML_GetErrorCode</h4>
+<pre class="fcndec">
 enum XML_Error XMLCALL
 XML_GetErrorCode(XML_Parser p);
 </pre>
@@ -1921,7 +1997,8 @@ XML_GetErrorCode(XML_Parser p);
 Return what type of error has occurred.
 </div>
 
-<pre class="fcndec" id="XML_ErrorString">
+<h4 id="XML_ErrorString">XML_ErrorString</h4>
+<pre class="fcndec">
 const XML_LChar * XMLCALL
 XML_ErrorString(enum XML_Error code);
 </pre>
@@ -1931,7 +2008,8 @@ The code should be one of the enums that can be returned from
 <code><a href= "#XML_GetErrorCode" >XML_GetErrorCode</a></code>.
 </div>
 
-<pre class="fcndec" id="XML_GetCurrentByteIndex">
+<h4 id="XML_GetCurrentByteIndex">XML_GetCurrentByteIndex</h4>
+<pre class="fcndec">
 XML_Index XMLCALL
 XML_GetCurrentByteIndex(XML_Parser p);
 </pre>
@@ -1942,7 +2020,8 @@ the values returned by <code><a href= "#XML_GetCurrentLineNumber"
 "#XML_GetCurrentColumnNumber" >XML_GetCurrentColumnNumber</a></code>.
 </div>
 
-<pre class="fcndec" id="XML_GetCurrentLineNumber">
+<h4 id="XML_GetCurrentLineNumber">XML_GetCurrentLineNumber</h4>
+<pre class="fcndec">
 XML_Size XMLCALL
 XML_GetCurrentLineNumber(XML_Parser p);
 </pre>
@@ -1951,7 +2030,8 @@ Return the line number of the position.  The first line is reported as
 <code>1</code>.
 </div>
 
-<pre class="fcndec" id="XML_GetCurrentColumnNumber">
+<h4 id="XML_GetCurrentColumnNumber">XML_GetCurrentColumnNumber</h4>
+<pre class="fcndec">
 XML_Size XMLCALL
 XML_GetCurrentColumnNumber(XML_Parser p);
 </pre>
@@ -1960,7 +2040,8 @@ Return the offset, from the beginning of the current line, of
 the position.
 </div>
 
-<pre class="fcndec" id="XML_GetCurrentByteCount">
+<h4 id="XML_GetCurrentByteCount">XML_GetCurrentByteCount</h4>
+<pre class="fcndec">
 int XMLCALL
 XML_GetCurrentByteCount(XML_Parser p);
 </pre>
@@ -1972,7 +2053,8 @@ be used to distinguish empty-element tags from empty elements using
 separate start and end tags).
 </div>
 
-<pre class="fcndec" id="XML_GetInputContext">
+<h4 id="XML_GetInputContext">XML_GetInputContext</h4>
+<pre class="fcndec">
 const char * XMLCALL
 XML_GetInputContext(XML_Parser p,
                     int *offset,
@@ -1998,12 +2080,105 @@ parse position may be before the beginning of the buffer.</p>
 return NULL.</p>
 </div>
 
+<h3><a name="billion-laughs">Billion Laughs Attack Protection</a></h3>
+
+<p>The functions in this section configure the built-in
+  protection against various forms of
+  <a href="https://en.wikipedia.org/wiki/Billion_laughs_attack">billion laughs attacks</a>.</p>
+
+<h4 id="XML_SetBillionLaughsAttackProtectionMaximumAmplification">XML_SetBillionLaughsAttackProtectionMaximumAmplification</h4>
+<pre class="fcndec">
+/* Added in Expat 2.4.0. */
+XML_Bool XMLCALL
+XML_SetBillionLaughsAttackProtectionMaximumAmplification(XML_Parser p,
+                                                         float maximumAmplificationFactor);
+</pre>
+<div class="fcndef">
+  <p>
+    Sets the maximum tolerated amplification factor
+    for protection against
+    <a href="https://en.wikipedia.org/wiki/Billion_laughs_attack">billion laughs attacks</a>
+    (default: <code>100.0</code>)
+    of parser <code>p</code> to <code>maximumAmplificationFactor</code>, and
+    returns <code>XML_TRUE</code> upon success and <code>XML_TRUE</code> upon error.
+  </p>
+
+  The amplification factor is calculated as ..
+  <pre>
+    amplification := (direct + indirect) / direct
+  </pre>
+  .. while parsing, whereas
+  <code>direct</code> is the number of bytes read from the primary document in parsing and
+  <code>indirect</code> is the number of bytes added by expanding entities and reading of external DTD files, combined.
+
+  <p>For a call to <code>XML_SetBillionLaughsAttackProtectionMaximumAmplification</code> to succeed:</p>
+  <ul>
+    <li>parser <code>p</code> must be a non-<code>NULL</code> root parser (without any parent parsers) and</li>
+    <li><code>maximumAmplificationFactor</code> must be non-<code>NaN</code> and greater than or equal to <code>1.0</code>.</li>
+  </ul>
+
+  <p>
+    <strong>Note:</strong>
+    If you ever need to increase this value for non-attack payload,
+    please <a href="https://github.com/libexpat/libexpat/issues">file a bug report</a>.
+  </p>
+
+  <p>
+    <strong>Note:</strong>
+    Peak amplifications
+    of factor 15,000 for the entire payload and
+    of factor 30,000 in the middle of parsing
+    have been observed with small benign files in practice.
+
+    So if you do reduce the maximum allowed amplification,
+    please make sure that the activation threshold is still big enough
+    to not end up with undesired false positives (i.e. benign files being rejected).
+  </p>
+</div>
+
+<h4 id="XML_SetBillionLaughsAttackProtectionActivationThreshold">XML_SetBillionLaughsAttackProtectionActivationThreshold</h4>
+<pre class="fcndec">
+/* Added in Expat 2.4.0. */
+XML_Bool XMLCALL
+XML_SetBillionLaughsAttackProtectionActivationThreshold(XML_Parser p,
+                                                        unsigned long long activationThresholdBytes);
+</pre>
+<div class="fcndef">
+  <p>
+    Sets number of output bytes (including amplification from entity expansion and reading DTD files)
+    needed to activate protection against
+    <a href="https://en.wikipedia.org/wiki/Billion_laughs_attack">billion laughs attacks</a>
+    (default: <code>8 MiB</code>)
+    of parser <code>p</code> to <code>activationThresholdBytes</code>, and
+    returns <code>XML_TRUE</code> upon success and <code>XML_TRUE</code> upon error.
+  </p>
+
+  <p>For a call to <code>XML_SetBillionLaughsAttackProtectionActivationThreshold</code> to succeed:</p>
+  <ul>
+    <li>parser <code>p</code> must be a non-<code>NULL</code> root parser (without any parent parsers).</li>
+  </ul>
+
+  <p>
+    <strong>Note:</strong>
+    If you ever need to increase this value for non-attack payload,
+    please <a href="https://github.com/libexpat/libexpat/issues">file a bug report</a>.
+  </p>
+
+  <p>
+    <strong>Note:</strong>
+    Activation thresholds below 4 MiB are known to break support for
+    <a href="https://en.wikipedia.org/wiki/Darwin_Information_Typing_Architecture">DITA</a> 1.3 payload
+    and are hence not recommended.
+  </p>
+</div>
+
 <h3><a name="miscellaneous">Miscellaneous functions</a></h3>
 
 <p>The functions in this section either obtain state information from
 the parser or can be used to dynamically set parser options.</p>
 
-<pre class="fcndec" id="XML_SetUserData">
+<h4 id="XML_SetUserData">XML_SetUserData</h4>
+<pre class="fcndec">
 void XMLCALL
 XML_SetUserData(XML_Parser p,
                 void *userData);
@@ -2018,7 +2193,8 @@ the memory associated with it, then you've probably just leaked
 memory.
 </div>
 
-<pre class="fcndec" id="XML_GetUserData">
+<h4 id="XML_GetUserData">XML_GetUserData</h4>
+<pre class="fcndec">
 void * XMLCALL
 XML_GetUserData(XML_Parser p);
 </pre>
@@ -2027,7 +2203,8 @@ This returns the user data pointer that gets passed to handlers.
 It is actually implemented as a macro.
 </div>
 
-<pre class="fcndec" id="XML_UseParserAsHandlerArg">
+<h4 id="XML_UseParserAsHandlerArg">XML_UseParserAsHandlerArg</h4>
+<pre class="fcndec">
 void XMLCALL
 XML_UseParserAsHandlerArg(XML_Parser p);
 </pre>
@@ -2038,7 +2215,8 @@ using the <code><a href= "#XML_GetUserData"
 >XML_GetUserData</a></code> function.
 </div>
 
-<pre class="fcndec" id="XML_SetBase">
+<h4 id="XML_SetBase">XML_SetBase</h4>
+<pre class="fcndec">
 enum XML_Status XMLCALL
 XML_SetBase(XML_Parser p,
             const XML_Char *base);
@@ -2050,7 +2228,8 @@ there's no memory to store base, otherwise it's
 <code>XML_STATUS_OK</code>.
 </div>
 
-<pre class="fcndec" id="XML_GetBase">
+<h4 id="XML_GetBase">XML_GetBase</h4>
+<pre class="fcndec">
 const XML_Char * XMLCALL
 XML_GetBase(XML_Parser p);
 </pre>
@@ -2058,7 +2237,8 @@ XML_GetBase(XML_Parser p);
 Return the base for resolving relative URIs.
 </div>
 
-<pre class="fcndec" id="XML_GetSpecifiedAttributeCount">
+<h4 id="XML_GetSpecifiedAttributeCount">XML_GetSpecifiedAttributeCount</h4>
+<pre class="fcndec">
 int XMLCALL
 XML_GetSpecifiedAttributeCount(XML_Parser p);
 </pre>
@@ -2074,7 +2254,8 @@ call to a start handler. If called inside a start handler, then that
 means the current call.
 </div>
 
-<pre class="fcndec" id="XML_GetIdAttributeIndex">
+<h4 id="XML_GetIdAttributeIndex">XML_GetIdAttributeIndex</h4>
+<pre class="fcndec">
 int XMLCALL
 XML_GetIdAttributeIndex(XML_Parser p);
 </pre>
@@ -2086,7 +2267,8 @@ attribute. If called inside a start handler, then that means the
 current call.
 </div>
 
-<pre class="fcndec" id="XML_GetAttributeInfo">
+<h4 id="XML_GetAttributeInfo">XML_GetAttributeInfo</h4>
+<pre class="fcndec">
 const XML_AttrInfo * XMLCALL
 XML_GetAttributeInfo(XML_Parser parser);
 </pre>
@@ -2107,7 +2289,8 @@ as 1; thus the number of entries in the array is
 <code>XML_GetSpecifiedAttributeCount(parser) / 2</code>.
 </div>
 
-<pre class="fcndec" id="XML_SetEncoding">
+<h4 id="XML_SetEncoding">XML_SetEncoding</h4>
+<pre class="fcndec">
 enum XML_Status XMLCALL
 XML_SetEncoding(XML_Parser p,
                 const XML_Char *encoding);
@@ -2122,7 +2305,8 @@ Returns <code>XML_STATUS_OK</code> on success or
 <code>XML_STATUS_ERROR</code> on error.
 </div>
 
-<pre class="fcndec" id="XML_SetParamEntityParsing">
+<h4 id="XML_SetParamEntityParsing">XML_SetParamEntityParsing</h4>
+<pre class="fcndec">
 int XMLCALL
 XML_SetParamEntityParsing(XML_Parser p,
                           enum XML_ParamEntityParsing code);
@@ -2142,7 +2326,8 @@ The choices for <code>code</code> are:
 no effect and will always return 0.
 </div>
 
-<pre class="fcndec" id="XML_SetHashSalt">
+<h4 id="XML_SetHashSalt">XML_SetHashSalt</h4>
+<pre class="fcndec">
 int XMLCALL
 XML_SetHashSalt(XML_Parser p,
                 unsigned long hash_salt);
@@ -2153,15 +2338,16 @@ Helps in preventing DoS attacks based on predicting hash
 function behavior. In order to have an effect this must be called
 before parsing has started. Returns 1 if successful, 0 when called
 after <code>XML_Parse</code> or <code>XML_ParseBuffer</code>.
-<p><b>Note:</b>This call is optional, as the parser will auto-generate 
+<p><b>Note:</b> This call is optional, as the parser will auto-generate
 a new random salt value if no value has been set at the start of parsing.</p>
-<p><b>Note:</b>One should not call <code>XML_SetHashSalt</code> with a
+<p><b>Note:</b> One should not call <code>XML_SetHashSalt</code> with a
 hash salt value of 0, as this value is used as sentinel value to indicate
 that <code>XML_SetHashSalt</code> has <b>not</b> been called. Consequently
 such a call will have no effect, even if it returns 1.</p>
 </div>
 
-<pre class="fcndec" id="XML_UseForeignDTD">
+<h4 id="XML_UseForeignDTD">XML_UseForeignDTD</h4>
+<pre class="fcndec">
 enum XML_Error XMLCALL
 XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD);
 </pre>
@@ -2198,7 +2384,8 @@ the document had a DTD with an external subset. This holds true even if
 the external entity reference handler returns without action.</p>
 </div>
 
-<pre class="fcndec" id="XML_SetReturnNSTriplet">
+<h4 id="XML_SetReturnNSTriplet">XML_SetReturnNSTriplet</h4>
+<pre class="fcndec">
 void XMLCALL
 XML_SetReturnNSTriplet(XML_Parser parser,
                        int        do_nst);
@@ -2220,7 +2407,8 @@ default manner, URI then local_name separated by the namespace
 separator.</p>
 </div>
 
-<pre class="fcndec" id="XML_DefaultCurrent">
+<h4 id="XML_DefaultCurrent">XML_DefaultCurrent</h4>
+<pre class="fcndec">
 void XMLCALL
 XML_DefaultCurrent(XML_Parser parser);
 </pre>
@@ -2234,7 +2422,8 @@ href="#XML_SetDefaultHandler" >XML_SetDefaultHandler</a></code> or
 not a default handler.
 </div>
 
-<pre class="fcndec" id="XML_ExpatVersion">
+<h4 id="XML_ExpatVersion">XML_ExpatVersion</h4>
+<pre class="fcndec">
 XML_LChar * XMLCALL
 XML_ExpatVersion();
 </pre>
@@ -2242,7 +2431,8 @@ XML_ExpatVersion();
 Return the library version as a string (e.g. <code>"expat_1.95.1"</code>).
 </div>
 
-<pre class="fcndec" id="XML_ExpatVersionInfo">
+<h4 id="XML_ExpatVersionInfo">XML_ExpatVersionInfo</h4>
+<pre class="fcndec">
 struct XML_Expat_Version XMLCALL
 XML_ExpatVersionInfo();
 </pre>
@@ -2266,7 +2456,8 @@ Testing these constants is currently the best way to determine if
 particular parts of the Expat API are available.
 </div>
 
-<pre class="fcndec" id="XML_GetFeatureList">
+<h4 id="XML_GetFeatureList">XML_GetFeatureList</h4>
+<pre class="fcndec">
 const XML_Feature * XMLCALL
 XML_GetFeatureList();
 </pre>
@@ -2327,7 +2518,8 @@ time, the following features have been defined to have values:</p>
 </dl>
 </div>
 
-<pre class="fcndec" id="XML_FreeContentModel">
+<h4 id="XML_FreeContentModel">XML_FreeContentModel</h4>
+<pre class="fcndec">
 void XMLCALL
 XML_FreeContentModel(XML_Parser parser, XML_Content *model);
 </pre>
@@ -2346,7 +2538,8 @@ applications.  This can be essential when using dynamically loaded
 libraries which use different C standard libraries (this can happen on
 Windows, at least).</p>
 
-<pre class="fcndec" id="XML_MemMalloc">
+<h4 id="XML_MemMalloc">XML_MemMalloc</h4>
+<pre class="fcndec">
 void * XMLCALL
 XML_MemMalloc(XML_Parser parser, size_t size);
 </pre>
@@ -2358,7 +2551,8 @@ way must be freed using <code><a href="#XML_MemFree"
 >XML_MemFree</a></code>.
 </div>
 
-<pre class="fcndec" id="XML_MemRealloc">
+<h4 id="XML_MemRealloc">XML_MemRealloc</h4>
+<pre class="fcndec">
 void * XMLCALL
 XML_MemRealloc(XML_Parser parser, void *ptr, size_t size);
 </pre>
@@ -2377,7 +2571,8 @@ original block.  Memory allocated in this way must be freed using
 >XML_MemFree</a></code>.
 </div>
 
-<pre class="fcndec" id="XML_MemFree">
+<h4 id="XML_MemFree">XML_MemFree</h4>
+<pre class="fcndec">
 void XMLCALL
 XML_MemFree(XML_Parser parser, void *ptr);
 </pre>
@@ -2388,9 +2583,12 @@ have been allocated by <code><a href="#XML_MemMalloc"
 </div>
 
 <hr />
-<p><a href="http://validator.w3.org/check/referer"><img
-        src="valid-xhtml10.png" alt="Valid XHTML 1.0!"
-        height="31" width="88" class="noborder" /></a></p>
+
+  <div class="footer">
+    Found a bug in the documentation?
+    <a href="https://github.com/libexpat/libexpat/issues">Please file a bug report.</a>
+  </div>
+
 </div>
 </body>
 </html>
index 69df30b..1b8cd10 100644 (file)
+/*
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2000-2004 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2021      Sebastian Pipping <sebastian@pipping.org>
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+/* Stop not using half the screen */
 body {
-  background-color: white;
-  border: 0px;
-  margin: 0px;
-  padding: 0px;
-}
-
-.corner {
-  width: 200px;
-  height: 80px;
-  text-align: center;
-}
-
-.banner {
-  background-color: rgb(110,139,61);
-  color: rgb(255,236,176);
-  padding-left: 2em;
-}
-
-.banner h1 {
-  font-size: 200%;
-}
-
-.content {
-  padding: 0em 2em 1em 2em;
-}
-
-.releaseno {
-  background-color: rgb(110,139,61);
-  color: rgb(255,236,176);
-  padding-bottom: 0.3em;
-  padding-top: 0.5em;
-  text-align: center;
-  font-weight: bold;
-}
-
-.noborder {
-  border-width: 0px;
-}
-
-.eg {
-  padding-left: 1em;
-  padding-top: .5em;
-  padding-bottom: .5em;
-  border: solid thin;
-  margin: 1em 0;
-  background-color: tan;
-  margin-left: 2em;
-  margin-right: 10%;
-}
-
-.pseudocode {
-  padding-left: 1em;
-  padding-top: .5em;
-  padding-bottom: .5em;
-  border: solid thin;
-  margin: 1em 0;
-  background-color: rgb(250,220,180);
-  margin-left: 2em;
-  margin-right: 10%;
-}
-
-.handler {
-  width: 100%;
-  border-top-width: thin;  
-  margin-bottom: 1em;
-}
-
-.handler p {
-  margin-left: 2em;
-}
-
-.setter {
-  font-weight: bold;
-}
-
-.signature {
-  color: navy;
-}
-
-.fcndec {
-  width: 100%;
-  border-top-width: thin;
-  font-weight: bold;
-}
-
-.fcndef {
-  margin-left: 2em;
-  margin-bottom: 2em;
-}
-
-dd {
-  margin-bottom: 2em;
+  max-width: none; /* was: 80ch */
 }
 
 .cpp-symbols dt {
   font-family: monospace;
 }
-.cpp-symbols dd {
-  margin-bottom: 1em;
+
+/* Resemble style of <footer> which is not part of xhtml1-strict */
+.footer {
+  font-size: var(--ok-fs-5);
+  color: var(--ok-tc-1);
 }
index eb0c729..2b66456 100644 (file)
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2001-2003 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2004-2006 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2005-2007 Steven Solie <ssolie@users.sourceforge.net>
+   Copyright (c) 2016-2019 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2019      Zhongyuan Zhou <zhouzhongyuan@huawei.com>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
index d996b8e..4ed041f 100644 (file)
@@ -8,8 +8,12 @@
                         \___/_/\_\ .__/ \__,_|\__|
                                  |_| XML parser
 
-   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2001-2003 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2005-2007 Steven Solie <ssolie@users.sourceforge.net>
+   Copyright (c) 2005-2006 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2016-2019 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
index c3587e5..1f594d2 100644 (file)
@@ -6,8 +6,11 @@
                         \___/_/\_\ .__/ \__,_|\__|
                                  |_| XML parser
 
-   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 1999-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2002      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2007      Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2017      Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
index 63b1d1b..af766fb 100644 (file)
@@ -7,7 +7,9 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2002      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2017      Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
index 7aa60f3..b7d6d35 100644 (file)
@@ -7,7 +7,14 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2000-2005 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2001-2002 Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2002-2016 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2016-2021 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2016      Cristian Rodríguez <crrodriguez@opensuse.org>
+   Copyright (c) 2016      Thomas Beutlich <tc@tbeu.de>
+   Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -117,7 +124,9 @@ enum XML_Error {
   /* Added in 2.2.1. */
   XML_ERROR_INVALID_ARGUMENT,
   /* Added in 2.3.0. */
-  XML_ERROR_NO_BUFFER
+  XML_ERROR_NO_BUFFER,
+  /* Added in 2.4.0. */
+  XML_ERROR_AMPLIFICATION_LIMIT_BREACH
 };
 
 enum XML_Content_Type {
@@ -999,7 +1008,10 @@ enum XML_FeatureEnum {
   XML_FEATURE_SIZEOF_XML_LCHAR,
   XML_FEATURE_NS,
   XML_FEATURE_LARGE_SIZE,
-  XML_FEATURE_ATTR_INFO
+  XML_FEATURE_ATTR_INFO,
+  /* Added in Expat 2.4.0. */
+  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. */
 };
 
@@ -1012,12 +1024,24 @@ typedef struct {
 XMLPARSEAPI(const XML_Feature *)
 XML_GetFeatureList(void);
 
+#ifdef XML_DTD
+/* Added in Expat 2.4.0. */
+XMLPARSEAPI(XML_Bool)
+XML_SetBillionLaughsAttackProtectionMaximumAmplification(
+    XML_Parser parser, float maximumAmplificationFactor);
+
+/* Added in Expat 2.4.0. */
+XMLPARSEAPI(XML_Bool)
+XML_SetBillionLaughsAttackProtectionActivationThreshold(
+    XML_Parser parser, unsigned long long activationThresholdBytes);
+#endif
+
 /* Expat follows the semantic versioning convention.
    See http://semver.org.
 */
 #define XML_MAJOR_VERSION 2
-#define XML_MINOR_VERSION 3
-#define XML_MICRO_VERSION 0
+#define XML_MINOR_VERSION 4
+#define XML_MICRO_VERSION 1
 
 #ifdef __cplusplus
 }
index b3b6e74..8829f77 100644 (file)
@@ -7,7 +7,14 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2000-2004 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2001-2002 Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2002-2006 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2016      Cristian Rodríguez <crrodriguez@opensuse.org>
+   Copyright (c) 2016-2019 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2018      Yury Gribov <tetra2005@gmail.com>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
index ea97cfc..5d8646f 100644 (file)
@@ -7,7 +7,9 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2002      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2017      Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
index d11ba1a..444eba0 100644 (file)
                         \___/_/\_\ .__/ \__,_|\__|
                                  |_| XML parser
 
-   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2002-2003 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2002-2006 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2003      Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2016-2021 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2018      Yury Gribov <tetra2005@gmail.com>
+   Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
 #  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
 
-void
-_INTERNAL_trim_to_complete_utf8_characters(const char *from,
-                                           const char **fromLimRef);
+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
 }
index 6f91604..b681d27 100644 (file)
@@ -7,7 +7,9 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2002      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2017      Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
index 3681df3..6348544 100644 (file)
@@ -6,8 +6,8 @@
                         \___/_/\_\ .__/ \__,_|\__|
                                  |_| XML parser
 
-   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2017 Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
index a22986a..88efcf9 100644 (file)
@@ -7,7 +7,9 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2002      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2017      Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
index 562a4a8..2ecd61b 100644 (file)
@@ -6,8 +6,10 @@
                         \___/_/\_\ .__/ \__,_|\__|
                                  |_| XML parser
 
-   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2002      Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2005      Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2017-2021 Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
 #include <memory.h>
 #include <string.h>
 
-#if defined(HAVE_EXPAT_CONFIG_H) /* e.g. MinGW */
-#  include <expat_config.h>
-#else /* !defined(HAVE_EXPAT_CONFIG_H) */
-
-#  define XML_NS 1
-#  define XML_DTD 1
-#  define XML_CONTEXT_BYTES 1024
-
-/* we will assume all Windows platforms are little endian */
-#  define BYTEORDER 1234
-
-#endif /* !defined(HAVE_EXPAT_CONFIG_H) */
-
 #endif /* ndef WINCONFIG_H */
index f71b60e..db8132d 100644 (file)
@@ -1,4 +1,4 @@
-/* d667b5f8e56e24fdfaf5e38596d419d924a9fadceb987d81d5613ecb7ca51b0e (2.3.0+)
+/* 8539b9040d9d901366a62560a064af7cb99811335784b363abc039c5b0ebc416 (2.4.1+)
                             __  __            _
                          ___\ \/ /_ __   __ _| |_
                         / _ \\  /| '_ \ / _` | __|
@@ -7,7 +7,31 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2000-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2001-2002 Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2002-2016 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2005-2009 Steven Solie <ssolie@users.sourceforge.net>
+   Copyright (c) 2016      Eric Rahm <erahm@mozilla.com>
+   Copyright (c) 2016-2021 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2016      Gaurav <g.gupta@samsung.com>
+   Copyright (c) 2016      Thomas Beutlich <tc@tbeu.de>
+   Copyright (c) 2016      Gustavo Grieco <gustavo.grieco@imag.fr>
+   Copyright (c) 2016      Pascal Cuoq <cuoq@trust-in-soft.com>
+   Copyright (c) 2016      Ed Schouten <ed@nuxi.nl>
+   Copyright (c) 2017-2018 Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2017      Václav Slavík <vaclav@slavik.io>
+   Copyright (c) 2017      Viktor Szakats <commit@vsz.me>
+   Copyright (c) 2017      Chanho Park <chanho61.park@samsung.com>
+   Copyright (c) 2017      Rolf Eike Beer <eike@sf-mail.de>
+   Copyright (c) 2017      Hans Wennborg <hans@chromium.org>
+   Copyright (c) 2018      Anton Maklakov <antmak.pub@gmail.com>
+   Copyright (c) 2018      Benjamin Peterson <benjamin@python.org>
+   Copyright (c) 2018      Marco Maggi <marco.maggi-ipsu@poste.it>
+   Copyright (c) 2018      Mariusz Zaborski <oshogbo@vexillium.org>
+   Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
+   Copyright (c) 2019-2020 Ben Wagner <bungeman@chromium.org>
+   Copyright (c) 2019      Vadim Zeitlin <vadim@zeitlins.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -48,6 +72,7 @@
 #include <stdio.h>  /* fprintf */
 #include <stdlib.h> /* getenv, rand_s */
 #include <stdint.h> /* uintptr_t */
+#include <math.h>   /* isnan */
 
 #ifdef _WIN32
 #  define getpid GetCurrentProcessId
@@ -63,9 +88,9 @@
 
 #ifdef _WIN32
 #  include "winconfig.h"
-#elif defined(HAVE_EXPAT_CONFIG_H)
-#  include <expat_config.h>
-#endif /* ndef _WIN32 */
+#endif
+
+#include <expat_config.h>
 
 #include "ascii.h"
 #include "expat.h"
@@ -372,6 +397,31 @@ typedef struct open_internal_entity {
   XML_Bool betweenDecl; /* WFC: PE Between Declarations */
 } OPEN_INTERNAL_ENTITY;
 
+enum XML_Account {
+  XML_ACCOUNT_DIRECT,           /* bytes directly passed to the Expat parser */
+  XML_ACCOUNT_ENTITY_EXPANSION, /* intermediate bytes produced during entity
+                                   expansion */
+  XML_ACCOUNT_NONE              /* i.e. do not account, was accounted already */
+};
+
+#ifdef XML_DTD
+typedef unsigned long long XmlBigCount;
+typedef struct accounting {
+  XmlBigCount countBytesDirect;
+  XmlBigCount countBytesIndirect;
+  int debugLevel;
+  float maximumAmplificationFactor; // >=1.0
+  unsigned long long activationThresholdBytes;
+} ACCOUNTING;
+
+typedef struct entity_stats {
+  unsigned int countEverOpened;
+  unsigned int currentDepth;
+  unsigned int maximumDepthSeen;
+  int debugLevel;
+} ENTITY_STATS;
+#endif /* XML_DTD */
+
 typedef enum XML_Error PTRCALL Processor(XML_Parser parser, const char *start,
                                          const char *end, const char **endPtr);
 
@@ -402,16 +452,18 @@ static enum XML_Error initializeEncoding(XML_Parser parser);
 static enum XML_Error doProlog(XML_Parser parser, const ENCODING *enc,
                                const char *s, const char *end, int tok,
                                const char *next, const char **nextPtr,
-                               XML_Bool haveMore, XML_Bool allowClosingDoctype);
+                               XML_Bool haveMore, XML_Bool allowClosingDoctype,
+                               enum XML_Account account);
 static enum XML_Error processInternalEntity(XML_Parser parser, ENTITY *entity,
                                             XML_Bool betweenDecl);
 static enum XML_Error doContent(XML_Parser parser, int startTagLevel,
                                 const ENCODING *enc, const char *start,
                                 const char *end, const char **endPtr,
-                                XML_Bool haveMore);
+                                XML_Bool haveMore, enum XML_Account account);
 static enum XML_Error doCdataSection(XML_Parser parser, const ENCODING *,
                                      const char **startPtr, const char *end,
-                                     const char **nextPtr, XML_Bool haveMore);
+                                     const char **nextPtr, XML_Bool haveMore,
+                                     enum XML_Account account);
 #ifdef XML_DTD
 static enum XML_Error doIgnoreSection(XML_Parser parser, const ENCODING *,
                                       const char **startPtr, const char *end,
@@ -421,7 +473,8 @@ static enum XML_Error doIgnoreSection(XML_Parser parser, const ENCODING *,
 static void freeBindings(XML_Parser parser, BINDING *bindings);
 static enum XML_Error storeAtts(XML_Parser parser, const ENCODING *,
                                 const char *s, TAG_NAME *tagNamePtr,
-                                BINDING **bindingsPtr);
+                                BINDING **bindingsPtr,
+                                enum XML_Account account);
 static enum XML_Error addBinding(XML_Parser parser, PREFIX *prefix,
                                  const ATTRIBUTE_ID *attId, const XML_Char *uri,
                                  BINDING **bindingsPtr);
@@ -430,15 +483,18 @@ static int defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *, XML_Bool isCdata,
                            XML_Parser parser);
 static enum XML_Error storeAttributeValue(XML_Parser parser, const ENCODING *,
                                           XML_Bool isCdata, const char *,
-                                          const char *, STRING_POOL *);
+                                          const char *, STRING_POOL *,
+                                          enum XML_Account account);
 static enum XML_Error appendAttributeValue(XML_Parser parser, const ENCODING *,
                                            XML_Bool isCdata, const char *,
-                                           const char *, STRING_POOL *);
+                                           const char *, STRING_POOL *,
+                                           enum XML_Account account);
 static ATTRIBUTE_ID *getAttributeId(XML_Parser parser, const ENCODING *enc,
                                     const char *start, const char *end);
 static int setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *);
 static enum XML_Error storeEntityValue(XML_Parser parser, const ENCODING *enc,
-                                       const char *start, const char *end);
+                                       const char *start, const char *end,
+                                       enum XML_Account account);
 static int reportProcessingInstruction(XML_Parser parser, const ENCODING *enc,
                                        const char *start, const char *end);
 static int reportComment(XML_Parser parser, const ENCODING *enc,
@@ -502,6 +558,34 @@ static XML_Parser parserCreate(const XML_Char *encodingName,
 
 static void parserInit(XML_Parser parser, const XML_Char *encodingName);
 
+#ifdef XML_DTD
+static float accountingGetCurrentAmplification(XML_Parser rootParser);
+static void accountingReportStats(XML_Parser originParser, const char *epilog);
+static void accountingOnAbort(XML_Parser originParser);
+static void accountingReportDiff(XML_Parser rootParser,
+                                 unsigned int levelsAwayFromRootParser,
+                                 const char *before, const char *after,
+                                 ptrdiff_t bytesMore, int source_line,
+                                 enum XML_Account account);
+static XML_Bool accountingDiffTolerated(XML_Parser originParser, int tok,
+                                        const char *before, const char *after,
+                                        int source_line,
+                                        enum XML_Account account);
+
+static void entityTrackingReportStats(XML_Parser parser, ENTITY *entity,
+                                      const char *action, int sourceLine);
+static void entityTrackingOnOpen(XML_Parser parser, ENTITY *entity,
+                                 int sourceLine);
+static void entityTrackingOnClose(XML_Parser parser, ENTITY *entity,
+                                  int sourceLine);
+
+static XML_Parser getRootParserOf(XML_Parser parser,
+                                  unsigned int *outLevelDiff);
+#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)
@@ -615,6 +699,10 @@ struct XML_ParserStruct {
   enum XML_ParamEntityParsing m_paramEntityParsing;
 #endif
   unsigned long m_hash_secret_salt;
+#ifdef XML_DTD
+  ACCOUNTING m_accounting;
+  ENTITY_STATS m_entity_stats;
+#endif
 };
 
 #define MALLOC(parser, s) (parser->m_mem.malloc_fcn((s)))
@@ -867,6 +955,18 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) {
   parser->m_paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER;
 #endif
   parser->m_hash_secret_salt = 0;
+
+#ifdef XML_DTD
+  memset(&parser->m_accounting, 0, sizeof(ACCOUNTING));
+  parser->m_accounting.debugLevel = getDebugLevel("EXPAT_ACCOUNTING_DEBUG", 0u);
+  parser->m_accounting.maximumAmplificationFactor
+      = EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT;
+  parser->m_accounting.activationThresholdBytes
+      = EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT;
+
+  memset(&parser->m_entity_stats, 0, sizeof(ENTITY_STATS));
+  parser->m_entity_stats.debugLevel = getDebugLevel("EXPAT_ENTITY_DEBUG", 0u);
+#endif
 }
 
 /* moves list of bindings to m_freeBindingList */
@@ -2141,6 +2241,10 @@ XML_ErrorString(enum XML_Error code) {
   case XML_ERROR_NO_BUFFER:
     return XML_L(
         "a successful prior call to function XML_GetBuffer is required");
+  /* Added in 2.4.0. */
+  case XML_ERROR_AMPLIFICATION_LIMIT_BREACH:
+    return XML_L(
+        "limit on input amplification factor (from DTD and entities) breached");
   }
   return NULL;
 }
@@ -2177,41 +2281,75 @@ XML_ExpatVersionInfo(void) {
 
 const XML_Feature *XMLCALL
 XML_GetFeatureList(void) {
-  static const XML_Feature features[]
-      = {{XML_FEATURE_SIZEOF_XML_CHAR, XML_L("sizeof(XML_Char)"),
-          sizeof(XML_Char)},
-         {XML_FEATURE_SIZEOF_XML_LCHAR, XML_L("sizeof(XML_LChar)"),
-          sizeof(XML_LChar)},
+  static const XML_Feature features[] = {
+      {XML_FEATURE_SIZEOF_XML_CHAR, XML_L("sizeof(XML_Char)"),
+       sizeof(XML_Char)},
+      {XML_FEATURE_SIZEOF_XML_LCHAR, XML_L("sizeof(XML_LChar)"),
+       sizeof(XML_LChar)},
 #ifdef XML_UNICODE
-         {XML_FEATURE_UNICODE, XML_L("XML_UNICODE"), 0},
+      {XML_FEATURE_UNICODE, XML_L("XML_UNICODE"), 0},
 #endif
 #ifdef XML_UNICODE_WCHAR_T
-         {XML_FEATURE_UNICODE_WCHAR_T, XML_L("XML_UNICODE_WCHAR_T"), 0},
+      {XML_FEATURE_UNICODE_WCHAR_T, XML_L("XML_UNICODE_WCHAR_T"), 0},
 #endif
 #ifdef XML_DTD
-         {XML_FEATURE_DTD, XML_L("XML_DTD"), 0},
+      {XML_FEATURE_DTD, XML_L("XML_DTD"), 0},
 #endif
 #ifdef XML_CONTEXT_BYTES
-         {XML_FEATURE_CONTEXT_BYTES, XML_L("XML_CONTEXT_BYTES"),
-          XML_CONTEXT_BYTES},
+      {XML_FEATURE_CONTEXT_BYTES, XML_L("XML_CONTEXT_BYTES"),
+       XML_CONTEXT_BYTES},
 #endif
 #ifdef XML_MIN_SIZE
-         {XML_FEATURE_MIN_SIZE, XML_L("XML_MIN_SIZE"), 0},
+      {XML_FEATURE_MIN_SIZE, XML_L("XML_MIN_SIZE"), 0},
 #endif
 #ifdef XML_NS
-         {XML_FEATURE_NS, XML_L("XML_NS"), 0},
+      {XML_FEATURE_NS, XML_L("XML_NS"), 0},
 #endif
 #ifdef XML_LARGE_SIZE
-         {XML_FEATURE_LARGE_SIZE, XML_L("XML_LARGE_SIZE"), 0},
+      {XML_FEATURE_LARGE_SIZE, XML_L("XML_LARGE_SIZE"), 0},
 #endif
 #ifdef XML_ATTR_INFO
-         {XML_FEATURE_ATTR_INFO, XML_L("XML_ATTR_INFO"), 0},
+      {XML_FEATURE_ATTR_INFO, XML_L("XML_ATTR_INFO"), 0},
 #endif
-         {XML_FEATURE_END, NULL, 0}};
+#ifdef XML_DTD
+      /* Added in Expat 2.4.0. */
+      {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
@@ -2264,9 +2402,9 @@ storeRawNames(XML_Parser parser) {
 static enum XML_Error PTRCALL
 contentProcessor(XML_Parser parser, const char *start, const char *end,
                  const char **endPtr) {
-  enum XML_Error result
-      = doContent(parser, 0, parser->m_encoding, start, end, endPtr,
-                  (XML_Bool)! parser->m_parsingStatus.finalBuffer);
+  enum XML_Error result = doContent(
+      parser, 0, parser->m_encoding, start, end, endPtr,
+      (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_ACCOUNT_DIRECT);
   if (result == XML_ERROR_NONE) {
     if (! storeRawNames(parser))
       return XML_ERROR_NO_MEMORY;
@@ -2291,6 +2429,14 @@ externalEntityInitProcessor2(XML_Parser parser, const char *start,
   int tok = XmlContentTok(parser->m_encoding, start, end, &next);
   switch (tok) {
   case XML_TOK_BOM:
+#ifdef XML_DTD
+    if (! accountingDiffTolerated(parser, tok, start, next, __LINE__,
+                                  XML_ACCOUNT_DIRECT)) {
+      accountingOnAbort(parser);
+      return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
+    }
+#endif /* XML_DTD */
+
     /* If we are at the end of the buffer, this would cause the next stage,
        i.e. externalEntityInitProcessor3, to pass control directly to
        doContent (by detecting XML_TOK_NONE) without processing any xml text
@@ -2328,6 +2474,10 @@ externalEntityInitProcessor3(XML_Parser parser, const char *start,
   const char *next = start; /* XmlContentTok doesn't always set the last arg */
   parser->m_eventPtr = start;
   tok = XmlContentTok(parser->m_encoding, start, end, &next);
+  /* Note: These bytes are accounted later in:
+           - processXmlDecl
+           - externalEntityContentProcessor
+  */
   parser->m_eventEndPtr = next;
 
   switch (tok) {
@@ -2369,7 +2519,8 @@ externalEntityContentProcessor(XML_Parser parser, const char *start,
                                const char *end, const char **endPtr) {
   enum XML_Error result
       = doContent(parser, 1, parser->m_encoding, start, end, endPtr,
-                  (XML_Bool)! parser->m_parsingStatus.finalBuffer);
+                  (XML_Bool)! parser->m_parsingStatus.finalBuffer,
+                  XML_ACCOUNT_ENTITY_EXPANSION);
   if (result == XML_ERROR_NONE) {
     if (! storeRawNames(parser))
       return XML_ERROR_NO_MEMORY;
@@ -2380,7 +2531,7 @@ externalEntityContentProcessor(XML_Parser parser, const char *start,
 static enum XML_Error
 doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
           const char *s, const char *end, const char **nextPtr,
-          XML_Bool haveMore) {
+          XML_Bool haveMore, enum XML_Account account) {
   /* save one level of indirection */
   DTD *const dtd = parser->m_dtd;
 
@@ -2398,6 +2549,17 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
   for (;;) {
     const char *next = s; /* XmlContentTok doesn't always set the last arg */
     int tok = XmlContentTok(enc, s, end, &next);
+#ifdef XML_DTD
+    const char *accountAfter
+        = ((tok == XML_TOK_TRAILING_RSQB) || (tok == XML_TOK_TRAILING_CR))
+              ? (haveMore ? s /* i.e. 0 bytes */ : end)
+              : next;
+    if (! accountingDiffTolerated(parser, tok, s, accountAfter, __LINE__,
+                                  account)) {
+      accountingOnAbort(parser);
+      return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
+    }
+#endif
     *eventEndPP = next;
     switch (tok) {
     case XML_TOK_TRAILING_CR:
@@ -2453,6 +2615,14 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
       XML_Char ch = (XML_Char)XmlPredefinedEntityName(
           enc, s + enc->minBytesPerChar, next - enc->minBytesPerChar);
       if (ch) {
+#ifdef XML_DTD
+        /* NOTE: We are replacing 4-6 characters original input for 1 character
+         *       so there is no amplification and hence recording without
+         *       protection. */
+        accountingDiffTolerated(parser, tok, (char *)&ch,
+                                ((char *)&ch) + sizeof(XML_Char), __LINE__,
+                                XML_ACCOUNT_ENTITY_EXPANSION);
+#endif /* XML_DTD */
         if (parser->m_characterDataHandler)
           parser->m_characterDataHandler(parser->m_handlerArg, &ch, 1);
         else if (parser->m_defaultHandler)
@@ -2571,7 +2741,8 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
       }
       tag->name.str = (XML_Char *)tag->buf;
       *toPtr = XML_T('\0');
-      result = storeAtts(parser, enc, s, &(tag->name), &(tag->bindings));
+      result
+          = storeAtts(parser, enc, s, &(tag->name), &(tag->bindings), account);
       if (result)
         return result;
       if (parser->m_startElementHandler)
@@ -2595,7 +2766,8 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
       if (! name.str)
         return XML_ERROR_NO_MEMORY;
       poolFinish(&parser->m_tempPool);
-      result = storeAtts(parser, enc, s, &name, &bindings);
+      result = storeAtts(parser, enc, s, &name, &bindings,
+                         XML_ACCOUNT_NONE /* token spans whole start tag */);
       if (result != XML_ERROR_NONE) {
         freeBindings(parser, bindings);
         return result;
@@ -2730,7 +2902,8 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
       /* END disabled code */
       else if (parser->m_defaultHandler)
         reportDefault(parser, enc, s, next);
-      result = doCdataSection(parser, enc, &next, end, nextPtr, haveMore);
+      result
+          = doCdataSection(parser, enc, &next, end, nextPtr, haveMore, account);
       if (result != XML_ERROR_NONE)
         return result;
       else if (! next) {
@@ -2859,7 +3032,8 @@ freeBindings(XML_Parser parser, BINDING *bindings) {
 */
 static enum XML_Error
 storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
-          TAG_NAME *tagNamePtr, BINDING **bindingsPtr) {
+          TAG_NAME *tagNamePtr, BINDING **bindingsPtr,
+          enum XML_Account account) {
   DTD *const dtd = parser->m_dtd; /* save one level of indirection */
   ELEMENT_TYPE *elementType;
   int nDefaultAtts;
@@ -2969,7 +3143,7 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
       /* normalize the attribute value */
       result = storeAttributeValue(
           parser, enc, isCdata, parser->m_atts[i].valuePtr,
-          parser->m_atts[i].valueEnd, &parser->m_tempPool);
+          parser->m_atts[i].valueEnd, &parser->m_tempPool, account);
       if (result)
         return result;
       appAtts[attIndex] = poolStart(&parser->m_tempPool);
@@ -3358,9 +3532,9 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId,
 static enum XML_Error PTRCALL
 cdataSectionProcessor(XML_Parser parser, const char *start, const char *end,
                       const char **endPtr) {
-  enum XML_Error result
-      = doCdataSection(parser, parser->m_encoding, &start, end, endPtr,
-                       (XML_Bool)! parser->m_parsingStatus.finalBuffer);
+  enum XML_Error result = doCdataSection(
+      parser, parser->m_encoding, &start, end, endPtr,
+      (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_ACCOUNT_DIRECT);
   if (result != XML_ERROR_NONE)
     return result;
   if (start) {
@@ -3380,7 +3554,8 @@ cdataSectionProcessor(XML_Parser parser, const char *start, const char *end,
 */
 static enum XML_Error
 doCdataSection(XML_Parser parser, const ENCODING *enc, const char **startPtr,
-               const char *end, const char **nextPtr, XML_Bool haveMore) {
+               const char *end, const char **nextPtr, XML_Bool haveMore,
+               enum XML_Account account) {
   const char *s = *startPtr;
   const char **eventPP;
   const char **eventEndPP;
@@ -3398,6 +3573,14 @@ doCdataSection(XML_Parser parser, const ENCODING *enc, const char **startPtr,
   for (;;) {
     const char *next = s; /* in case of XML_TOK_NONE or XML_TOK_PARTIAL */
     int tok = XmlCdataSectionTok(enc, s, end, &next);
+#ifdef XML_DTD
+    if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, account)) {
+      accountingOnAbort(parser);
+      return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
+    }
+#else
+    UNUSED_P(account);
+#endif
     *eventEndPP = next;
     switch (tok) {
     case XML_TOK_CDATA_SECT_CLOSE:
@@ -3542,6 +3725,13 @@ doIgnoreSection(XML_Parser parser, const ENCODING *enc, const char **startPtr,
   *eventPP = s;
   *startPtr = NULL;
   tok = XmlIgnoreSectionTok(enc, s, end, &next);
+#  ifdef XML_DTD
+  if (! accountingDiffTolerated(parser, tok, s, next, __LINE__,
+                                XML_ACCOUNT_DIRECT)) {
+    accountingOnAbort(parser);
+    return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
+  }
+#  endif
   *eventEndPP = next;
   switch (tok) {
   case XML_TOK_IGNORE_SECT:
@@ -3626,6 +3816,15 @@ processXmlDecl(XML_Parser parser, int isGeneralTextEntity, const char *s,
   const char *versionend;
   const XML_Char *storedversion = NULL;
   int standalone = -1;
+
+#ifdef XML_DTD
+  if (! accountingDiffTolerated(parser, XML_TOK_XML_DECL, s, next, __LINE__,
+                                XML_ACCOUNT_DIRECT)) {
+    accountingOnAbort(parser);
+    return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
+  }
+#endif
+
   if (! (parser->m_ns ? XmlParseXmlDeclNS : XmlParseXmlDecl)(
           isGeneralTextEntity, parser->m_encoding, s, next, &parser->m_eventPtr,
           &version, &versionend, &encodingName, &newEncoding, &standalone)) {
@@ -3775,6 +3974,10 @@ entityValueInitProcessor(XML_Parser parser, const char *s, const char *end,
 
   for (;;) {
     tok = XmlPrologTok(parser->m_encoding, start, end, &next);
+    /* Note: Except for XML_TOK_BOM below, these bytes are accounted later in:
+             - storeEntityValue
+             - processXmlDecl
+    */
     parser->m_eventEndPtr = next;
     if (tok <= 0) {
       if (! parser->m_parsingStatus.finalBuffer && tok != XML_TOK_INVALID) {
@@ -3793,7 +3996,8 @@ entityValueInitProcessor(XML_Parser parser, const char *s, const char *end,
         break;
       }
       /* found end of entity value - can store it now */
-      return storeEntityValue(parser, parser->m_encoding, s, end);
+      return storeEntityValue(parser, parser->m_encoding, s, end,
+                              XML_ACCOUNT_DIRECT);
     } else if (tok == XML_TOK_XML_DECL) {
       enum XML_Error result;
       result = processXmlDecl(parser, 0, start, next);
@@ -3820,6 +4024,14 @@ entityValueInitProcessor(XML_Parser parser, const char *s, const char *end,
     */
     else if (tok == XML_TOK_BOM && next == end
              && ! parser->m_parsingStatus.finalBuffer) {
+#  ifdef XML_DTD
+      if (! accountingDiffTolerated(parser, tok, s, next, __LINE__,
+                                    XML_ACCOUNT_DIRECT)) {
+        accountingOnAbort(parser);
+        return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
+      }
+#  endif
+
       *nextPtr = next;
       return XML_ERROR_NONE;
     }
@@ -3862,16 +4074,24 @@ externalParEntProcessor(XML_Parser parser, const char *s, const char *end,
   }
   /* This would cause the next stage, i.e. doProlog to be passed XML_TOK_BOM.
      However, when parsing an external subset, doProlog will not accept a BOM
-     as valid, and report a syntax error, so we have to skip the BOM
+     as valid, and report a syntax error, so we have to skip the BOM, and
+     account for the BOM bytes.
   */
   else if (tok == XML_TOK_BOM) {
+    if (! accountingDiffTolerated(parser, tok, s, next, __LINE__,
+                                  XML_ACCOUNT_DIRECT)) {
+      accountingOnAbort(parser);
+      return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
+    }
+
     s = next;
     tok = XmlPrologTok(parser->m_encoding, s, end, &next);
   }
 
   parser->m_processor = prologProcessor;
   return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr,
-                  (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE);
+                  (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE,
+                  XML_ACCOUNT_DIRECT);
 }
 
 static enum XML_Error PTRCALL
@@ -3884,6 +4104,9 @@ entityValueProcessor(XML_Parser parser, const char *s, const char *end,
 
   for (;;) {
     tok = XmlPrologTok(enc, start, end, &next);
+    /* Note: These bytes are accounted later in:
+             - storeEntityValue
+    */
     if (tok <= 0) {
       if (! parser->m_parsingStatus.finalBuffer && tok != XML_TOK_INVALID) {
         *nextPtr = s;
@@ -3901,7 +4124,7 @@ entityValueProcessor(XML_Parser parser, const char *s, const char *end,
         break;
       }
       /* found end of entity value - can store it now */
-      return storeEntityValue(parser, enc, s, end);
+      return storeEntityValue(parser, enc, s, end, XML_ACCOUNT_DIRECT);
     }
     start = next;
   }
@@ -3915,13 +4138,14 @@ prologProcessor(XML_Parser parser, const char *s, const char *end,
   const char *next = s;
   int tok = XmlPrologTok(parser->m_encoding, s, end, &next);
   return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr,
-                  (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE);
+                  (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE,
+                  XML_ACCOUNT_DIRECT);
 }
 
 static enum XML_Error
 doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
          int tok, const char *next, const char **nextPtr, XML_Bool haveMore,
-         XML_Bool allowClosingDoctype) {
+         XML_Bool allowClosingDoctype, enum XML_Account account) {
 #ifdef XML_DTD
   static const XML_Char externalSubsetName[] = {ASCII_HASH, '\0'};
 #endif /* XML_DTD */
@@ -3948,6 +4172,10 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
   static const XML_Char enumValueSep[] = {ASCII_PIPE, '\0'};
   static const XML_Char enumValueStart[] = {ASCII_LPAREN, '\0'};
 
+#ifndef XML_DTD
+  UNUSED_P(account);
+#endif
+
   /* save one level of indirection */
   DTD *const dtd = parser->m_dtd;
 
@@ -4012,6 +4240,19 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
       }
     }
     role = XmlTokenRole(&parser->m_prologState, tok, s, next, enc);
+#ifdef XML_DTD
+    switch (role) {
+    case XML_ROLE_INSTANCE_START: // bytes accounted in contentProcessor
+    case XML_ROLE_XML_DECL:       // bytes accounted in processXmlDecl
+    case XML_ROLE_TEXT_DECL:      // bytes accounted in processXmlDecl
+      break;
+    default:
+      if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, account)) {
+        accountingOnAbort(parser);
+        return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
+      }
+    }
+#endif
     switch (role) {
     case XML_ROLE_XML_DECL: {
       enum XML_Error result = processXmlDecl(parser, 0, s, next);
@@ -4287,7 +4528,8 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
         const XML_Char *attVal;
         enum XML_Error result = storeAttributeValue(
             parser, enc, parser->m_declAttributeIsCdata,
-            s + enc->minBytesPerChar, next - enc->minBytesPerChar, &dtd->pool);
+            s + enc->minBytesPerChar, next - enc->minBytesPerChar, &dtd->pool,
+            XML_ACCOUNT_NONE);
         if (result)
           return result;
         attVal = poolStart(&dtd->pool);
@@ -4320,8 +4562,9 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
       break;
     case XML_ROLE_ENTITY_VALUE:
       if (dtd->keepProcessing) {
-        enum XML_Error result = storeEntityValue(
-            parser, enc, s + enc->minBytesPerChar, next - enc->minBytesPerChar);
+        enum XML_Error result
+            = storeEntityValue(parser, enc, s + enc->minBytesPerChar,
+                               next - enc->minBytesPerChar, XML_ACCOUNT_NONE);
         if (parser->m_declEntity) {
           parser->m_declEntity->textPtr = poolStart(&dtd->entityValuePool);
           parser->m_declEntity->textLen
@@ -4711,12 +4954,15 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
         if (parser->m_externalEntityRefHandler) {
           dtd->paramEntityRead = XML_FALSE;
           entity->open = XML_TRUE;
+          entityTrackingOnOpen(parser, entity, __LINE__);
           if (! parser->m_externalEntityRefHandler(
                   parser->m_externalEntityRefHandlerArg, 0, entity->base,
                   entity->systemId, entity->publicId)) {
+            entityTrackingOnClose(parser, entity, __LINE__);
             entity->open = XML_FALSE;
             return XML_ERROR_EXTERNAL_ENTITY_HANDLING;
           }
+          entityTrackingOnClose(parser, entity, __LINE__);
           entity->open = XML_FALSE;
           handleDefault = XML_FALSE;
           if (! dtd->paramEntityRead) {
@@ -4914,6 +5160,13 @@ epilogProcessor(XML_Parser parser, const char *s, const char *end,
   for (;;) {
     const char *next = NULL;
     int tok = XmlPrologTok(parser->m_encoding, s, end, &next);
+#ifdef XML_DTD
+    if (! accountingDiffTolerated(parser, tok, s, next, __LINE__,
+                                  XML_ACCOUNT_DIRECT)) {
+      accountingOnAbort(parser);
+      return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
+    }
+#endif
     parser->m_eventEndPtr = next;
     switch (tok) {
     /* report partial linebreak - it might be the last token */
@@ -4987,6 +5240,9 @@ processInternalEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl) {
       return XML_ERROR_NO_MEMORY;
   }
   entity->open = XML_TRUE;
+#ifdef XML_DTD
+  entityTrackingOnOpen(parser, entity, __LINE__);
+#endif
   entity->processed = 0;
   openEntity->next = parser->m_openInternalEntities;
   parser->m_openInternalEntities = openEntity;
@@ -5005,17 +5261,22 @@ processInternalEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl) {
     int tok
         = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next);
     result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd,
-                      tok, next, &next, XML_FALSE, XML_FALSE);
+                      tok, next, &next, XML_FALSE, XML_FALSE,
+                      XML_ACCOUNT_ENTITY_EXPANSION);
   } else
 #endif /* XML_DTD */
     result = doContent(parser, parser->m_tagLevel, parser->m_internalEncoding,
-                       textStart, textEnd, &next, XML_FALSE);
+                       textStart, textEnd, &next, XML_FALSE,
+                       XML_ACCOUNT_ENTITY_EXPANSION);
 
   if (result == XML_ERROR_NONE) {
     if (textEnd != next && parser->m_parsingStatus.parsing == XML_SUSPENDED) {
       entity->processed = (int)(next - textStart);
       parser->m_processor = internalEntityProcessor;
     } else {
+#ifdef XML_DTD
+      entityTrackingOnClose(parser, entity, __LINE__);
+#endif /* XML_DTD */
       entity->open = XML_FALSE;
       parser->m_openInternalEntities = openEntity->next;
       /* put openEntity back in list of free instances */
@@ -5048,12 +5309,13 @@ internalEntityProcessor(XML_Parser parser, const char *s, const char *end,
     int tok
         = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next);
     result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd,
-                      tok, next, &next, XML_FALSE, XML_TRUE);
+                      tok, next, &next, XML_FALSE, XML_TRUE,
+                      XML_ACCOUNT_ENTITY_EXPANSION);
   } else
 #endif /* XML_DTD */
     result = doContent(parser, openEntity->startTagLevel,
                        parser->m_internalEncoding, textStart, textEnd, &next,
-                       XML_FALSE);
+                       XML_FALSE, XML_ACCOUNT_ENTITY_EXPANSION);
 
   if (result != XML_ERROR_NONE)
     return result;
@@ -5062,6 +5324,9 @@ internalEntityProcessor(XML_Parser parser, const char *s, const char *end,
     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 */
@@ -5075,7 +5340,8 @@ internalEntityProcessor(XML_Parser parser, const char *s, const char *end,
     parser->m_processor = prologProcessor;
     tok = XmlPrologTok(parser->m_encoding, s, end, &next);
     return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr,
-                    (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE);
+                    (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE,
+                    XML_ACCOUNT_DIRECT);
   } else
 #endif /* XML_DTD */
   {
@@ -5083,7 +5349,8 @@ internalEntityProcessor(XML_Parser parser, const char *s, const char *end,
     /* see externalEntityContentProcessor vs contentProcessor */
     return doContent(parser, parser->m_parentParser ? 1 : 0, parser->m_encoding,
                      s, end, nextPtr,
-                     (XML_Bool)! parser->m_parsingStatus.finalBuffer);
+                     (XML_Bool)! parser->m_parsingStatus.finalBuffer,
+                     XML_ACCOUNT_DIRECT);
   }
 }
 
@@ -5098,9 +5365,10 @@ errorProcessor(XML_Parser parser, const char *s, const char *end,
 
 static enum XML_Error
 storeAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
-                    const char *ptr, const char *end, STRING_POOL *pool) {
+                    const char *ptr, const char *end, STRING_POOL *pool,
+                    enum XML_Account account) {
   enum XML_Error result
-      = appendAttributeValue(parser, enc, isCdata, ptr, end, pool);
+      = appendAttributeValue(parser, enc, isCdata, ptr, end, pool, account);
   if (result)
     return result;
   if (! isCdata && poolLength(pool) && poolLastChar(pool) == 0x20)
@@ -5112,11 +5380,23 @@ storeAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
 
 static enum XML_Error
 appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
-                     const char *ptr, const char *end, STRING_POOL *pool) {
+                     const char *ptr, const char *end, STRING_POOL *pool,
+                     enum XML_Account account) {
   DTD *const dtd = parser->m_dtd; /* save one level of indirection */
+#ifndef XML_DTD
+  UNUSED_P(account);
+#endif
+
   for (;;) {
-    const char *next;
+    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;
@@ -5176,6 +5456,14 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
       XML_Char ch = (XML_Char)XmlPredefinedEntityName(
           enc, ptr + enc->minBytesPerChar, next - enc->minBytesPerChar);
       if (ch) {
+#ifdef XML_DTD
+        /* NOTE: We are replacing 4-6 characters original input for 1 character
+         *       so there is no amplification and hence recording without
+         *       protection. */
+        accountingDiffTolerated(parser, tok, (char *)&ch,
+                                ((char *)&ch) + sizeof(XML_Char), __LINE__,
+                                XML_ACCOUNT_ENTITY_EXPANSION);
+#endif /* XML_DTD */
         if (! poolAppendChar(pool, ch))
           return XML_ERROR_NO_MEMORY;
         break;
@@ -5253,9 +5541,16 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
         enum XML_Error result;
         const XML_Char *textEnd = entity->textPtr + entity->textLen;
         entity->open = XML_TRUE;
+#ifdef XML_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;
@@ -5285,13 +5580,16 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
 
 static enum XML_Error
 storeEntityValue(XML_Parser parser, const ENCODING *enc,
-                 const char *entityTextPtr, const char *entityTextEnd) {
+                 const char *entityTextPtr, const char *entityTextEnd,
+                 enum XML_Account account) {
   DTD *const dtd = parser->m_dtd; /* save one level of indirection */
   STRING_POOL *pool = &(dtd->entityValuePool);
   enum XML_Error result = XML_ERROR_NONE;
 #ifdef XML_DTD
   int oldInEntityValue = parser->m_prologState.inEntityValue;
   parser->m_prologState.inEntityValue = 1;
+#else
+  UNUSED_P(account);
 #endif /* XML_DTD */
   /* never return Null for the value argument in EntityDeclHandler,
      since this would indicate an external entity; therefore we
@@ -5302,8 +5600,19 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc,
   }
 
   for (;;) {
-    const char *next;
+    const char *next
+        = entityTextPtr; /* XmlEntityValueTok doesn't always set the last arg */
     int tok = XmlEntityValueTok(enc, entityTextPtr, entityTextEnd, &next);
+
+#ifdef XML_DTD
+    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
@@ -5339,13 +5648,16 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc,
           if (parser->m_externalEntityRefHandler) {
             dtd->paramEntityRead = XML_FALSE;
             entity->open = XML_TRUE;
+            entityTrackingOnOpen(parser, entity, __LINE__);
             if (! parser->m_externalEntityRefHandler(
                     parser->m_externalEntityRefHandlerArg, 0, entity->base,
                     entity->systemId, entity->publicId)) {
+              entityTrackingOnClose(parser, entity, __LINE__);
               entity->open = XML_FALSE;
               result = XML_ERROR_EXTERNAL_ENTITY_HANDLING;
               goto endEntityValue;
             }
+            entityTrackingOnClose(parser, entity, __LINE__);
             entity->open = XML_FALSE;
             if (! dtd->paramEntityRead)
               dtd->keepProcessing = dtd->standalone;
@@ -5353,9 +5665,12 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc,
             dtd->keepProcessing = dtd->standalone;
         } else {
           entity->open = XML_TRUE;
+          entityTrackingOnOpen(parser, entity, __LINE__);
           result = storeEntityValue(
               parser, parser->m_internalEncoding, (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;
@@ -6716,3 +7031,755 @@ copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) {
   memcpy(result, s, charsRequired * sizeof(XML_Char));
   return result;
 }
+
+#ifdef XML_DTD
+
+static float
+accountingGetCurrentAmplification(XML_Parser rootParser) {
+  const XmlBigCount countBytesOutput
+      = rootParser->m_accounting.countBytesDirect
+        + rootParser->m_accounting.countBytesIndirect;
+  const float amplificationFactor
+      = rootParser->m_accounting.countBytesDirect
+            ? (countBytesOutput
+               / (float)(rootParser->m_accounting.countBytesDirect))
+            : 1.0f;
+  assert(! rootParser->m_parentParser);
+  return amplificationFactor;
+}
+
+static void
+accountingReportStats(XML_Parser originParser, const char *epilog) {
+  const XML_Parser rootParser = getRootParserOf(originParser, NULL);
+  assert(! rootParser->m_parentParser);
+
+  if (rootParser->m_accounting.debugLevel < 1) {
+    return;
+  }
+
+  const float amplificationFactor
+      = accountingGetCurrentAmplification(rootParser);
+  fprintf(stderr,
+          "expat: Accounting(%p): Direct " EXPAT_FMT_ULL(
+              "10") ", indirect " EXPAT_FMT_ULL("10") ", amplification %8.2f%s",
+          (void *)rootParser, rootParser->m_accounting.countBytesDirect,
+          rootParser->m_accounting.countBytesIndirect,
+          (double)amplificationFactor, epilog);
+}
+
+static void
+accountingOnAbort(XML_Parser originParser) {
+  accountingReportStats(originParser, " ABORTING\n");
+}
+
+static void
+accountingReportDiff(XML_Parser rootParser,
+                     unsigned int levelsAwayFromRootParser, const char *before,
+                     const char *after, ptrdiff_t bytesMore, int source_line,
+                     enum XML_Account account) {
+  assert(! rootParser->m_parentParser);
+
+  fprintf(stderr,
+          " (+" EXPAT_FMT_PTRDIFF_T("6") " bytes %s|%d, xmlparse.c:%d) %*s\"",
+          bytesMore, (account == XML_ACCOUNT_DIRECT) ? "DIR" : "EXP",
+          levelsAwayFromRootParser, source_line, 10, "");
+
+  const char ellipis[] = "[..]";
+  const size_t ellipsisLength = sizeof(ellipis) /* because compile-time */ - 1;
+  const unsigned int contextLength = 10;
+
+  /* Note: Performance is of no concern here */
+  const char *walker = before;
+  if ((rootParser->m_accounting.debugLevel >= 3)
+      || (after - before)
+             <= (ptrdiff_t)(contextLength + ellipsisLength + contextLength)) {
+    for (; walker < after; walker++) {
+      fprintf(stderr, "%s", unsignedCharToPrintable(walker[0]));
+    }
+  } else {
+    for (; walker < before + contextLength; walker++) {
+      fprintf(stderr, "%s", unsignedCharToPrintable(walker[0]));
+    }
+    fprintf(stderr, ellipis);
+    walker = after - contextLength;
+    for (; walker < after; walker++) {
+      fprintf(stderr, "%s", unsignedCharToPrintable(walker[0]));
+    }
+  }
+  fprintf(stderr, "\"\n");
+}
+
+static XML_Bool
+accountingDiffTolerated(XML_Parser originParser, int tok, const char *before,
+                        const char *after, int source_line,
+                        enum XML_Account account) {
+  /* Note: We need to check the token type *first* to be sure that
+   *       we can even access variable <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 */
+
+  unsigned int levelsAwayFromRootParser;
+  const XML_Parser rootParser
+      = getRootParserOf(originParser, &levelsAwayFromRootParser);
+  assert(! rootParser->m_parentParser);
+
+  const int isDirect
+      = (account == XML_ACCOUNT_DIRECT) && (originParser == rootParser);
+  const ptrdiff_t bytesMore = after - before;
+
+  XmlBigCount *const additionTarget
+      = isDirect ? &rootParser->m_accounting.countBytesDirect
+                 : &rootParser->m_accounting.countBytesIndirect;
+
+  /* Detect and avoid integer overflow */
+  if (*additionTarget > (XmlBigCount)(-1) - (XmlBigCount)bytesMore)
+    return XML_FALSE;
+  *additionTarget += bytesMore;
+
+  const XmlBigCount countBytesOutput
+      = rootParser->m_accounting.countBytesDirect
+        + rootParser->m_accounting.countBytesIndirect;
+  const float amplificationFactor
+      = accountingGetCurrentAmplification(rootParser);
+  const XML_Bool tolerated
+      = (countBytesOutput < rootParser->m_accounting.activationThresholdBytes)
+        || (amplificationFactor
+            <= rootParser->m_accounting.maximumAmplificationFactor);
+
+  if (rootParser->m_accounting.debugLevel >= 2) {
+    accountingReportStats(rootParser, "");
+    accountingReportDiff(rootParser, levelsAwayFromRootParser, before, after,
+                         bytesMore, source_line, account);
+  }
+
+  return tolerated;
+}
+
+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) {
+  assert(! rootParser->m_parentParser);
+  if (rootParser->m_entity_stats.debugLevel < 1)
+    return;
+
+#  if defined(XML_UNICODE)
+  const char *const entityName = "[..]";
+#  else
+  const char *const entityName = entity->name;
+#  endif
+
+  fprintf(
+      stderr,
+      "expat: Entities(%p): Count %9d, depth %2d/%2d %*s%s%s; %s length %d (xmlparse.c:%d)\n",
+      (void *)rootParser, rootParser->m_entity_stats.countEverOpened,
+      rootParser->m_entity_stats.currentDepth,
+      rootParser->m_entity_stats.maximumDepthSeen,
+      (rootParser->m_entity_stats.currentDepth - 1) * 2, "",
+      entity->is_param ? "%" : "&", entityName, action, entity->textLen,
+      sourceLine);
+}
+
+static void
+entityTrackingOnOpen(XML_Parser originParser, ENTITY *entity, int sourceLine) {
+  const XML_Parser rootParser = getRootParserOf(originParser, NULL);
+  assert(! rootParser->m_parentParser);
+
+  rootParser->m_entity_stats.countEverOpened++;
+  rootParser->m_entity_stats.currentDepth++;
+  if (rootParser->m_entity_stats.currentDepth
+      > rootParser->m_entity_stats.maximumDepthSeen) {
+    rootParser->m_entity_stats.maximumDepthSeen++;
+  }
+
+  entityTrackingReportStats(rootParser, entity, "OPEN ", sourceLine);
+}
+
+static void
+entityTrackingOnClose(XML_Parser originParser, ENTITY *entity, int sourceLine) {
+  const XML_Parser rootParser = getRootParserOf(originParser, NULL);
+  assert(! rootParser->m_parentParser);
+
+  entityTrackingReportStats(rootParser, entity, "CLOSE", sourceLine);
+  rootParser->m_entity_stats.currentDepth--;
+}
+
+static XML_Parser
+getRootParserOf(XML_Parser parser, unsigned int *outLevelDiff) {
+  XML_Parser rootParser = parser;
+  unsigned int stepsTakenUpwards = 0;
+  while (rootParser->m_parentParser) {
+    rootParser = rootParser->m_parentParser;
+    stepsTakenUpwards++;
+  }
+  assert(! rootParser->m_parentParser);
+  if (outLevelDiff != NULL) {
+    *outLevelDiff = stepsTakenUpwards;
+  }
+  return rootParser;
+}
+
+const char *
+unsignedCharToPrintable(unsigned char c) {
+  switch (c) {
+  case 0:
+    return "\\0";
+  case 1:
+    return "\\x1";
+  case 2:
+    return "\\x2";
+  case 3:
+    return "\\x3";
+  case 4:
+    return "\\x4";
+  case 5:
+    return "\\x5";
+  case 6:
+    return "\\x6";
+  case 7:
+    return "\\x7";
+  case 8:
+    return "\\x8";
+  case 9:
+    return "\\t";
+  case 10:
+    return "\\n";
+  case 11:
+    return "\\xB";
+  case 12:
+    return "\\xC";
+  case 13:
+    return "\\r";
+  case 14:
+    return "\\xE";
+  case 15:
+    return "\\xF";
+  case 16:
+    return "\\x10";
+  case 17:
+    return "\\x11";
+  case 18:
+    return "\\x12";
+  case 19:
+    return "\\x13";
+  case 20:
+    return "\\x14";
+  case 21:
+    return "\\x15";
+  case 22:
+    return "\\x16";
+  case 23:
+    return "\\x17";
+  case 24:
+    return "\\x18";
+  case 25:
+    return "\\x19";
+  case 26:
+    return "\\x1A";
+  case 27:
+    return "\\x1B";
+  case 28:
+    return "\\x1C";
+  case 29:
+    return "\\x1D";
+  case 30:
+    return "\\x1E";
+  case 31:
+    return "\\x1F";
+  case 32:
+    return " ";
+  case 33:
+    return "!";
+  case 34:
+    return "\\\"";
+  case 35:
+    return "#";
+  case 36:
+    return "$";
+  case 37:
+    return "%";
+  case 38:
+    return "&";
+  case 39:
+    return "'";
+  case 40:
+    return "(";
+  case 41:
+    return ")";
+  case 42:
+    return "*";
+  case 43:
+    return "+";
+  case 44:
+    return ",";
+  case 45:
+    return "-";
+  case 46:
+    return ".";
+  case 47:
+    return "/";
+  case 48:
+    return "0";
+  case 49:
+    return "1";
+  case 50:
+    return "2";
+  case 51:
+    return "3";
+  case 52:
+    return "4";
+  case 53:
+    return "5";
+  case 54:
+    return "6";
+  case 55:
+    return "7";
+  case 56:
+    return "8";
+  case 57:
+    return "9";
+  case 58:
+    return ":";
+  case 59:
+    return ";";
+  case 60:
+    return "<";
+  case 61:
+    return "=";
+  case 62:
+    return ">";
+  case 63:
+    return "?";
+  case 64:
+    return "@";
+  case 65:
+    return "A";
+  case 66:
+    return "B";
+  case 67:
+    return "C";
+  case 68:
+    return "D";
+  case 69:
+    return "E";
+  case 70:
+    return "F";
+  case 71:
+    return "G";
+  case 72:
+    return "H";
+  case 73:
+    return "I";
+  case 74:
+    return "J";
+  case 75:
+    return "K";
+  case 76:
+    return "L";
+  case 77:
+    return "M";
+  case 78:
+    return "N";
+  case 79:
+    return "O";
+  case 80:
+    return "P";
+  case 81:
+    return "Q";
+  case 82:
+    return "R";
+  case 83:
+    return "S";
+  case 84:
+    return "T";
+  case 85:
+    return "U";
+  case 86:
+    return "V";
+  case 87:
+    return "W";
+  case 88:
+    return "X";
+  case 89:
+    return "Y";
+  case 90:
+    return "Z";
+  case 91:
+    return "[";
+  case 92:
+    return "\\\\";
+  case 93:
+    return "]";
+  case 94:
+    return "^";
+  case 95:
+    return "_";
+  case 96:
+    return "`";
+  case 97:
+    return "a";
+  case 98:
+    return "b";
+  case 99:
+    return "c";
+  case 100:
+    return "d";
+  case 101:
+    return "e";
+  case 102:
+    return "f";
+  case 103:
+    return "g";
+  case 104:
+    return "h";
+  case 105:
+    return "i";
+  case 106:
+    return "j";
+  case 107:
+    return "k";
+  case 108:
+    return "l";
+  case 109:
+    return "m";
+  case 110:
+    return "n";
+  case 111:
+    return "o";
+  case 112:
+    return "p";
+  case 113:
+    return "q";
+  case 114:
+    return "r";
+  case 115:
+    return "s";
+  case 116:
+    return "t";
+  case 117:
+    return "u";
+  case 118:
+    return "v";
+  case 119:
+    return "w";
+  case 120:
+    return "x";
+  case 121:
+    return "y";
+  case 122:
+    return "z";
+  case 123:
+    return "{";
+  case 124:
+    return "|";
+  case 125:
+    return "}";
+  case 126:
+    return "~";
+  case 127:
+    return "\\x7F";
+  case 128:
+    return "\\x80";
+  case 129:
+    return "\\x81";
+  case 130:
+    return "\\x82";
+  case 131:
+    return "\\x83";
+  case 132:
+    return "\\x84";
+  case 133:
+    return "\\x85";
+  case 134:
+    return "\\x86";
+  case 135:
+    return "\\x87";
+  case 136:
+    return "\\x88";
+  case 137:
+    return "\\x89";
+  case 138:
+    return "\\x8A";
+  case 139:
+    return "\\x8B";
+  case 140:
+    return "\\x8C";
+  case 141:
+    return "\\x8D";
+  case 142:
+    return "\\x8E";
+  case 143:
+    return "\\x8F";
+  case 144:
+    return "\\x90";
+  case 145:
+    return "\\x91";
+  case 146:
+    return "\\x92";
+  case 147:
+    return "\\x93";
+  case 148:
+    return "\\x94";
+  case 149:
+    return "\\x95";
+  case 150:
+    return "\\x96";
+  case 151:
+    return "\\x97";
+  case 152:
+    return "\\x98";
+  case 153:
+    return "\\x99";
+  case 154:
+    return "\\x9A";
+  case 155:
+    return "\\x9B";
+  case 156:
+    return "\\x9C";
+  case 157:
+    return "\\x9D";
+  case 158:
+    return "\\x9E";
+  case 159:
+    return "\\x9F";
+  case 160:
+    return "\\xA0";
+  case 161:
+    return "\\xA1";
+  case 162:
+    return "\\xA2";
+  case 163:
+    return "\\xA3";
+  case 164:
+    return "\\xA4";
+  case 165:
+    return "\\xA5";
+  case 166:
+    return "\\xA6";
+  case 167:
+    return "\\xA7";
+  case 168:
+    return "\\xA8";
+  case 169:
+    return "\\xA9";
+  case 170:
+    return "\\xAA";
+  case 171:
+    return "\\xAB";
+  case 172:
+    return "\\xAC";
+  case 173:
+    return "\\xAD";
+  case 174:
+    return "\\xAE";
+  case 175:
+    return "\\xAF";
+  case 176:
+    return "\\xB0";
+  case 177:
+    return "\\xB1";
+  case 178:
+    return "\\xB2";
+  case 179:
+    return "\\xB3";
+  case 180:
+    return "\\xB4";
+  case 181:
+    return "\\xB5";
+  case 182:
+    return "\\xB6";
+  case 183:
+    return "\\xB7";
+  case 184:
+    return "\\xB8";
+  case 185:
+    return "\\xB9";
+  case 186:
+    return "\\xBA";
+  case 187:
+    return "\\xBB";
+  case 188:
+    return "\\xBC";
+  case 189:
+    return "\\xBD";
+  case 190:
+    return "\\xBE";
+  case 191:
+    return "\\xBF";
+  case 192:
+    return "\\xC0";
+  case 193:
+    return "\\xC1";
+  case 194:
+    return "\\xC2";
+  case 195:
+    return "\\xC3";
+  case 196:
+    return "\\xC4";
+  case 197:
+    return "\\xC5";
+  case 198:
+    return "\\xC6";
+  case 199:
+    return "\\xC7";
+  case 200:
+    return "\\xC8";
+  case 201:
+    return "\\xC9";
+  case 202:
+    return "\\xCA";
+  case 203:
+    return "\\xCB";
+  case 204:
+    return "\\xCC";
+  case 205:
+    return "\\xCD";
+  case 206:
+    return "\\xCE";
+  case 207:
+    return "\\xCF";
+  case 208:
+    return "\\xD0";
+  case 209:
+    return "\\xD1";
+  case 210:
+    return "\\xD2";
+  case 211:
+    return "\\xD3";
+  case 212:
+    return "\\xD4";
+  case 213:
+    return "\\xD5";
+  case 214:
+    return "\\xD6";
+  case 215:
+    return "\\xD7";
+  case 216:
+    return "\\xD8";
+  case 217:
+    return "\\xD9";
+  case 218:
+    return "\\xDA";
+  case 219:
+    return "\\xDB";
+  case 220:
+    return "\\xDC";
+  case 221:
+    return "\\xDD";
+  case 222:
+    return "\\xDE";
+  case 223:
+    return "\\xDF";
+  case 224:
+    return "\\xE0";
+  case 225:
+    return "\\xE1";
+  case 226:
+    return "\\xE2";
+  case 227:
+    return "\\xE3";
+  case 228:
+    return "\\xE4";
+  case 229:
+    return "\\xE5";
+  case 230:
+    return "\\xE6";
+  case 231:
+    return "\\xE7";
+  case 232:
+    return "\\xE8";
+  case 233:
+    return "\\xE9";
+  case 234:
+    return "\\xEA";
+  case 235:
+    return "\\xEB";
+  case 236:
+    return "\\xEC";
+  case 237:
+    return "\\xED";
+  case 238:
+    return "\\xEE";
+  case 239:
+    return "\\xEF";
+  case 240:
+    return "\\xF0";
+  case 241:
+    return "\\xF1";
+  case 242:
+    return "\\xF2";
+  case 243:
+    return "\\xF3";
+  case 244:
+    return "\\xF4";
+  case 245:
+    return "\\xF5";
+  case 246:
+    return "\\xF6";
+  case 247:
+    return "\\xF7";
+  case 248:
+    return "\\xF8";
+  case 249:
+    return "\\xF9";
+  case 250:
+    return "\\xFA";
+  case 251:
+    return "\\xFB";
+  case 252:
+    return "\\xFC";
+  case 253:
+    return "\\xFD";
+  case 254:
+    return "\\xFE";
+  case 255:
+    return "\\xFF";
+  default:
+    assert(0); /* never gets here */
+    return "dead code";
+  }
+  assert(0); /* never gets here */
+}
+
+#endif /* XML_DTD */
+
+static unsigned long
+getDebugLevel(const char *variableName, unsigned long defaultDebugLevel) {
+  const char *const valueOrNull = getenv(variableName);
+  if (valueOrNull == NULL) {
+    return defaultDebugLevel;
+  }
+  const char *const value = valueOrNull;
+
+  errno = 0;
+  char *afterValue = (char *)value;
+  unsigned long debugLevel = strtoul(value, &afterValue, 10);
+  if ((errno != 0) || (afterValue[0] != '\0')) {
+    errno = 0;
+    return defaultDebugLevel;
+  }
+
+  return debugLevel;
+}
index 3b676a4..08173b0 100644 (file)
@@ -7,7 +7,14 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2002      Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2002-2006 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2002-2003 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2005-2009 Steven Solie <ssolie@users.sourceforge.net>
+   Copyright (c) 2016-2021 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
 
 #ifdef _WIN32
 #  include "winconfig.h"
-#else
-#  ifdef HAVE_EXPAT_CONFIG_H
-#    include <expat_config.h>
-#  endif
-#endif /* ndef _WIN32 */
+#endif
+
+#include <expat_config.h>
 
 #include "expat_external.h"
 #include "internal.h"
index 036aba6..d6e1fa1 100644 (file)
@@ -7,7 +7,10 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2002      Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2002      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2017      Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
index 58dce90..f2b6b40 100644 (file)
@@ -7,7 +7,19 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2001-2003 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2002      Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2002-2016 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2005-2009 Steven Solie <ssolie@users.sourceforge.net>
+   Copyright (c) 2016-2021 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2016      Pascal Cuoq <cuoq@trust-in-soft.com>
+   Copyright (c) 2016      Don Lewis <truckman@apache.org>
+   Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2017      Alexander Bluhm <alexander.bluhm@gmx.net>
+   Copyright (c) 2017      Benbuck Nason <bnason@netflix.com>
+   Copyright (c) 2017      José Gutiérrez de la Concha <jose@zeroc.com>
+   Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
 
 #ifdef _WIN32
 #  include "winconfig.h"
-#else
-#  ifdef HAVE_EXPAT_CONFIG_H
-#    include <expat_config.h>
-#  endif
-#endif /* ndef _WIN32 */
+#endif
+
+#include <expat_config.h>
 
 #include "expat_external.h"
 #include "internal.h"
@@ -261,8 +271,14 @@ sb_byteToAscii(const ENCODING *enc, const char *p) {
 
 #define IS_NAME_CHAR(enc, p, n) (AS_NORMAL_ENCODING(enc)->isName##n(enc, p))
 #define IS_NMSTRT_CHAR(enc, p, n) (AS_NORMAL_ENCODING(enc)->isNmstrt##n(enc, p))
-#define IS_INVALID_CHAR(enc, p, n)                                             \
-  (AS_NORMAL_ENCODING(enc)->isInvalid##n(enc, p))
+#ifdef XML_MIN_SIZE
+#  define IS_INVALID_CHAR(enc, p, n)                                           \
+    (AS_NORMAL_ENCODING(enc)->isInvalid##n                                     \
+     && AS_NORMAL_ENCODING(enc)->isInvalid##n(enc, p))
+#else
+#  define IS_INVALID_CHAR(enc, p, n)                                           \
+    (AS_NORMAL_ENCODING(enc)->isInvalid##n(enc, p))
+#endif
 
 #ifdef XML_MIN_SIZE
 #  define IS_NAME_CHAR_MINBPC(enc, p)                                          \
index 2adbf53..6f630c2 100644 (file)
@@ -7,7 +7,11 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2002      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2002-2005 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2016-2017 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
index 06d5c90..0430591 100644 (file)
@@ -1,4 +1,4 @@
-/* This file is included!
+/* This file is included (from xmltok.c, 1-3 times depending on XML_MIN_SIZE)!
                             __  __            _
                          ___\ \/ /_ __   __ _| |_
                         / _ \\  /| '_ \ / _` | __|
@@ -7,7 +7,15 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2002      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2002-2016 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2016-2021 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2018      Benjamin Peterson <benjamin@python.org>
+   Copyright (c) 2018      Anton Maklakov <antmak.pub@gmail.com>
+   Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
+   Copyright (c) 2020      Boris Kolpackov <boris@codesynthesis.com>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -32,7 +40,7 @@
 
 #ifdef XML_TOK_IMPL_C
 
-#  ifndef IS_INVALID_CHAR
+#  ifndef IS_INVALID_CHAR // i.e. for UTF-16 and XML_MIN_SIZE not defined
 #    define IS_INVALID_CHAR(enc, ptr, n) (0)
 #  endif
 
index e925dbc..c518aad 100644 (file)
@@ -7,7 +7,8 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2017-2019 Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
index 919c74e..5fd8392 100644 (file)
@@ -7,7 +7,11 @@
                                  |_| XML parser
 
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
+   Copyright (c) 2002      Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2002      Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2002-2006 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2017      Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
index 262f3bc..d85251e 100644 (file)
@@ -1,2 +1,2 @@
-major=13
+major=14
 minor=0
index fda8268..ba2bc6e 100644 (file)
@@ -6,8 +6,10 @@
                         \___/_/\_\ .__/ \__,_|\__|
                                  |_| XML parser
 
-   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2003-2006 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2005-2007 Steven Solie <ssolie@users.sourceforge.net>
+   Copyright (c) 2017      Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
index 75a5016..d1989a8 100644 (file)
@@ -6,8 +6,12 @@
                         \___/_/\_\ .__/ \__,_|\__|
                                  |_| XML parser
 
-   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2002-2004 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2003      Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2016      Gilles Espinasse <g.esp@free.fr>
+   Copyright (c) 2016-2021 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017      Joe Orton <jorton@redhat.com>
+   Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -30,9 +34,7 @@
    USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
-#ifdef HAVE_EXPAT_CONFIG_H
-#  include <expat_config.h>
-#endif
+#include <expat_config.h>
 #include "minicheck.h"
 
 #include <assert.h>
index 4001b9b..ccb631f 100644 (file)
@@ -7,8 +7,9 @@
                         \___/_/\_\ .__/ \__,_|\__|
                                  |_| XML parser
 
-   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2002-2004 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2017      Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
index 41355f6..48822e5 100644 (file)
@@ -6,8 +6,8 @@
                         \___/_/\_\ .__/ \__,_|\__|
                                  |_| XML parser
 
-   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2017 Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2017 Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
index 4d20f4b..4c0cb72 100644 (file)
@@ -7,8 +7,8 @@
                         \___/_/\_\ .__/ \__,_|\__|
                                  |_| XML parser
 
-   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2017 Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2017 Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
index ab0c35f..1c65748 100644 (file)
                         \___/_/\_\ .__/ \__,_|\__|
                                  |_| XML parser
 
-   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2004-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2016-2020 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2018      Marco Maggi <marco.maggi-ipsu@poste.it>
+   Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
index 88a1658..cc1f835 100644 (file)
@@ -12,8 +12,9 @@
                         \___/_/\_\ .__/ \__,_|\__|
                                  |_| XML parser
 
-   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2004-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2006-2012 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2016-2017 Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
index 91ab4f2..45ba5d5 100644 (file)
@@ -6,8 +6,17 @@
                         \___/_/\_\ .__/ \__,_|\__|
                                  |_| XML parser
 
-   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2001-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2003      Greg Stein <gstein@users.sourceforge.net>
+   Copyright (c) 2005-2007 Steven Solie <ssolie@users.sourceforge.net>
+   Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net>
+   Copyright (c) 2016-2021 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017-2018 Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2017      Joe Orton <jorton@redhat.com>
+   Copyright (c) 2017      José Gutiérrez de la Concha <jose@zeroc.com>
+   Copyright (c) 2018      Marco Maggi <marco.maggi-ipsu@poste.it>
+   Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
+   Copyright (c) 2020      Tim Gates <tim.gates@iress.com>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -34,9 +43,7 @@
 #  undef NDEBUG /* because test suite relies on assert(...) at the moment */
 #endif
 
-#ifdef HAVE_EXPAT_CONFIG_H
-#  include <expat_config.h>
-#endif
+#include <expat_config.h>
 
 #include <assert.h>
 #include <stdlib.h>
@@ -46,6 +53,7 @@
 #include <ctype.h>
 #include <limits.h>
 #include <stdint.h> /* intptr_t uint64_t */
+#include <math.h>   /* NAN, INFINITY, isnan */
 
 #if ! defined(__cplusplus)
 #  include <stdbool.h>
@@ -54,7 +62,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"
@@ -2247,7 +2255,6 @@ START_TEST(test_long_cdata_utf16) {
 END_TEST
 
 /* Test handling of multiple unit UTF-16 characters */
-#ifndef XML_MIN_SIZE /* FIXME workaround -DXML_MIN_SIZE + ASan (issue #332) */
 START_TEST(test_multichar_cdata_utf16) {
   /* Test data is:
    *   <?xml version='1.0' encoding='utf-16'?>
@@ -2269,11 +2276,11 @@ START_TEST(test_multichar_cdata_utf16) {
                       "\0<\0a\0>\0<\0!\0[\0C\0D\0A\0T\0A\0["
                       "\xd8\x34\xdd\x5e\xd8\x34\xdd\x5f"
                       "\0]\0]\0>\0<\0/\0a\0>";
-#  ifdef XML_UNICODE
+#ifdef XML_UNICODE
   const XML_Char *expected = XCS("\xd834\xdd5e\xd834\xdd5f");
-#  else
+#else
   const XML_Char *expected = XCS("\xf0\x9d\x85\x9e\xf0\x9d\x85\x9f");
-#  endif
+#endif
   CharData storage;
 
   CharData_Init(&storage);
@@ -2286,7 +2293,6 @@ START_TEST(test_multichar_cdata_utf16) {
   CharData_CheckXMLChars(&storage, expected);
 }
 END_TEST
-#endif /* ifndef XML_MIN_SIZE */
 
 /* Test that an element name with a UTF-16 surrogate pair is rejected */
 START_TEST(test_utf16_bad_surrogate_pair) {
@@ -2371,7 +2377,6 @@ START_TEST(test_bad_cdata) {
 END_TEST
 
 /* Test failures in UTF-16 CDATA */
-#ifndef XML_MIN_SIZE /* FIXME workaround -DXML_MIN_SIZE + ASan (issue #332) */
 START_TEST(test_bad_cdata_utf16) {
   struct CaseData {
     size_t text_bytes;
@@ -2444,7 +2449,6 @@ START_TEST(test_bad_cdata_utf16) {
   }
 }
 END_TEST
-#endif /* ifndef XML_MIN_SIZE */
 
 static const char *long_cdata_text
     = "<s><![CDATA["
@@ -7347,7 +7351,7 @@ START_TEST(test_misc_version) {
     fail("Version mismatch");
 
 #if ! defined(XML_UNICODE) || defined(XML_UNICODE_WCHAR_T)
-  if (xcstrcmp(version_text, XCS("expat_2.3.0"))) /* needs bump on releases */
+  if (xcstrcmp(version_text, XCS("expat_2.4.1"))) /* needs bump on releases */
     fail("XML_*_VERSION in expat.h out of sync?\n");
 #else
   /* If we have XML_UNICODE defined but not XML_UNICODE_WCHAR_T
@@ -11222,6 +11226,389 @@ START_TEST(test_nsalloc_prefixed_element) {
 }
 END_TEST
 
+#if defined(XML_DTD)
+typedef enum XML_Status (*XmlParseFunction)(XML_Parser, const char *, int, int);
+
+struct AccountingTestCase {
+  const char *primaryText;
+  const char *firstExternalText;  /* often NULL */
+  const char *secondExternalText; /* often NULL */
+  const unsigned long long expectedCountBytesIndirectExtra;
+  XML_Bool singleBytesWanted;
+};
+
+static int
+accounting_external_entity_ref_handler(XML_Parser parser,
+                                       const XML_Char *context,
+                                       const XML_Char *base,
+                                       const XML_Char *systemId,
+                                       const XML_Char *publicId) {
+  UNUSED_P(context);
+  UNUSED_P(base);
+  UNUSED_P(publicId);
+
+  const struct AccountingTestCase *const testCase
+      = (const struct AccountingTestCase *)XML_GetUserData(parser);
+
+  const char *externalText = NULL;
+  if (xcstrcmp(systemId, XCS("first.ent")) == 0) {
+    externalText = testCase->firstExternalText;
+  } else if (xcstrcmp(systemId, XCS("second.ent")) == 0) {
+    externalText = testCase->secondExternalText;
+  } else {
+    assert(! "systemId is neither \"first.ent\" nor \"second.ent\"");
+  }
+  assert(externalText);
+
+  XML_Parser entParser = XML_ExternalEntityParserCreate(parser, context, 0);
+  assert(entParser);
+
+  const XmlParseFunction xmlParseFunction
+      = testCase->singleBytesWanted ? _XML_Parse_SINGLE_BYTES : XML_Parse;
+
+  const enum XML_Status status = xmlParseFunction(
+      entParser, externalText, (int)strlen(externalText), XML_TRUE);
+
+  XML_ParserFree(entParser);
+  return status;
+}
+
+START_TEST(test_accounting_precision) {
+  const XML_Bool filled_later = XML_TRUE; /* value is arbitrary */
+  struct AccountingTestCase cases[] = {
+      {"<e/>", NULL, NULL, 0, 0},
+      {"<e></e>", NULL, NULL, 0, 0},
+
+      /* Attributes */
+      {"<e k1=\"v2\" k2=\"v2\"/>", NULL, NULL, 0, filled_later},
+      {"<e k1=\"v2\" k2=\"v2\"></e>", NULL, NULL, 0, 0},
+      {"<p:e xmlns:p=\"https://domain.invalid/\" />", NULL, NULL, 0,
+       filled_later},
+      {"<e k=\"&amp;&apos;&gt;&lt;&quot;\" />", NULL, NULL,
+       sizeof(XML_Char) * 5 /* number of predefined entites */, filled_later},
+      {"<e1 xmlns='https://example.org/'>\n"
+       "  <e2 xmlns=''/>\n"
+       "</e1>",
+       NULL, NULL, 0, filled_later},
+
+      /* Text */
+      {"<e>text</e>", NULL, NULL, 0, filled_later},
+      {"<e1><e2>text1<e3/>text2</e2></e1>", NULL, NULL, 0, filled_later},
+      {"<e>&amp;&apos;&gt;&lt;&quot;</e>", NULL, NULL,
+       sizeof(XML_Char) * 5 /* number of predefined entites */, filled_later},
+      {"<e>&#65;&#41;</e>", NULL, NULL, 0, filled_later},
+
+      /* Prolog */
+      {"<?xml version=\"1.0\"?><root/>", NULL, NULL, 0, filled_later},
+
+      /* Whitespace */
+      {"  <e1>  <e2>  </e2>  </e1>  ", NULL, NULL, 0, filled_later},
+      {"<e1  ><e2  /></e1  >", NULL, NULL, 0, filled_later},
+      {"<e1><e2 k = \"v\"/><e3 k = 'v'/></e1>", NULL, NULL, 0, filled_later},
+
+      /* Comments */
+      {"<!-- Comment --><e><!-- Comment --></e>", NULL, NULL, 0, filled_later},
+
+      /* Processing instructions */
+      {"<?xml-stylesheet type=\"text/xsl\" href=\"https://domain.invalid/\" media=\"all\"?><e/>",
+       NULL, NULL, 0, filled_later},
+      {"<?pi0?><?pi1 ?><?pi2  ?><!DOCTYPE r SYSTEM 'first.ent'><r/>",
+       "<?pi3?><!ENTITY % e1 SYSTEM 'second.ent'><?pi4?>%e1;<?pi5?>", "<?pi6?>",
+       0, filled_later},
+
+      /* CDATA */
+      {"<e><![CDATA[one two three]]></e>", NULL, NULL, 0, filled_later},
+      /* The following is the essence of this OSS-Fuzz finding:
+         https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=34302
+         https://oss-fuzz.com/testcase-detail/4860575394955264
+      */
+      {"<!DOCTYPE r [\n"
+       "<!ENTITY e \"111<![CDATA[2 <= 2]]>333\">\n"
+       "]>\n"
+       "<r>&e;</r>\n",
+       NULL, NULL, sizeof(XML_Char) * strlen("111<![CDATA[2 <= 2]]>333"),
+       filled_later},
+
+      /* Conditional sections */
+      {"<!DOCTYPE r [\n"
+       "<!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");
@@ -11230,6 +11617,9 @@ make_suite(void) {
   TCase *tc_misc = tcase_create("miscellaneous tests");
   TCase *tc_alloc = tcase_create("allocation tests");
   TCase *tc_nsalloc = tcase_create("namespace allocation tests");
+#if defined(XML_DTD)
+  TCase *tc_accounting = tcase_create("accounting tests");
+#endif
 
   suite_add_tcase(s, tc_basic);
   tcase_add_checked_fixture(tc_basic, basic_setup, basic_teardown);
@@ -11303,14 +11693,10 @@ make_suite(void) {
   tcase_add_test(tc_basic, test_good_cdata_utf16);
   tcase_add_test(tc_basic, test_good_cdata_utf16_le);
   tcase_add_test(tc_basic, test_long_cdata_utf16);
-#ifndef XML_MIN_SIZE /* FIXME workaround -DXML_MIN_SIZE + ASan (issue #332) */
   tcase_add_test(tc_basic, test_multichar_cdata_utf16);
-#endif
   tcase_add_test(tc_basic, test_utf16_bad_surrogate_pair);
   tcase_add_test(tc_basic, test_bad_cdata);
-#ifndef XML_MIN_SIZE /* FIXME workaround -DXML_MIN_SIZE + ASan (issue #332) */
   tcase_add_test(tc_basic, test_bad_cdata_utf16);
-#endif
   tcase_add_test(tc_basic, test_stop_parser_between_cdata_calls);
   tcase_add_test(tc_basic, test_suspend_parser_between_cdata_calls);
   tcase_add_test(tc_basic, test_memory_allocation);
@@ -11594,6 +11980,13 @@ make_suite(void) {
   tcase_add_test(tc_nsalloc, test_nsalloc_long_systemid_in_ext);
   tcase_add_test(tc_nsalloc, test_nsalloc_prefixed_element);
 
+#if defined(XML_DTD)
+  suite_add_tcase(s, tc_accounting);
+  tcase_add_test(tc_accounting, test_accounting_precision);
+  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;
 }
 
index fd3ceaa..52f5305 100644 (file)
@@ -9,8 +9,8 @@
                         \___/_/\_\ .__/ \__,_|\__|
                                  |_| XML parser
 
-   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2005 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+   Copyright (c) 2017 Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
index e81b7b1..d40e6c4 100644 (file)
@@ -6,8 +6,8 @@
                         \___/_/\_\ .__/ \__,_|\__|
                                  |_| XML parser
 
-   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
+   Copyright (c) 2017-2021 Sebastian Pipping <sebastian@pipping.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -30,9 +30,7 @@
    USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
-#ifdef HAVE_EXPAT_CONFIG_H
-#  include "expat_config.h"
-#endif
+#include "expat_config.h"
 
 #include <assert.h>
 #include <stdlib.h>
index 870ffaf..09881b1 100644 (file)
@@ -7,8 +7,7 @@
                         \___/_/\_\ .__/ \__,_|\__|
                                  |_| XML parser
 
-   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
-   Copyright (c) 2000-2017 Expat development team
+   Copyright (c) 2017 Rhodri James <rhodri@wildebeest.org.uk>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
index 9b50035..dc409d0 100755 (executable)
@@ -1,24 +1,55 @@
 #! /usr/bin/env bash
-
-#   EXPAT TEST SCRIPT FOR W3C XML TEST SUITE
-
+# EXPAT TEST SCRIPT FOR W3C XML TEST SUITE
+#
 # This script can be used to exercise Expat against the
 # w3c.org xml test suite, available from
 # http://www.w3.org/XML/Test/xmlts20020606.zip.
-
+#
 # To run this script, first set XMLWF below so that xmlwf can be
 # found, then set the output directory with OUTPUT.
-
+#
 # The script lists all test cases where Expat shows a discrepancy
 # from the expected result. Test cases where only the canonical
 # output differs are prefixed with "Output differs:", and a diff file
 # is generated in the appropriate subdirectory under $OUTPUT.
-
+#
 # If there are output files provided, the script will use
 # output from xmlwf and compare the desired output against it.
 # However, one has to take into account that the canonical output
 # produced by xmlwf conforms to an older definition of canonical XML
 # and does not generate notation declarations.
+#
+#                          __  __            _
+#                       ___\ \/ /_ __   __ _| |_
+#                      / _ \\  /| '_ \ / _` | __|
+#                     |  __//  \| |_) | (_| | |_
+#                      \___/_/\_\ .__/ \__,_|\__|
+#                               |_| XML parser
+#
+# Copyright (c) 2002-2004 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
+# Copyright (c) 2002      Karl Waclawek <karl@waclawek.net>
+# Copyright (c) 2008-2019 Sebastian Pipping <sebastian@pipping.org>
+# Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
+# Licensed under the MIT license:
+#
+# Permission is  hereby granted,  free of charge,  to any  person obtaining
+# a  copy  of  this  software   and  associated  documentation  files  (the
+# "Software"),  to  deal in  the  Software  without restriction,  including
+# without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+# distribute, sublicense, and/or sell copies of the Software, and to permit
+# persons  to whom  the Software  is  furnished to  do so,  subject to  the
+# following conditions:
+#
+# The above copyright  notice and this permission notice  shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+# EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+# NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+# DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+# USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 shopt -s nullglob