git: dec0bf8096b3 - releng/13.5 - contrib/expat: update libexpat from 2.6.4 to 2.7.1

From: Philip Paeps <philip_at_FreeBSD.org>
Date: Thu, 10 Apr 2025 14:59:02 UTC
The branch releng/13.5 has been updated by philip:

URL: https://cgit.FreeBSD.org/src/commit/?id=dec0bf8096b312c395421d11c0e76e954a7e2386

commit dec0bf8096b312c395421d11c0e76e954a7e2386
Author:     Philip Paeps <philip@FreeBSD.org>
AuthorDate: 2025-04-02 08:56:02 +0000
Commit:     Philip Paeps <philip@FreeBSD.org>
CommitDate: 2025-04-10 14:39:08 +0000

    contrib/expat: update libexpat from 2.6.4 to 2.7.1
    
    Changes: https://github.com/libexpat/libexpat/blob/R_2_7_1/expat/Changes
    
    Note that libbsdxml(3) is only intended to used by utilities in the
    FreeBSD base system.  The vulnerability addressed by expat 2.7.0 is not
    exploitable on FreeBSD as supported by the security-officer@ team.
    
    Approved by:    so
    Security:       FreeBSD-EN-25:05.expat
    Security:       CVE-2024-8176
    
    (cherry picked from commit fe9278888fd4414abe2d922e469cf608005f4c65)
    (cherry picked from commit 41b768ae1970ed484abaaea401453c3902df93c2)
    (cherry picked from commit 03a1992591b0ae85b6b250255fe56e17f6d919c6)
    (cherry picked from commit adc9e9e8dbddcf7d57bcdef0d9d0a0e7c08c15ba)
    (cherry picked from commit 00c8538e87c61f1fd57ccd9e02a6d435b68d9a73)
    (cherry picked from commit 5630672e6f6d58597a3d6f01928a7703f1cdd207)
---
 contrib/expat/COPYING                       |   2 +-
 contrib/expat/Changes                       | 123 +++++-
 contrib/expat/Makefile.am                   |   4 +-
 contrib/expat/Makefile.in                   |   4 +-
 contrib/expat/README.md                     |  18 +-
 contrib/expat/configure.ac                  |   4 +-
 contrib/expat/doc/reference.html            |   9 +-
 contrib/expat/doc/xmlwf.1                   |   2 +-
 contrib/expat/doc/xmlwf.xml                 |   4 +-
 contrib/expat/fuzz/xml_lpm_fuzzer.cpp       | 464 ++++++++++++++++++++++
 contrib/expat/fuzz/xml_lpm_fuzzer.proto     |  58 +++
 contrib/expat/fuzz/xml_parse_fuzzer.c       |   2 +-
 contrib/expat/fuzz/xml_parsebuffer_fuzzer.c |   2 +-
 contrib/expat/lib/expat.h                   |   6 +-
 contrib/expat/lib/internal.h                |   5 +-
 contrib/expat/lib/xmlparse.c                | 586 ++++++++++++++++++++--------
 contrib/expat/tests/acc_tests.c             |   5 +-
 contrib/expat/tests/alloc_tests.c           |  27 ++
 contrib/expat/tests/basic_tests.c           | 331 +++++++++++++++-
 contrib/expat/tests/benchmark/benchmark.c   |  57 ++-
 contrib/expat/tests/common.c                |  33 +-
 contrib/expat/tests/common.h                |   4 +-
 contrib/expat/tests/handlers.c              |  23 ++
 contrib/expat/tests/handlers.h              |   9 +
 contrib/expat/tests/minicheck.h             |   6 +-
 contrib/expat/tests/misc_tests.c            | 247 ++++++++++--
 contrib/expat/tests/xmltest.sh              |   5 +-
 contrib/expat/xmlwf/readfilemap.c           |   3 +-
 lib/libexpat/expat_config.h                 |   6 +-
 lib/libexpat/libbsdxml.3                    |   4 +-
 30 files changed, 1784 insertions(+), 269 deletions(-)

diff --git a/contrib/expat/COPYING b/contrib/expat/COPYING
index ce9e5939291e..c6d184a8aae8 100644
--- a/contrib/expat/COPYING
+++ b/contrib/expat/COPYING
@@ -1,5 +1,5 @@
 Copyright (c) 1998-2000 Thai Open Source Software Center Ltd and Clark Cooper
