git: 78ab4c182b9f - stable/14 - MFC: MFV: expat 2.6.4.

From: Xin LI <delphij_at_FreeBSD.org>
Date: Sun, 22 Dec 2024 07:45:52 UTC
The branch stable/14 has been updated by delphij:

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

commit 78ab4c182b9faa09688a040b252ba111952e5e17
Author:     Xin LI <delphij@FreeBSD.org>
AuthorDate: 2024-12-09 04:24:16 +0000
Commit:     Xin LI <delphij@FreeBSD.org>
CommitDate: 2024-12-22 07:45:34 +0000

    MFC: MFV: expat 2.6.4.
    
    (cherry picked from commit 908f215e80fa482aa953c39afa6bb516f561fc00)
---
 contrib/expat/Changes                         | 31 ++++++++++++++++++
 contrib/expat/README.md                       | 47 ++++++++++++++++++++++++---
 contrib/expat/configure.ac                    |  6 ++--
 contrib/expat/doc/reference.html              |  2 +-
 contrib/expat/doc/xmlwf.1                     |  2 +-
 contrib/expat/doc/xmlwf.xml                   |  2 +-
 contrib/expat/examples/element_declarations.c |  9 ++---
 contrib/expat/lib/expat.h                     |  6 ++--
 contrib/expat/lib/xmlparse.c                  | 18 +++++++---
 contrib/expat/tests/basic_tests.c             | 17 +++++++---
 contrib/expat/tests/common.c                  | 33 ++-----------------
 contrib/expat/tests/common.h                  | 11 +------
 contrib/expat/tests/handlers.c                | 42 +++++++++++++++++++-----
 contrib/expat/tests/handlers.h                | 17 ++++++++--
 contrib/expat/tests/misc_tests.c              | 39 ++++++++++++++++++----
 contrib/expat/xmlwf/xmlfile.c                 |  4 ++-
 lib/libexpat/Makefile                         |  1 -
 lib/libexpat/expat_config.h                   |  9 ++---
 lib/libexpat/libbsdxml.3                      |  4 +--
 19 files changed, 206 insertions(+), 94 deletions(-)

diff --git a/contrib/expat/Changes b/contrib/expat/Changes
index c1d22efa5a3c..aa19f70ae219 100644
--- a/contrib/expat/Changes
+++ b/contrib/expat/Changes
@@ -30,6 +30,37 @@
 !! THANK YOU!                        Sebastian Pipping -- Berlin, 2024-03-09 !!
 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
+Release 2.6.4 Wed November 6 2024
+        Security fixes:
+            #915  CVE-2024-50602 -- Fix crash within function XML_ResumeParser
+                    from a NULL pointer dereference by disallowing function
+                    XML_StopParser to (stop or) suspend an unstarted parser.
+                    A new error code XML_ERROR_NOT_STARTED was introduced to
+                    properly communicate this situation.  // CWE-476 CWE-754
+
+        Other changes:
+            #903  CMake: Add alias target "expat::expat"
+            #905  docs: Document use via CMake >=3.18 with FetchContent
+                    and SOURCE_SUBDIR and its consequences
+            #902  tests: Reduce use of global parser instance
+            #904  tests: Resolve duplicate handler
+       #317 #918  tests: Improve tests on doctype closing (ex CVE-2019-15903)
+            #914  Fix signedness of format strings
+       #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
+
+        Infrastructure:
+            #907  CI: Upgrade Clang from 18 to 19
+            #913  CI: Drop macos-12 and add macos-15
+            #910  CI: Adapt to breaking changes in GitHub Actions
+            #898  Add missing entries to .gitignore
+
+        Special thanks to:
+            Hanno Böck
+            José Eduardo Gutiérrez Conejo
+            José Ricardo Cardona Quesada
+
 Release 2.6.3 Wed September 4 2024
         Security fixes:
        #887 #890  CVE-2024-45490 -- Calling function XML_ParseBuffer with
diff --git a/contrib/expat/README.md b/contrib/expat/README.md
index 180a68e4abbe..23d26dad2b92 100644
--- a/contrib/expat/README.md
+++ b/contrib/expat/README.md
@@ -11,7 +11,7 @@
 > at the top of the `Changes` file.
 
 