-Copyright (c) 2001-2022 Expat maintainers
+Copyright (c) 2001-2025 Expat maintainers
 
 Permission is hereby granted, free of charge, to any person obtaining
 a copy of this software and associated documentation files (the
diff --git a/contrib/expat/Changes b/contrib/expat/Changes
index aa19f70ae219..9d6c64b6a460 100644
--- a/contrib/expat/Changes
+++ b/contrib/expat/Changes
@@ -11,16 +11,23 @@
 !! The following topics need *additional skilled C developers* to progress   !!
 !! in a timely manner or at all (loosely ordered by descending priority):    !!
 !!                                                                           !!
-!! - <blink>fixing a complex non-public security issue</blink>,              !!
 !! - teaming up on researching and fixing future security reports and        !!
 !!   ClusterFuzz findings with few-days-max response times in communication  !!
 !!   in order to (1) have a sound fix ready before the end of a 90 days      !!
 !!   grace period and (2) in a sustainable manner,                           !!
+!! - helping CPython Expat bindings with supporting Expat's billion laughs   !!
+!!   attack protection API (https://github.com/python/cpython/issues/90949): !!
+!!   - XML_SetBillionLaughsAttackProtectionActivationThreshold               !!
+!!   - XML_SetBillionLaughsAttackProtectionMaximumAmplification              !!
+!! - helping Perl's XML::Parser Expat bindings with supporting Expat's       !!
+!!   security API (https://github.com/cpan-authors/XML-Parser/issues/102):   !!
+!!   - XML_SetBillionLaughsAttackProtectionActivationThreshold               !!
+!!   - XML_SetBillionLaughsAttackProtectionMaximumAmplification              !!
+!!   - XML_SetReparseDeferralEnabled                                         !!
 !! - implementing and auto-testing XML 1.0r5 support                         !!
 !!   (needs discussion before pull requests),                                !!
 !! - smart ideas on fixing the Autotools CMake files generation issue        !!
 !!   without breaking CI (needs discussion before pull requests),            !!
-!! - the Windows binaries topic (needs requirements engineering first),      !!
 !! - pushing migration from `int` to `size_t` further                        !!
 !!   including edge-cases test coverage (needs discussion before anything).  !!
 !!                                                                           !!
@@ -30,6 +37,116 @@
 !! THANK YOU!                        Sebastian Pipping -- Berlin, 2024-03-09 !!
 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
+Release 2.7.1 Thu March 27 2025
+        Bug fixes:
+       #980 #989  Restore event pointer behavior from Expat 2.6.4
+                    (that the fix to CVE-2024-8176 changed in 2.7.0);
+                    affected API functions are:
+                    - XML_GetCurrentByteCount
+                    - XML_GetCurrentByteIndex
+                    - XML_GetCurrentColumnNumber
+                    - XML_GetCurrentLineNumber
+                    - XML_GetInputContext
+
+        Other changes:
+       #976 #977  Autotools: Integrate files "fuzz/xml_lpm_fuzzer.{cpp,proto}"
+                    with Automake that were missing from 2.7.0 release tarballs
+       #983 #984  Fix printf format specifiers for 32bit Emscripten
+            #992  docs: Promote OpenSSF Best Practices self-certification
+            #978  tests/benchmark: Resolve mistaken double close
+            #986  Address compiler warnings
+       #990 #993  Version info bumped from 11:1:10 (libexpat*.so.1.10.1)
+                    to 11:2:10 (libexpat*.so.1.10.2); see https://verbump.de/
+                    for what these numbers do
+
+        Infrastructure:
+            #982  CI: Start running Perl XML::Parser integration tests
+            #987  CI: Enforce Clang Static Analyzer clean code
+            #991  CI: Re-enable warning clang-analyzer-valist.Uninitialized
+                    for clang-tidy
+            #981  CI: Cover compilation with musl
+       #983 #984  CI: Cover compilation with 32bit Emscripten
+       #976 #977  CI: Protect against fuzzer files missing from future
+                    release archives
+
+        Special thanks to:
+            Berkay Eren Ürün
+            Matthew Fernandez
+                 and
+            Perl XML::Parser
+
+Release 2.7.0 Thu March 13 2025
+        Security fixes:
+       #893 #973  CVE-2024-8176 -- Fix crash from chaining a large number
+                    of entities caused by stack overflow by resolving use of
+                    recursion, for all three uses of entities:
+                    - general entities in character data ("<e>&g1;</e>")
+                    - general entities in attribute values ("<e k1='&g1;'/>")
+                    - parameter entities ("%p1;")
+                    Known impact is (reliable and easy) denial of service:
+                    CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H/E:H/RL:O/RC:C
+                    (Base Score: 7.5, Temporal Score: 7.2)
+                    Please note that a layer of compression around XML can
+                    significantly reduce the minimum attack payload size.
+
+        Other changes:
+       #935 #937  Autotools: Make generated CMake files look for
+                    libexpat.@SO_MAJOR@.dylib on macOS
+            #925  Autotools: Sync CMake templates with CMake 3.29
+  #945 #962 #966  CMake: Drop support for CMake <3.13
+            #942  CMake: Small fuzzing related improvements
+            #921  docs: Add missing documentation of error code
+                    XML_ERROR_NOT_STARTED that was introduced with 2.6.4
+            #941  docs: Document need for C++11 compiler for use from C++
+            #959  tests/benchmark: Fix a (harmless) TOCTTOU
+            #944  Windows: Fix installer target location of file xmlwf.xml
+                    for CMake
+            #953  Windows: Address warning -Wunknown-warning-option
+                    about -Wno-pedantic-ms-format from LLVM MinGW
+            #971  Address Cppcheck warnings
+       #969 #970  Mass-migrate links from http:// to https://
+    #947 #958 ..
+       #974 #975  Document changes since the previous release
+       #974 #975  Version info bumped from 11:0:10 (libexpat*.so.1.10.0)
+                    to 11:1:10 (libexpat*.so.1.10.1); see https://verbump.de/
+                    for what these numbers do
+
+        Infrastructure:
+            #926  tests: Increase robustness
+    #927 #932 ..
+       #930 #933  tests: Increase test coverage
+    #617 #950 ..
+    #951 #952 ..
+    #954 #955 ..  Fuzzing: Add new fuzzer "xml_lpm_fuzzer" based on
+            #961    Google's libprotobuf-mutator ("LPM")
+            #957  Fuzzing|CI: Start producing fuzzing code coverage reports
+            #936  CI: Pass -q -q for LCOV >=2.1 in coverage.sh
+            #942  CI: Small fuzzing related improvements
+    #139 #203 ..
+       #791 #946  CI: Make GitHub Actions build using MSVC on Windows and
+                      produce 32bit and 64bit Windows binaries
+            #956  CI: Get off of about-to-be-removed Ubuntu 20.04
+       #960 #964  CI: Start uploading to Coverity Scan for static analysis
+            #972  CI: Stop loading DTD from the internet to address flaky CI
+            #971  CI: Adapt to breaking changes in Cppcheck
+
+        Special thanks to:
+            Alexander Gieringer
+            Berkay Eren Ürün
+            Hanno Böck
+            Jann Horn
+            Mark Brand
+            Sebastian Andrzej Siewior
+            Snild Dolkow
+            Thomas Pröll
+            Tomas Korbar
+            valord577
+                 and
+            Google Project Zero
+            Linutronix
+            Red Hat
+            Siemens
+
 Release 2.6.4 Wed November 6 2024
         Security fixes:
             #915  CVE-2024-50602 -- Fix crash within function XML_ResumeParser
@@ -46,6 +163,8 @@ Release 2.6.4 Wed November 6 2024
             #904  tests: Resolve duplicate handler
        #317 #918  tests: Improve tests on doctype closing (ex CVE-2019-15903)
             #914  Fix signedness of format strings
+            #915  For use from C++, expat.h started requiring C++11 due to
+                    use of C99 features
        #919 #920  Version info bumped from 10:3:9 (libexpat*.so.1.9.3)
                     to 11:0:10 (libexpat*.so.1.10.0); see https://verbump.de/
                     for what these numbers do
diff --git a/contrib/expat/Makefile.am b/contrib/expat/Makefile.am
index 7d8e17c2cf86..c20531a8d6c6 100644
--- a/contrib/expat/Makefile.am
+++ b/contrib/expat/Makefile.am
@@ -6,7 +6,7 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2017-2023 Sebastian Pipping <sebastian@pipping.org>
+# Copyright (c) 2017-2025 Sebastian Pipping <sebastian@pipping.org>
 # Copyright (c) 2018      KangLin <kl222@126.com>
 # Copyright (c) 2022      Johnny Jazeix <jazeix@gmail.com>
 # Copyright (c) 2023      Sony Corporation / Snild Dolkow <snild@sony.com>
@@ -96,6 +96,8 @@ EXTRA_DIST = \
     conftools/expat.m4 \
     conftools/get-version.sh \
     \
+    fuzz/xml_lpm_fuzzer.cpp \
+    fuzz/xml_lpm_fuzzer.proto \
     fuzz/xml_parsebuffer_fuzzer.c \
     fuzz/xml_parse_fuzzer.c \
     \
diff --git a/contrib/expat/Makefile.in b/contrib/expat/Makefile.in
index c0fcb5dd05d1..069ec4047eea 100644
--- a/contrib/expat/Makefile.in
+++ b/contrib/expat/Makefile.in
@@ -22,7 +22,7 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2017-2023 Sebastian Pipping <sebastian@pipping.org>
+# Copyright (c) 2017-2025 Sebastian Pipping <sebastian@pipping.org>
 # Copyright (c) 2018      KangLin <kl222@126.com>
 # Copyright (c) 2022      Johnny Jazeix <jazeix@gmail.com>
 # Copyright (c) 2023      Sony Corporation / Snild Dolkow <snild@sony.com>
@@ -494,6 +494,8 @@ EXTRA_DIST = \
     conftools/expat.m4 \
     conftools/get-version.sh \
     \
+    fuzz/xml_lpm_fuzzer.cpp \
+    fuzz/xml_lpm_fuzzer.proto \
     fuzz/xml_parsebuffer_fuzzer.c \
     fuzz/xml_parse_fuzzer.c \
     \
diff --git a/contrib/expat/README.md b/contrib/expat/README.md
index 23d26dad2b92..77c6bf27d307 100644
--- a/contrib/expat/README.md
+++ b/contrib/expat/README.md
@@ -3,6 +3,7 @@
 [![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)
+[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/10205/badge)](https://www.bestpractices.dev/projects/10205)
 
 > [!CAUTION]
 >
@@ -11,7 +12,7 @@
 > at the top of the `Changes` file.
 
 
-# Expat, Release 2.6.4
+# Expat, Release 2.7.1
 
 This is Expat, a C99 library for parsing
 [XML 1.0 Fourth Edition](https://www.w3.org/TR/2006/REC-xml-20060816/), started by
@@ -22,9 +23,9 @@ are called when the parser discovers the associated structures in the
 document being parsed.  A start tag is an example of the kind of
 structures for which you may register handlers.
 
-Expat supports the following compilers:
+Expat supports the following C99 compilers:
 
-- GNU GCC >=4.5
+- GNU GCC >=4.5 (for use from C) or GNU GCC >=4.8.1 (for use from C++)
 - LLVM Clang >=3.5
 - Microsoft Visual Studio >=16.0/2019 (rolling `${today} minus 5 years`)
 
@@ -52,7 +53,7 @@ This approach leverages CMake's own [module `FindEXPAT`](https://cmake.org/cmake
 Notice the *uppercase* `EXPAT` in the following example:
 
 ```cmake
-cmake_minimum_required(VERSION 3.0)  # or 3.10, see below
+cmake_minimum_required(VERSION 3.10)
 
 project(hello VERSION 1.0.0)
 
@@ -62,12 +63,7 @@ add_executable(hello
     hello.c
 )
 
-# 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) `find_package` with Config Mode
@@ -85,7 +81,7 @@ or
 Notice the *lowercase* `expat` in the following example:
 
 ```cmake
-cmake_minimum_required(VERSION 3.0)
+cmake_minimum_required(VERSION 3.10)
 
 project(hello VERSION 1.0.0)
 
@@ -295,7 +291,7 @@ EXPAT_ENABLE_INSTALL:BOOL=ON
 // Use /MT flag (static CRT) when compiling in MSVC
 EXPAT_MSVC_STATIC_CRT:BOOL=OFF
 
-// Build fuzzers via ossfuzz for the expat library
+// Build fuzzers via OSS-Fuzz for the expat library
 EXPAT_OSSFUZZ_BUILD:BOOL=OFF
 
 // Build a shared expat library
diff --git a/contrib/expat/configure.ac b/contrib/expat/configure.ac
index fffcd125e9c4..0c88b8867019 100644
--- a/contrib/expat/configure.ac
+++ b/contrib/expat/configure.ac
@@ -11,7 +11,7 @@ dnl   Copyright (c) 2000      Clark Cooper <coopercc@users.sourceforge.net>
 dnl   Copyright (c) 2000-2005 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
 dnl   Copyright (c) 2001-2003 Greg Stein <gstein@users.sourceforge.net>
 dnl   Copyright (c) 2006-2012 Karl Waclawek <karl@waclawek.net>
-dnl   Copyright (c) 2016-2024 Sebastian Pipping <sebastian@pipping.org>
+dnl   Copyright (c) 2016-2025 Sebastian Pipping <sebastian@pipping.org>
 dnl   Copyright (c) 2017      S. P. Zeidler <spz@netbsd.org>
 dnl   Copyright (c) 2017      Stephen Groat <stephen@groat.us>
 dnl   Copyright (c) 2017-2020 Joe Orton <jorton@redhat.com>
@@ -85,7 +85,7 @@ dnl If the API changes incompatibly set LIBAGE back to 0
 dnl
 
 LIBCURRENT=11  # sync
-LIBREVISION=0  # with
+LIBREVISION=2  # with
 LIBAGE=10      # CMakeLists.txt!
 
 AC_CONFIG_HEADERS([expat_config.h])
diff --git a/contrib/expat/doc/reference.html b/contrib/expat/doc/reference.html
index c2ae9bb71431..2b3bd39580a9 100644
--- a/contrib/expat/doc/reference.html
+++ b/contrib/expat/doc/reference.html
@@ -14,7 +14,7 @@
    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-2024 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2017-2025 Sebastian Pipping <sebastian@pipping.org>
    Copyright (c) 2017      Jakub Wilk <jwilk@jwilk.net>
    Copyright (c) 2021      Tomas Korbar <tkorbar@redhat.com>
    Copyright (c) 2021      Nicolas Cavallari <nicolas.cavallari@green-communications.fr>
@@ -52,7 +52,7 @@
   <div>
     <h1>
       The Expat XML Parser
-      <small>Release 2.6.4</small>
+      <small>Release 2.7.1</small>
     </h1>
   </div>
 <div class="content">
@@ -1267,6 +1267,11 @@ call-backs, except when parsing an external parameter entity and
 <code>XML_STATUS_ERROR</code> otherwise.  The possible error codes
 are:</p>
 <dl>
+  <dt><code>XML_ERROR_NOT_STARTED</code></dt>
+  <dd>
+    when stopping or suspending a parser before it has started,
+    added in Expat 2.6.4.
+  </dd>
   <dt><code>XML_ERROR_SUSPENDED</code></dt>
   <dd>when suspending an already suspended parser.</dd>
   <dt><code>XML_ERROR_FINISHED</code></dt>
diff --git a/contrib/expat/doc/xmlwf.1 b/contrib/expat/doc/xmlwf.1
index 61b302581ce9..76aa7e30d074 100644
--- a/contrib/expat/doc/xmlwf.1
+++ b/contrib/expat/doc/xmlwf.1
@@ -5,7 +5,7 @@
 \\$2 \(la\\$1\(ra\\$3
 ..
 .if \n(.g .mso www.tmac
-.TH XMLWF 1 "November 6, 2024" "" ""
+.TH XMLWF 1 "March 27, 2025" "" ""
 .SH NAME
 xmlwf \- Determines if an XML document is well-formed
 .SH SYNOPSIS
diff --git a/contrib/expat/doc/xmlwf.xml b/contrib/expat/doc/xmlwf.xml
index cf6d984af463..17e9cf51c191 100644
--- a/contrib/expat/doc/xmlwf.xml
+++ b/contrib/expat/doc/xmlwf.xml
@@ -9,7 +9,7 @@
    Copyright (c) 2001      Scott Bronson <bronson@rinspin.com>
    Copyright (c) 2002-2003 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
    Copyright (c) 2009      Karl Waclawek <karl@waclawek.net>
-   Copyright (c) 2016-2024 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2016-2025 Sebastian Pipping <sebastian@pipping.org>
    Copyright (c) 2016      Ardo van Rangelrooij <ardo@debian.org>
    Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
    Copyright (c) 2020      Joe Orton <jorton@redhat.com>
@@ -21,7 +21,7 @@
           "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
   <!ENTITY dhfirstname "<firstname>Scott</firstname>">
   <!ENTITY dhsurname   "<surname>Bronson</surname>">
-  <!ENTITY dhdate      "<date>November 6, 2024</date>">
+  <!ENTITY dhdate      "<date>March 27, 2025</date>">
   <!-- Please adjust this^^ date whenever cutting a new release. -->
   <!ENTITY dhsection   "<manvolnum>1</manvolnum>">
   <!ENTITY dhemail     "<email>bronson@rinspin.com</email>">
diff --git a/contrib/expat/fuzz/xml_lpm_fuzzer.cpp b/contrib/expat/fuzz/xml_lpm_fuzzer.cpp
new file mode 100644
index 000000000000..f52ea7b21e40
--- /dev/null
+++ b/contrib/expat/fuzz/xml_lpm_fuzzer.cpp
@@ -0,0 +1,464 @@
+/*
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2022 Mark Brand <markbrand@google.com>
+   Copyright (c) 2025 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.
+*/
+
+#if defined(NDEBUG)
+#  undef NDEBUG // because checks below rely on assert(...)
+#endif
+
+#include <assert.h>
+#include <stdint.h>
+#include <vector>
+
+#include "expat.h"
+#include "xml_lpm_fuzzer.pb.h"
+#include "src/libfuzzer/libfuzzer_macro.h"
+
+static const char *g_encoding = nullptr;
+static const char *g_external_entity = nullptr;
+static size_t g_external_entity_size = 0;
+
+void
+SetEncoding(const xml_lpm_fuzzer::Encoding &e) {
+  switch (e) {
+  case xml_lpm_fuzzer::Encoding::UTF8:
+    g_encoding = "UTF-8";
+    break;
+
+  case xml_lpm_fuzzer::Encoding::UTF16:
+    g_encoding = "UTF-16";
+    break;
+
+  case xml_lpm_fuzzer::Encoding::ISO88591:
+    g_encoding = "ISO-8859-1";
+    break;
+
+  case xml_lpm_fuzzer::Encoding::ASCII:
+    g_encoding = "US-ASCII";
+    break;
+
+  case xml_lpm_fuzzer::Encoding::NONE:
+    g_encoding = NULL;
+    break;
+
+  default:
+    g_encoding = "UNKNOWN";
+    break;
+  }
+}
+
+static int g_allocation_count = 0;
+static std::vector<int> g_fail_allocations = {};
+
+void *
+MallocHook(size_t size) {
+  g_allocation_count += 1;
+  for (auto index : g_fail_allocations) {
+    if (index == g_allocation_count) {
+      return NULL;
+    }
+  }
+  return malloc(size);
+}
+
+void *
+ReallocHook(void *ptr, size_t size) {
+  g_allocation_count += 1;
+  for (auto index : g_fail_allocations) {
+    if (index == g_allocation_count) {
+      return NULL;
+    }
+  }
+  return realloc(ptr, size);
+}
+
+void
+FreeHook(void *ptr) {
+  free(ptr);
+}
+
+XML_Memory_Handling_Suite memory_handling_suite
+    = {MallocHook, ReallocHook, FreeHook};
+
+void InitializeParser(XML_Parser parser);
+
+// We want a parse function that supports resumption, so that we can cover the
+// suspend/resume code.
+enum XML_Status
+Parse(XML_Parser parser, const char *input, int input_len, int is_final) {
+  enum XML_Status status = XML_Parse(parser, input, input_len, is_final);
+  while (status == XML_STATUS_SUSPENDED) {
+    status = XML_ResumeParser(parser);
+  }
+  return status;
+}
+
+// When the fuzzer is compiled with instrumentation such as ASan, then the
+// accesses in TouchString will fault if they access invalid memory (ie. detect
+// either a use-after-free or buffer-overflow). By calling TouchString in each
+// of the callbacks, we can check that the arguments meet the API specifications
+// in terms of length/null-termination. no_optimize is used to ensure that the
+// compiler has to emit actual memory reads, instead of removing them.
+static volatile size_t no_optimize = 0;
+static void
+TouchString(const XML_Char *ptr, int len = -1) {
+  if (! ptr) {
+    return;
+  }
+
+  if (len == -1) {
+    for (XML_Char value = *ptr++; value; value = *ptr++) {
+      no_optimize += value;
+    }
+  } else {
+    for (int i = 0; i < len; ++i) {
+      no_optimize += ptr[i];
+    }
+  }
+}
+
+static void
+TouchNodeAndRecurse(XML_Content *content) {
+  switch (content->type) {
+  case XML_CTYPE_EMPTY:
+  case XML_CTYPE_ANY:
+    assert(content->quant == XML_CQUANT_NONE);
+    assert(content->name == NULL);
+    assert(content->numchildren == 0);
+    assert(content->children == NULL);
+    break;
+
+  case XML_CTYPE_MIXED:
+    assert(content->quant == XML_CQUANT_NONE
+           || content->quant == XML_CQUANT_REP);
+    assert(content->name == NULL);
+    for (unsigned int i = 0; i < content->numchildren; ++i) {
+      assert(content->children[i].type == XML_CTYPE_NAME);
+      assert(content->children[i].quant == XML_CQUANT_NONE);
+      assert(content->children[i].numchildren == 0);
+      assert(content->children[i].children == NULL);
+      TouchString(content->children[i].name);
+    }
+    break;
+
+  case XML_CTYPE_NAME:
+    assert((content->quant == XML_CQUANT_NONE)
+           || (content->quant == XML_CQUANT_OPT)
+           || (content->quant == XML_CQUANT_REP)
+           || (content->quant == XML_CQUANT_PLUS));
+    assert(content->numchildren == 0);
+    assert(content->children == NULL);
+    TouchString(content->name);
+    break;
+
+  case XML_CTYPE_CHOICE:
+  case XML_CTYPE_SEQ:
+    assert((content->quant == XML_CQUANT_NONE)
+           || (content->quant == XML_CQUANT_OPT)
+           || (content->quant == XML_CQUANT_REP)
+           || (content->quant == XML_CQUANT_PLUS));
+    assert(content->name == NULL);
+    for (unsigned int i = 0; i < content->numchildren; ++i) {
+      TouchNodeAndRecurse(&content->children[i]);
+    }
+    break;
+
+  default:
+    assert(false);
+  }
+}
+
+static void XMLCALL
+ElementDeclHandler(void *userData, const XML_Char *name, XML_Content *model) {
+  TouchString(name);
+  TouchNodeAndRecurse(model);
+  XML_FreeContentModel((XML_Parser)userData, model);
+}
+
+static void XMLCALL
+AttlistDeclHandler(void *userData, const XML_Char *elname,
+                   const XML_Char *attname, const XML_Char *atttype,
+                   const XML_Char *dflt, int isrequired) {
+  (void)userData;
+  TouchString(elname);
+  TouchString(attname);
+  TouchString(atttype);
+  TouchString(dflt);
+  (void)isrequired;
+}
+
+static void XMLCALL
+XmlDeclHandler(void *userData, const XML_Char *version,
+               const XML_Char *encoding, int standalone) {
+  (void)userData;
+  TouchString(version);
+  TouchString(encoding);
+  (void)standalone;
+}
+
+static void XMLCALL
+StartElementHandler(void *userData, const XML_Char *name,
+                    const XML_Char **atts) {
+  (void)userData;
+  TouchString(name);
+  for (size_t i = 0; atts[i] != NULL; ++i) {
+    TouchString(atts[i]);
+  }
+}
+
+static void XMLCALL
+EndElementHandler(void *userData, const XML_Char *name) {
+  (void)userData;
+  TouchString(name);
+}
+
+static void XMLCALL
+CharacterDataHandler(void *userData, const XML_Char *s, int len) {
+  (void)userData;
+  TouchString(s, len);
+}
+
+static void XMLCALL
+ProcessingInstructionHandler(void *userData, const XML_Char *target,
+                             const XML_Char *data) {
+  (void)userData;
+  TouchString(target);
+  TouchString(data);
+}
+
+static void XMLCALL
+CommentHandler(void *userData, const XML_Char *data) {
+  TouchString(data);
+  // Use the comment handler to trigger parser suspend, so that we can get
+  // coverage of that code.
+  XML_StopParser((XML_Parser)userData, XML_TRUE);
+}
+
+static void XMLCALL
+StartCdataSectionHandler(void *userData) {
+  (void)userData;
+}
+
+static void XMLCALL
+EndCdataSectionHandler(void *userData) {
+  (void)userData;
+}
+
+static void XMLCALL
+DefaultHandler(void *userData, const XML_Char *s, int len) {
+  (void)userData;
+  TouchString(s, len);
+}
+
+static void XMLCALL
+StartDoctypeDeclHandler(void *userData, const XML_Char *doctypeName,
+                        const XML_Char *sysid, const XML_Char *pubid,
+                        int has_internal_subset) {
+  (void)userData;
+  TouchString(doctypeName);
+  TouchString(sysid);
+  TouchString(pubid);
+  (void)has_internal_subset;
+}
+
+static void XMLCALL
+EndDoctypeDeclHandler(void *userData) {
+  (void)userData;
+}
+
+static void XMLCALL
+EntityDeclHandler(void *userData, const XML_Char *entityName,
+                  int is_parameter_entity, const XML_Char *value,
+                  int value_length, const XML_Char *base,
+                  const XML_Char *systemId, const XML_Char *publicId,
+                  const XML_Char *notationName) {
+  (void)userData;
+  TouchString(entityName);
+  (void)is_parameter_entity;
+  TouchString(value, value_length);
+  TouchString(base);
+  TouchString(systemId);
+  TouchString(publicId);
+  TouchString(notationName);
+}
+
+static void XMLCALL
+NotationDeclHandler(void *userData, const XML_Char *notationName,
+                    const XML_Char *base, const XML_Char *systemId,
+                    const XML_Char *publicId) {
+  (void)userData;
+  TouchString(notationName);
+  TouchString(base);
+  TouchString(systemId);
+  TouchString(publicId);
+}
+
+static void XMLCALL
+StartNamespaceDeclHandler(void *userData, const XML_Char *prefix,
+                          const XML_Char *uri) {
+  (void)userData;
+  TouchString(prefix);
+  TouchString(uri);
+}
+
+static void XMLCALL
+EndNamespaceDeclHandler(void *userData, const XML_Char *prefix) {
+  (void)userData;
+  TouchString(prefix);
+}
+
+static int XMLCALL
+NotStandaloneHandler(void *userData) {
+  (void)userData;
+  return XML_STATUS_OK;
+}
+
+static int XMLCALL
+ExternalEntityRefHandler(XML_Parser parser, const XML_Char *context,
+                         const XML_Char *base, const XML_Char *systemId,
+                         const XML_Char *publicId) {
+  int rc = XML_STATUS_ERROR;
+  TouchString(context);
+  TouchString(base);
+  TouchString(systemId);
+  TouchString(publicId);
+
+  if (g_external_entity) {
+    XML_Parser ext_parser
+        = XML_ExternalEntityParserCreate(parser, context, g_encoding);
+    rc = Parse(ext_parser, g_external_entity, g_external_entity_size, 1);
+    XML_ParserFree(ext_parser);
+  }
+
+  return rc;
+}
+
+static void XMLCALL
+SkippedEntityHandler(void *userData, const XML_Char *entityName,
+                     int is_parameter_entity) {
+  (void)userData;
+  TouchString(entityName);
+  (void)is_parameter_entity;
+}
+
+static int XMLCALL
+UnknownEncodingHandler(void *encodingHandlerData, const XML_Char *name,
+                       XML_Encoding *info) {
+  (void)encodingHandlerData;
+  TouchString(name);
+  (void)info;
+  return XML_STATUS_ERROR;
+}
+
+void
+InitializeParser(XML_Parser parser) {
+  XML_SetUserData(parser, (void *)parser);
+  XML_SetHashSalt(parser, 0x41414141);
+  XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+
+  XML_SetElementDeclHandler(parser, ElementDeclHandler);
+  XML_SetAttlistDeclHandler(parser, AttlistDeclHandler);
+  XML_SetXmlDeclHandler(parser, XmlDeclHandler);
+  XML_SetElementHandler(parser, StartElementHandler, EndElementHandler);
+  XML_SetCharacterDataHandler(parser, CharacterDataHandler);
+  XML_SetProcessingInstructionHandler(parser, ProcessingInstructionHandler);
+  XML_SetCommentHandler(parser, CommentHandler);
+  XML_SetCdataSectionHandler(parser, StartCdataSectionHandler,
+                             EndCdataSectionHandler);
+  // XML_SetDefaultHandler disables entity expansion
+  XML_SetDefaultHandlerExpand(parser, DefaultHandler);
+  XML_SetDoctypeDeclHandler(parser, StartDoctypeDeclHandler,
+                            EndDoctypeDeclHandler);
+  // Note: This is mutually exclusive with XML_SetUnparsedEntityDeclHandler,
+  //       and there isn't any significant code change between the two.
+  XML_SetEntityDeclHandler(parser, EntityDeclHandler);
+  XML_SetNotationDeclHandler(parser, NotationDeclHandler);
+  XML_SetNamespaceDeclHandler(parser, StartNamespaceDeclHandler,
+                              EndNamespaceDeclHandler);
+  XML_SetNotStandaloneHandler(parser, NotStandaloneHandler);
+  XML_SetExternalEntityRefHandler(parser, ExternalEntityRefHandler);
+  XML_SetSkippedEntityHandler(parser, SkippedEntityHandler);
+  XML_SetUnknownEncodingHandler(parser, UnknownEncodingHandler, (void *)parser);
+}
+
+DEFINE_TEXT_PROTO_FUZZER(const xml_lpm_fuzzer::Testcase &testcase) {
+  g_external_entity = nullptr;
+
+  if (! testcase.actions_size()) {
+    return;
+  }
+
+  g_allocation_count = 0;
+  g_fail_allocations.clear();
+  for (int i = 0; i < testcase.fail_allocations_size(); ++i) {
+    g_fail_allocations.push_back(testcase.fail_allocations(i));
+  }
+
+  SetEncoding(testcase.encoding());
+  XML_Parser parser
+      = XML_ParserCreate_MM(g_encoding, &memory_handling_suite, "|");
+  InitializeParser(parser);
+
+  for (int i = 0; i < testcase.actions_size(); ++i) {
+    const auto &action = testcase.actions(i);
+    switch (action.action_case()) {
+    case xml_lpm_fuzzer::Action::kChunk:
+      if (XML_STATUS_ERROR
+          == Parse(parser, action.chunk().data(), action.chunk().size(), 0)) {
+        // Force a reset after parse error.
+        XML_ParserReset(parser, g_encoding);
+        InitializeParser(parser);
+      }
+      break;
+
+    case xml_lpm_fuzzer::Action::kLastChunk:
+      Parse(parser, action.last_chunk().data(), action.last_chunk().size(), 1);
+      XML_ParserReset(parser, g_encoding);
+      InitializeParser(parser);
+      break;
+
+    case xml_lpm_fuzzer::Action::kReset:
+      XML_ParserReset(parser, g_encoding);
+      InitializeParser(parser);
+      break;
+
+    case xml_lpm_fuzzer::Action::kExternalEntity:
+      g_external_entity = action.external_entity().data();
+      g_external_entity_size = action.external_entity().size();
+      break;
+
+    default:
+      break;
+    }
+  }
+
+  XML_ParserFree(parser);
+}
diff --git a/contrib/expat/fuzz/xml_lpm_fuzzer.proto b/contrib/expat/fuzz/xml_lpm_fuzzer.proto
new file mode 100644
index 000000000000..ddc4e958b919
--- /dev/null
+++ b/contrib/expat/fuzz/xml_lpm_fuzzer.proto
@@ -0,0 +1,58 @@
+/*
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 2022 Mark Brand <markbrand@google.com>
+   Copyright (c) 2025 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.
+*/
+
+syntax = "proto2";
+package xml_lpm_fuzzer;
+
+enum Encoding {
+  UTF8 = 0;
+  UTF16 = 1;
+  ISO88591 = 2;
+  ASCII = 3;
+  UNKNOWN = 4;
+  NONE = 5;
+}
+
+message Action {
+  oneof action {
+    string chunk = 1;
+    string last_chunk = 2;
+    bool reset = 3;
+    string external_entity = 4;
+  }
+}
+
+message Testcase {
+  required Encoding encoding = 1;
+  repeated Action actions = 2;
+  repeated int32 fail_allocations = 3;
+}
diff --git a/contrib/expat/fuzz/xml_parse_fuzzer.c b/contrib/expat/fuzz/xml_parse_fuzzer.c
index a7e8414ce355..6a1affe2b1f6 100644
--- a/contrib/expat/fuzz/xml_parse_fuzzer.c
+++ b/contrib/expat/fuzz/xml_parse_fuzzer.c
@@ -5,7 +5,7 @@
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ *      https://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/contrib/expat/fuzz/xml_parsebuffer_fuzzer.c b/contrib/expat/fuzz/xml_parsebuffer_fuzzer.c
index 0327aa9f952e..cfc4af202851 100644
--- a/contrib/expat/fuzz/xml_parsebuffer_fuzzer.c
*** 2316 LINES SKIPPED ***