-# Expat, Release 2.6.3
+# Expat, Release 2.6.4
 
 This is Expat, a C99 library for parsing
 [XML 1.0 Fourth Edition](https://www.w3.org/TR/2006/REC-xml-20060816/), started by
@@ -43,9 +43,9 @@ This license is the same as the MIT/X Consortium license.
 
 ## Using libexpat in your CMake-Based Project
 
-There are two ways of using libexpat with CMake:
+There are three documented ways of using libexpat with CMake:
 
-### a) Module Mode
+### a) `find_package` with Module Mode
 
 This approach leverages CMake's own [module `FindEXPAT`](https://cmake.org/cmake/help/latest/module/FindEXPAT.html).
 
@@ -70,7 +70,7 @@ target_include_directories(hello PRIVATE ${EXPAT_INCLUDE_DIRS})
 target_link_libraries(hello PUBLIC ${EXPAT_LIBRARIES})
 ```
 
-### b) Config Mode
+### b) `find_package` with Config Mode
 
 This approach requires files from…
 
@@ -98,6 +98,45 @@ add_executable(hello
 target_link_libraries(hello PUBLIC expat::expat)
 ```
 
+### c) The `FetchContent` module
+
+This approach — as demonstrated below — requires CMake >=3.18 for both the
+[`FetchContent` module](https://cmake.org/cmake/help/latest/module/FetchContent.html)
+and its support for the `SOURCE_SUBDIR` option to be available.
+
+Please note that:
+- Use of the `FetchContent` module with *non-release* SHA1s or `master`
+  of libexpat is neither advised nor considered officially supported.
+- Pinning to a specific commit is great for robust CI.
+- Pinning to a specific commit needs updating every time there is a new
+  release of libexpat — either manually or through automation —,
+  to not miss out on libexpat security updates.
+
+For an example that pulls in libexpat via Git:
+
+```cmake
+cmake_minimum_required(VERSION 3.18)
+
+include(FetchContent)
+
+project(hello VERSION 1.0.0)
+
+FetchContent_Declare(
+    expat
+    GIT_REPOSITORY https://github.com/libexpat/libexpat/
+    GIT_TAG        000000000_GIT_COMMIT_SHA1_HERE_000000000  # i.e. Git tag R_0_Y_Z
+    SOURCE_SUBDIR  expat/
+)
+
+FetchContent_MakeAvailable(expat)
+
+add_executable(hello
+    hello.c
+)
+
+target_link_libraries(hello PUBLIC expat)
+```
+
 
 ## Building from a Git Clone
 
diff --git a/contrib/expat/configure.ac b/contrib/expat/configure.ac
index 1a930413ffe5..fffcd125e9c4 100644
--- a/contrib/expat/configure.ac
+++ b/contrib/expat/configure.ac
@@ -84,9 +84,9 @@ dnl
 dnl If the API changes incompatibly set LIBAGE back to 0
 dnl
 
-LIBCURRENT=10  # sync
-LIBREVISION=3  # with
-LIBAGE=9       # CMakeLists.txt!
+LIBCURRENT=11  # sync
+LIBREVISION=0  # with
+LIBAGE=10      # CMakeLists.txt!
 
 AC_CONFIG_HEADERS([expat_config.h])
 AH_TOP([#ifndef EXPAT_CONFIG_H
diff --git a/contrib/expat/doc/reference.html b/contrib/expat/doc/reference.html
index 4cfb2ce9384e..c2ae9bb71431 100644
--- a/contrib/expat/doc/reference.html
+++ b/contrib/expat/doc/reference.html
@@ -52,7 +52,7 @@
   <div>
     <h1>
       The Expat XML Parser
-      <small>Release 2.6.3</small>
+      <small>Release 2.6.4</small>
     </h1>
   </div>
 <div class="content">
diff --git a/contrib/expat/doc/xmlwf.1 b/contrib/expat/doc/xmlwf.1
index 347c36f06109..61b302581ce9 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 "September 4, 2024" "" ""
+.TH XMLWF 1 "November 6, 2024" "" ""
 .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 10b29782b197..cf6d984af463 100644
--- a/contrib/expat/doc/xmlwf.xml
+++ b/contrib/expat/doc/xmlwf.xml
@@ -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>September 4, 2024</date>">
+  <!ENTITY dhdate      "<date>November 6, 2024</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/examples/element_declarations.c b/contrib/expat/examples/element_declarations.c
index 7ce8544f6f04..d644b2ffa5ea 100644
--- a/contrib/expat/examples/element_declarations.c
+++ b/contrib/expat/examples/element_declarations.c
@@ -15,6 +15,7 @@
    Copyright (c) 2016-2024 Sebastian Pipping <sebastian@pipping.org>
    Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
    Copyright (c) 2019      Zhongyuan Zhou <zhouzhongyuan@huawei.com>
+   Copyright (c) 2024      Hanno Böck <hanno@gentoo.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -127,15 +128,15 @@ dumpContentModelElement(const XML_Content *model, unsigned level,
   }
 
   // Node
-  printf("[%u] type=%s(%d), quant=%s(%d)", (unsigned)(model - root),
-         contentTypeName(model->type), model->type,
-         contentQuantName(model->quant), model->quant);
+  printf("[%u] type=%s(%u), quant=%s(%u)", (unsigned)(model - root),
+         contentTypeName(model->type), (unsigned int)model->type,
+         contentQuantName(model->quant), (unsigned int)model->quant);
   if (model->name) {
     printf(", name=\"%" XML_FMT_STR "\"", model->name);
   } else {
     printf(", name=NULL");
   }
-  printf(", numchildren=%d", model->numchildren);
+  printf(", numchildren=%u", model->numchildren);
   printf("\n");
 }
 
diff --git a/contrib/expat/lib/expat.h b/contrib/expat/lib/expat.h
index d0d6015a6628..523b37d8d578 100644
--- a/contrib/expat/lib/expat.h
+++ b/contrib/expat/lib/expat.h
@@ -130,7 +130,9 @@ enum XML_Error {
   /* Added in 2.3.0. */
   XML_ERROR_NO_BUFFER,
   /* Added in 2.4.0. */
-  XML_ERROR_AMPLIFICATION_LIMIT_BREACH
+  XML_ERROR_AMPLIFICATION_LIMIT_BREACH,
+  /* Added in 2.6.4. */
+  XML_ERROR_NOT_STARTED,
 };
 
 enum XML_Content_Type {
@@ -1066,7 +1068,7 @@ XML_SetReparseDeferralEnabled(XML_Parser parser, XML_Bool enabled);
 */
 #define XML_MAJOR_VERSION 2
 #define XML_MINOR_VERSION 6
-#define XML_MICRO_VERSION 3
+#define XML_MICRO_VERSION 4
 
 #ifdef __cplusplus
 }
diff --git a/contrib/expat/lib/xmlparse.c b/contrib/expat/lib/xmlparse.c
index d9285b213b38..a4e091e7c33c 100644
--- a/contrib/expat/lib/xmlparse.c
+++ b/contrib/expat/lib/xmlparse.c
@@ -1,4 +1,4 @@
-/* ba4cdf9bdb534f355a9def4c9e25d20ee8e72f95b0a4d930be52e563f5080196 (2.6.3+)
+/* c5625880f4bf417c1463deee4eb92d86ff413f802048621c57e25fe483eb59e4 (2.6.4+)
                             __  __            _
                          ___\ \/ /_ __   __ _| |_
                         / _ \\  /| '_ \ / _` | __|
@@ -40,6 +40,7 @@
    Copyright (c) 2023      Owain Davies <owaind@bath.edu>
    Copyright (c) 2023-2024 Sony Corporation / Snild Dolkow <snild@sony.com>
    Copyright (c) 2024      Berkay Eren Ürün <berkay.ueruen@siemens.com>
+   Copyright (c) 2024      Hanno Böck <hanno@gentoo.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -2234,6 +2235,9 @@ XML_StopParser(XML_Parser parser, XML_Bool resumable) {
   if (parser == NULL)
     return XML_STATUS_ERROR;
   switch (parser->m_parsingStatus.parsing) {
+  case XML_INITIALIZED:
+    parser->m_errorCode = XML_ERROR_NOT_STARTED;
+    return XML_STATUS_ERROR;
   case XML_SUSPENDED:
     if (resumable) {
       parser->m_errorCode = XML_ERROR_SUSPENDED;
@@ -2244,7 +2248,7 @@ XML_StopParser(XML_Parser parser, XML_Bool resumable) {
   case XML_FINISHED:
     parser->m_errorCode = XML_ERROR_FINISHED;
     return XML_STATUS_ERROR;
-  default:
+  case XML_PARSING:
     if (resumable) {
 #ifdef XML_DTD
       if (parser->m_isParamEntity) {
@@ -2255,6 +2259,9 @@ XML_StopParser(XML_Parser parser, XML_Bool resumable) {
       parser->m_parsingStatus.parsing = XML_SUSPENDED;
     } else
       parser->m_parsingStatus.parsing = XML_FINISHED;
+    break;
+  default:
+    assert(0);
   }
   return XML_STATUS_OK;
 }
@@ -2519,6 +2526,9 @@ XML_ErrorString(enum XML_Error code) {
   case XML_ERROR_AMPLIFICATION_LIMIT_BREACH:
     return XML_L(
         "limit on input amplification factor (from DTD and entities) breached");
+  /* Added in 2.6.4. */
+  case XML_ERROR_NOT_STARTED:
+    return XML_L("parser not started");
   }
   return NULL;
 }
@@ -7856,7 +7866,7 @@ accountingReportDiff(XML_Parser rootParser,
   assert(! rootParser->m_parentParser);
 
   fprintf(stderr,
-          " (+" EXPAT_FMT_PTRDIFF_T("6") " bytes %s|%d, xmlparse.c:%d) %*s\"",
+          " (+" EXPAT_FMT_PTRDIFF_T("6") " bytes %s|%u, xmlparse.c:%d) %*s\"",
           bytesMore, (account == XML_ACCOUNT_DIRECT) ? "DIR" : "EXP",
           levelsAwayFromRootParser, source_line, 10, "");
 
@@ -7969,7 +7979,7 @@ entityTrackingReportStats(XML_Parser rootParser, ENTITY *entity,
 
   fprintf(
       stderr,
-      "expat: Entities(%p): Count %9d, depth %2d/%2d %*s%s%s; %s length %d (xmlparse.c:%d)\n",
+      "expat: Entities(%p): Count %9u, depth %2u/%2u %*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,
diff --git a/contrib/expat/tests/basic_tests.c b/contrib/expat/tests/basic_tests.c
index 0d97b1090c7f..d38b8fd18416 100644
--- a/contrib/expat/tests/basic_tests.c
+++ b/contrib/expat/tests/basic_tests.c
@@ -2357,11 +2357,20 @@ START_TEST(test_attributes) {
   info[0].attributes = doc_info;
   info[1].attributes = tag_info;
 
-  XML_SetStartElementHandler(g_parser, counting_start_element_handler);
-  XML_SetUserData(g_parser, info);
-  if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
+  XML_Parser parser = XML_ParserCreate(NULL);
+  assert_true(parser != NULL);
+  ParserAndElementInfo parserAndElementInfos = {
+      parser,
+      info,
+  };
+
+  XML_SetStartElementHandler(parser, counting_start_element_handler);
+  XML_SetUserData(parser, &parserAndElementInfos);
+  if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE)
       == XML_STATUS_ERROR)
-    xml_failure(g_parser);
+    xml_failure(parser);
+
+  XML_ParserFree(parser);
 }
 END_TEST
 
diff --git a/contrib/expat/tests/common.c b/contrib/expat/tests/common.c
index 26d0c5473a63..3aea8d74d1ee 100644
--- a/contrib/expat/tests/common.c
+++ b/contrib/expat/tests/common.c
@@ -10,7 +10,7 @@
    Copyright (c) 2003      Greg Stein <gstein@users.sourceforge.net>
    Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
    Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net>
-   Copyright (c) 2016-2023 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2016-2024 Sebastian Pipping <sebastian@pipping.org>
    Copyright (c) 2017-2022 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>
@@ -51,6 +51,7 @@
 #include "chardata.h"
 #include "minicheck.h"
 #include "common.h"
+#include "handlers.h"
 
 /* Common test data */
 
@@ -221,30 +222,6 @@ _expect_failure(const char *text, enum XML_Error errorCode,
     _xml_failure(g_parser, file, lineno);
 }
 
-/* Character data support for handlers, built on top of the code in
- * chardata.c
- */
-void XMLCALL
-accumulate_characters(void *userData, const XML_Char *s, int len) {
-  CharData_AppendXMLChars((CharData *)userData, s, len);
-}
-
-void XMLCALL
-accumulate_attribute(void *userData, const XML_Char *name,
-                     const XML_Char **atts) {
-  CharData *storage = (CharData *)userData;
-  UNUSED_P(name);
-  /* Check there are attributes to deal with */
-  if (atts == NULL)
-    return;
-
-  while (storage->count < 0 && atts[0] != NULL) {
-    /* "accumulate" the value of the first attribute we see */
-    CharData_AppendXMLChars(storage, atts[1], -1);
-    atts += 2;
-  }
-}
-
 void
 _run_character_check(const char *text, const XML_Char *expected,
                      const char *file, int line) {
@@ -273,12 +250,6 @@ _run_attribute_check(const char *text, const XML_Char *expected,
   CharData_CheckXMLChars(&storage, expected);
 }
 
-void XMLCALL
-ext_accumulate_characters(void *userData, const XML_Char *s, int len) {
-  ExtTest *test_data = (ExtTest *)userData;
-  accumulate_characters(test_data->storage, s, len);
-}
-
 void
 _run_ext_character_check(const char *text, ExtTest *test_data,
                          const XML_Char *expected, const char *file, int line) {
diff --git a/contrib/expat/tests/common.h b/contrib/expat/tests/common.h
index 52f00cc0eeb0..bc4c7da68071 100644
--- a/contrib/expat/tests/common.h
+++ b/contrib/expat/tests/common.h
@@ -10,7 +10,7 @@
    Copyright (c) 2003      Greg Stein <gstein@users.sourceforge.net>
    Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
    Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net>
-   Copyright (c) 2016-2023 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2016-2024 Sebastian Pipping <sebastian@pipping.org>
    Copyright (c) 2017-2022 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>
@@ -111,12 +111,6 @@ extern void _expect_failure(const char *text, enum XML_Error errorCode,
 /* Support functions for handlers to collect up character and attribute data.
  */
 
-extern void XMLCALL accumulate_characters(void *userData, const XML_Char *s,
-                                          int len);
-
-extern void XMLCALL accumulate_attribute(void *userData, const XML_Char *name,
-                                         const XML_Char **atts);
-
 extern void _run_character_check(const char *text, const XML_Char *expected,
                                  const char *file, int line);
 
@@ -135,9 +129,6 @@ typedef struct ExtTest {
   CharData *storage;
 } ExtTest;
 
-extern void XMLCALL ext_accumulate_characters(void *userData, const XML_Char *s,
-                                              int len);
-
 extern void _run_ext_character_check(const char *text, ExtTest *test_data,
                                      const XML_Char *expected, const char *file,
                                      int line);
diff --git a/contrib/expat/tests/handlers.c b/contrib/expat/tests/handlers.c
index 449ada70f9a2..0211985fe95c 100644
--- a/contrib/expat/tests/handlers.c
+++ b/contrib/expat/tests/handlers.c
@@ -103,7 +103,9 @@ end_element_event_handler2(void *userData, const XML_Char *name) {
 void XMLCALL
 counting_start_element_handler(void *userData, const XML_Char *name,
                                const XML_Char **atts) {
-  ElementInfo *info = (ElementInfo *)userData;
+  ParserAndElementInfo *const parserAndElementInfos
+      = (ParserAndElementInfo *)userData;
+  ElementInfo *info = parserAndElementInfos->info;
   AttrInfo *attr;
   int count, id, i;
 
@@ -120,12 +122,12 @@ counting_start_element_handler(void *userData, const XML_Char *name,
    * is possibly a little unexpected, but it is what the
    * documentation in expat.h tells us to expect.
    */
-  count = XML_GetSpecifiedAttributeCount(g_parser);
+  count = XML_GetSpecifiedAttributeCount(parserAndElementInfos->parser);
   if (info->attr_count * 2 != count) {
     fail("Not got expected attribute count");
     return;
   }
-  id = XML_GetIdAttributeIndex(g_parser);
+  id = XML_GetIdAttributeIndex(parserAndElementInfos->parser);
   if (id == -1 && info->id_name != NULL) {
     fail("ID not present");
     return;
@@ -1880,12 +1882,6 @@ accumulate_entity_decl(void *userData, const XML_Char *entityName,
   CharData_AppendXMLChars(storage, XCS("\n"), 1);
 }
 
-void XMLCALL
-accumulate_char_data(void *userData, const XML_Char *s, int len) {
-  CharData *const storage = (CharData *)userData;
-  CharData_AppendXMLChars(storage, s, len);
-}
-
 void XMLCALL
 accumulate_start_element(void *userData, const XML_Char *name,
                          const XML_Char **atts) {
@@ -1910,6 +1906,34 @@ accumulate_start_element(void *userData, const XML_Char *name,
   CharData_AppendXMLChars(storage, XCS(")\n"), 2);
 }
 
+void XMLCALL
+accumulate_characters(void *userData, const XML_Char *s, int len) {
+  CharData *const storage = (CharData *)userData;
+  CharData_AppendXMLChars(storage, s, len);
+}
+
+void XMLCALL
+accumulate_attribute(void *userData, const XML_Char *name,
+                     const XML_Char **atts) {
+  CharData *const storage = (CharData *)userData;
+  UNUSED_P(name);
+  /* Check there are attributes to deal with */
+  if (atts == NULL)
+    return;
+
+  while (storage->count < 0 && atts[0] != NULL) {
+    /* "accumulate" the value of the first attribute we see */
+    CharData_AppendXMLChars(storage, atts[1], -1);
+    atts += 2;
+  }
+}
+
+void XMLCALL
+ext_accumulate_characters(void *userData, const XML_Char *s, int len) {
+  ExtTest *const test_data = (ExtTest *)userData;
+  accumulate_characters(test_data->storage, s, len);
+}
+
 void XMLCALL
 checking_default_handler(void *userData, const XML_Char *s, int len) {
   DefaultCheck *data = (DefaultCheck *)userData;
diff --git a/contrib/expat/tests/handlers.h b/contrib/expat/tests/handlers.h
index e1f0995f79e6..8850bb948da3 100644
--- a/contrib/expat/tests/handlers.h
+++ b/contrib/expat/tests/handlers.h
@@ -92,6 +92,11 @@ typedef struct elementInfo {
   AttrInfo *attributes;
 } ElementInfo;
 
+typedef struct StructParserAndElementInfo {
+  XML_Parser parser;
+  ElementInfo *info;
+} ParserAndElementInfo;
+
 extern void XMLCALL counting_start_element_handler(void *userData,
                                                    const XML_Char *name,
                                                    const XML_Char **atts);
@@ -564,13 +569,19 @@ extern void XMLCALL accumulate_entity_decl(
     const XML_Char *systemId, const XML_Char *publicId,
     const XML_Char *notationName);
 
-extern void XMLCALL accumulate_char_data(void *userData, const XML_Char *s,
-                                         int len);
-
 extern void XMLCALL accumulate_start_element(void *userData,
                                              const XML_Char *name,
                                              const XML_Char **atts);
 
+extern void XMLCALL accumulate_characters(void *userData, const XML_Char *s,
+                                          int len);
+
+extern void XMLCALL accumulate_attribute(void *userData, const XML_Char *name,
+                                         const XML_Char **atts);
+
+extern void XMLCALL ext_accumulate_characters(void *userData, const XML_Char *s,
+                                              int len);
+
 typedef struct default_check {
   const XML_Char *expected;
   const int expectedLen;
diff --git a/contrib/expat/tests/misc_tests.c b/contrib/expat/tests/misc_tests.c
index 2ee9320b1392..9afe0922d6b2 100644
--- a/contrib/expat/tests/misc_tests.c
+++ b/contrib/expat/tests/misc_tests.c
@@ -208,7 +208,7 @@ START_TEST(test_misc_version) {
   if (! versions_equal(&read_version, &parsed_version))
     fail("Version mismatch");
 
-  if (xcstrcmp(version_text, XCS("expat_2.6.3"))) /* needs bump on releases */
+  if (xcstrcmp(version_text, XCS("expat_2.6.4"))) /* needs bump on releases */
     fail("XML_*_VERSION in expat.h out of sync?\n");
 }
 END_TEST
@@ -332,14 +332,15 @@ START_TEST(test_misc_deny_internal_entity_closing_doctype_issue_317) {
                                "<!ENTITY % e ']><d/>'>\n"
                                "\n"
                                "%e;";
-  const char *const inputTwo = "<!DOCTYPE d [\n"
-                               "<!ENTITY % e1 ']><d/>'><!ENTITY % e2 '&e1;'>\n"
-                               "\n"
-                               "%e2;";
+  const char *const inputTwo
+      = "<!DOCTYPE d [\n"
+        "<!ENTITY % e1 ']><d/>'><!ENTITY % e2 '&#37;e1;'>\n"
+        "\n"
+        "%e2;";
   const char *const inputThree = "<!DOCTYPE d [\n"
                                  "<!ENTITY % e ']><d'>\n"
                                  "\n"
-                                 "%e;";
+                                 "%e;/>";
   const char *const inputIssue317 = "<!DOCTYPE doc [\n"
                                     "<!ENTITY % foo ']>\n"
                                     "<doc>Hell<oc (#PCDATA)*>'>\n"
@@ -447,7 +448,7 @@ START_TEST(test_misc_general_entities_support) {
   XML_SetExternalEntityRefHandler(parser,
                                   external_entity_failer__if_not_xml_ge);
   XML_SetEntityDeclHandler(parser, accumulate_entity_decl);
-  XML_SetCharacterDataHandler(parser, accumulate_char_data);
+  XML_SetCharacterDataHandler(parser, accumulate_characters);
 
   if (_XML_Parse_SINGLE_BYTES(parser, doc, (int)strlen(doc), XML_TRUE)
       != XML_STATUS_OK) {
@@ -496,6 +497,28 @@ START_TEST(test_misc_char_handler_stop_without_leak) {
 }
 END_TEST
 
+START_TEST(test_misc_resumeparser_not_crashing) {
+  XML_Parser parser = XML_ParserCreate(NULL);
+  XML_GetBuffer(parser, 1);
+  XML_StopParser(parser, /*resumable=*/XML_TRUE);
+  XML_ResumeParser(parser); // could crash here, previously
+  XML_ParserFree(parser);
+}
+END_TEST
+
+START_TEST(test_misc_stopparser_rejects_unstarted_parser) {
+  const XML_Bool cases[] = {XML_TRUE, XML_FALSE};
+  for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
+    const XML_Bool resumable = cases[i];
+    XML_Parser parser = XML_ParserCreate(NULL);
+    assert_true(XML_GetErrorCode(parser) == XML_ERROR_NONE);
+    assert_true(XML_StopParser(parser, resumable) == XML_STATUS_ERROR);
+    assert_true(XML_GetErrorCode(parser) == XML_ERROR_NOT_STARTED);
+    XML_ParserFree(parser);
+  }
+}
+END_TEST
+
 void
 make_miscellaneous_test_case(Suite *s) {
   TCase *tc_misc = tcase_create("miscellaneous tests");
@@ -520,4 +543,6 @@ make_miscellaneous_test_case(Suite *s) {
                  test_misc_create_external_entity_parser_with_null_context);
   tcase_add_test(tc_misc, test_misc_general_entities_support);
   tcase_add_test(tc_misc, test_misc_char_handler_stop_without_leak);
+  tcase_add_test(tc_misc, test_misc_resumeparser_not_crashing);
+  tcase_add_test(tc_misc, test_misc_stopparser_rejects_unstarted_parser);
 }
diff --git a/contrib/expat/xmlwf/xmlfile.c b/contrib/expat/xmlwf/xmlfile.c
index 0598b86b5fb7..9c4f7f8dbadd 100644
--- a/contrib/expat/xmlwf/xmlfile.c
+++ b/contrib/expat/xmlwf/xmlfile.c
@@ -15,6 +15,7 @@
    Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
    Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
    Copyright (c) 2021      Donghee Na <donghee.na@python.org>
+   Copyright (c) 2024      Hanno Böck <hanno@gentoo.org>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -91,7 +92,8 @@ reportError(XML_Parser parser, const XML_Char *filename) {
              filename, XML_GetErrorLineNumber(parser),
              XML_GetErrorColumnNumber(parser), message);
   else
-    ftprintf(stderr, T("%s: (unknown message %d)\n"), filename, code);
+    ftprintf(stderr, T("%s: (unknown message %u)\n"), filename,
+             (unsigned int)code);
 }
 
 /* This implementation will give problems on files larger than INT_MAX. */
diff --git a/lib/libexpat/Makefile b/lib/libexpat/Makefile
index 08c8604e74c5..92ba6b671488 100644
--- a/lib/libexpat/Makefile
+++ b/lib/libexpat/Makefile
@@ -1,4 +1,3 @@
-
 PACKAGE=	runtime
 EXPAT=		${SRCTOP}/contrib/expat
 
diff --git a/lib/libexpat/expat_config.h b/lib/libexpat/expat_config.h
index 0d581a431a53..2bb52a4baa03 100644
--- a/lib/libexpat/expat_config.h
+++ b/lib/libexpat/expat_config.h
@@ -89,7 +89,7 @@
 #define PACKAGE_NAME "expat"
 
 /* Define to the full name and version of this package. */
-#define PACKAGE_STRING "expat 2.6.0"
+#define PACKAGE_STRING "expat 2.6.4"
 
 /* Define to the one symbol short name of this package. */
 #define PACKAGE_TARNAME "expat"
@@ -98,7 +98,7 @@
 #define PACKAGE_URL ""
 
 /* Define to the version of this package. */
-#define PACKAGE_VERSION "2.6.0"
+#define PACKAGE_VERSION "2.6.4"
 
 /* Define to 1 if all of the C90 standard headers exist (not just the ones
    required in a freestanding environment). This macro is provided for
@@ -106,7 +106,7 @@
 #define STDC_HEADERS 1
 
 /* Version number of package */
-#define VERSION "2.6.0"
+#define VERSION "2.6.4"
 
 /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
    significant byte first (like Motorola and SPARC, unlike Intel). */
@@ -146,7 +146,4 @@
 /* Define to `long int' if <sys/types.h> does not define. */
 /* #undef off_t */
 
-/* Define to `unsigned int' if <sys/types.h> does not define. */
-/* #undef size_t */
-
 #endif // ndef EXPAT_CONFIG_H
diff --git a/lib/libexpat/libbsdxml.3 b/lib/libexpat/libbsdxml.3
index 2ec1f71b58bb..21c07d39e5e2 100644
--- a/lib/libexpat/libbsdxml.3
+++ b/lib/libexpat/libbsdxml.3
@@ -23,7 +23,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"/
-.Dd October 1, 2024
+.Dd December 8, 2024
 .Dt LIBBSDXML 3
 .Os
 .Sh NAME
@@ -34,7 +34,7 @@
 .Sh DESCRIPTION
 The
 .Nm
-library is a verbatim copy of the eXpat XML library version 2.6.3.
+library is a verbatim copy of the eXpat XML library version 2.6.4.
 .Pp
 The
 .Nm