untrusted comment: verify with openbsd-78-base.pub
RWS3/nvFmk4SWfO4jYXiIB3VLYB15nTF47Ccz91zEyiRR53cU95qhAt9EMJ208JxsgO/z/Om7Bdqd4k1KBciKL0iRlO2nTI4GQM=

OpenBSD 7.8 errata 014, February 9, 2026:

In libexpat fix denial of service due to NULL dereference and integer
overflow.  CVE-2026-24515 CVE-2026-25210

Apply by doing:
    signify -Vep /etc/signify/openbsd-78-base.pub -x 014_expat.patch.sig \
        -m - | (cd /usr/src && patch -p0)

And then rebuild and install libexpat:
    cd /usr/src/lib/libexpat
    make obj
    make
    make install

Index: lib/libexpat/Changes
===================================================================
RCS file: /cvs/src/lib/libexpat/Changes,v
diff -u -p -r1.32 Changes
--- lib/libexpat/Changes	25 Sep 2025 19:05:10 -0000	1.32
+++ lib/libexpat/Changes	2 Feb 2026 22:11:23 -0000
@@ -41,6 +41,30 @@
 !! THANK YOU!                        Sebastian Pipping -- Berlin, 2024-03-09 !!
 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
+        Security fixes:
+           #1131  CVE-2026-24515 -- Function XML_ExternalEntityParserCreate
+                    failed to copy the encoding handler data passed to
+                    XML_SetUnknownEncodingHandler from the parent to the new
+                    subparser. This can cause a NULL dereference (CWE-476) from
+                    external entities that declare use of an unknown encoding.
+                    The expected impact is denial of service. It takes use of
+                    both functions XML_ExternalEntityParserCreate and
+                    XML_SetUnknownEncodingHandler for an application to be
+                    vulnerable.
+           #1075  CVE-2026-25210 -- Add missing check for integer overflow
+                    related to buffer size determination in function doContent
+
+        Bug fixes:
+           #1073  lib: Fix missing undoing of group size expansion in doProlog
+                    failure cases
+           #1104  WASI: Fix format specifiers for 32bit WASI SDK
+
+        Other changes:
+           #1105  lib: Fix strict aliasing
+           #1106  lib: Leverage feature "flexible array member" of C99
+           #1051  lib: Swap (size_t)(-1) for C99 equivalent SIZE_MAX
+     #1102 #1103  WASI: Stop using getpid
+
 Release 2.7.3 Wed September 24 2025
         Security fixes:
      #1046 #1048  Fix alignment of internal allocations for some non-amd64
Index: lib/libexpat/lib/xmlparse.c
===================================================================
RCS file: /cvs/src/lib/libexpat/lib/xmlparse.c,v
diff -u -p -r1.44 xmlparse.c
--- lib/libexpat/lib/xmlparse.c	25 Sep 2025 19:05:10 -0000	1.44
+++ lib/libexpat/lib/xmlparse.c	2 Feb 2026 22:11:25 -0000
@@ -13,7 +13,7 @@
    Copyright (c) 2002-2016 Karl Waclawek <karl@waclawek.net>
    Copyright (c) 2005-2009 Steven Solie <steven@solie.ca>
    Copyright (c) 2016      Eric Rahm <erahm@mozilla.com>
-   Copyright (c) 2016-2025 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2016-2026 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>
@@ -42,6 +42,9 @@
    Copyright (c) 2024-2025 Berkay Eren Ürün <berkay.ueruen@siemens.com>
    Copyright (c) 2024      Hanno Böck <hanno@gentoo.org>
    Copyright (c) 2025      Matthew Fernandez <matthew.fernandez@gmail.com>
+   Copyright (c) 2025      Atrem Borovik <polzovatellllk@gmail.com>
+   Copyright (c) 2025      Alfonso Gregory <gfunni234@gmail.com>
+   Copyright (c) 2026      Rosen Penev <rosenp@gmail.com>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -101,7 +104,7 @@
 #include <limits.h> /* INT_MAX, UINT_MAX */
 #include <stdio.h>  /* fprintf */
 #include <stdlib.h> /* getenv, rand_s */
-#include <stdint.h> /* uintptr_t */
+#include <stdint.h> /* SIZE_MAX, uintptr_t */
 #include <math.h>   /* isnan */
 
 #ifdef _WIN32
@@ -134,11 +137,6 @@
 #  endif /* defined(GRND_NONBLOCK) */
 #endif   /* defined(HAVE_GETRANDOM) || defined(HAVE_SYSCALL_GETRANDOM) */
 
-#if defined(HAVE_LIBBSD)                                                       \
-    && (defined(HAVE_ARC4RANDOM_BUF) || defined(HAVE_ARC4RANDOM))
-#  include <bsd/stdlib.h>
-#endif
-
 #if defined(_WIN32) && ! defined(LOAD_LIBRARY_SEARCH_SYSTEM32)
 #  define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800
 #endif
@@ -155,8 +153,6 @@
       * Linux >=3.17 + glibc (including <2.25) (syscall SYS_getrandom): HAVE_SYSCALL_GETRANDOM, \
       * BSD / macOS >=10.7 / glibc >=2.36 (arc4random_buf): HAVE_ARC4RANDOM_BUF, \
       * BSD / macOS (including <10.7) / glibc >=2.36 (arc4random): HAVE_ARC4RANDOM, \
-      * libbsd (arc4random_buf): HAVE_ARC4RANDOM_BUF + HAVE_LIBBSD, \
-      * libbsd (arc4random): HAVE_ARC4RANDOM + HAVE_LIBBSD, \
       * Linux (including <3.17) / BSD / macOS (including <10.7) / Solaris >=8 (/dev/urandom): XML_DEV_URANDOM, \
       * Windows >=Vista (rand_s): _WIN32. \
     \
@@ -311,8 +307,11 @@ typedef struct tag {
   const char *rawName; /* tagName in the original encoding */
   int rawNameLength;
   TAG_NAME name; /* tagName in the API encoding */
-  char *buf;     /* buffer for name components */
-  char *bufEnd;  /* end of the buffer */
+  union {
+    char *raw;     /* for byte-level access (rawName storage) */
+    XML_Char *str; /* for character-level access (converted name) */
+  } buf;           /* buffer for name components */
+  char *bufEnd;    /* end of the buffer */
   BINDING *bindings;
 } TAG;
 
@@ -349,7 +348,7 @@ typedef struct {
 typedef struct block {
   struct block *next;
   int size;
-  XML_Char s[1];
+  XML_Char s[];
 } BLOCK;
 
 typedef struct {
@@ -1230,8 +1229,11 @@ generate_hash_secret_salt(XML_Parser par
 #  endif /* ! defined(_WIN32) && defined(XML_DEV_URANDOM) */
   /* .. and self-made low quality for backup: */
 
+  entropy = gather_time_entropy();
+#  if ! defined(__wasi__)
   /* Process ID is 0 bits entropy if attacker has local access */
-  entropy = gather_time_entropy() ^ getpid();
+  entropy ^= getpid();
+#  endif
 
   /* Factors are 2^31-1 and 2^61-1 (Mersenne primes M31 and M61) */
   if (sizeof(unsigned long) == 4) {
@@ -1754,6 +1756,7 @@ XML_ExternalEntityParserCreate(XML_Parse
   XML_ExternalEntityRefHandler oldExternalEntityRefHandler;
   XML_SkippedEntityHandler oldSkippedEntityHandler;
   XML_UnknownEncodingHandler oldUnknownEncodingHandler;
+  void *oldUnknownEncodingHandlerData;
   XML_ElementDeclHandler oldElementDeclHandler;
   XML_AttlistDeclHandler oldAttlistDeclHandler;
   XML_EntityDeclHandler oldEntityDeclHandler;
@@ -1799,6 +1802,7 @@ XML_ExternalEntityParserCreate(XML_Parse
   oldExternalEntityRefHandler = parser->m_externalEntityRefHandler;
   oldSkippedEntityHandler = parser->m_skippedEntityHandler;
   oldUnknownEncodingHandler = parser->m_unknownEncodingHandler;
+  oldUnknownEncodingHandlerData = parser->m_unknownEncodingHandlerData;
   oldElementDeclHandler = parser->m_elementDeclHandler;
   oldAttlistDeclHandler = parser->m_attlistDeclHandler;
   oldEntityDeclHandler = parser->m_entityDeclHandler;
@@ -1859,6 +1863,7 @@ XML_ExternalEntityParserCreate(XML_Parse
   parser->m_externalEntityRefHandler = oldExternalEntityRefHandler;
   parser->m_skippedEntityHandler = oldSkippedEntityHandler;
   parser->m_unknownEncodingHandler = oldUnknownEncodingHandler;
+  parser->m_unknownEncodingHandlerData = oldUnknownEncodingHandlerData;
   parser->m_elementDeclHandler = oldElementDeclHandler;
   parser->m_attlistDeclHandler = oldAttlistDeclHandler;
   parser->m_entityDeclHandler = oldEntityDeclHandler;
@@ -1934,7 +1939,7 @@ XML_ParserFree(XML_Parser parser) {
     }
     p = tagList;
     tagList = tagList->parent;
-    FREE(parser, p->buf);
+    FREE(parser, p->buf.raw);
     destroyBindings(p->bindings, parser);
     FREE(parser, p);
   }
@@ -2599,7 +2604,7 @@ XML_GetBuffer(XML_Parser parser, int len
       // NOTE: We are avoiding MALLOC(..) here to leave limiting
       //       the input size to the application using Expat.
       newBuf = parser->m_mem.malloc_fcn(bufferSize);
-      if (newBuf == 0) {
+      if (newBuf == NULL) {
         parser->m_errorCode = XML_ERROR_NO_MEMORY;
         return NULL;
       }
@@ -3126,7 +3131,7 @@ storeRawNames(XML_Parser parser) {
     size_t bufSize;
     size_t nameLen = sizeof(XML_Char) * (tag->name.strLen + 1);
     size_t rawNameLen;
-    char *rawNameBuf = tag->buf + nameLen;
+    char *rawNameBuf = tag->buf.raw + nameLen;
     /* Stop if already stored.  Since m_tagStack is a stack, we can stop
        at the first entry that has already been copied; everything
        below it in the stack is already been accounted for in a
@@ -3142,22 +3147,22 @@ storeRawNames(XML_Parser parser) {
     if (rawNameLen > (size_t)INT_MAX - nameLen)
       return XML_FALSE;
     bufSize = nameLen + rawNameLen;
-    if (bufSize > (size_t)(tag->bufEnd - tag->buf)) {
-      char *temp = REALLOC(parser, tag->buf, bufSize);
+    if (bufSize > (size_t)(tag->bufEnd - tag->buf.raw)) {
+      char *temp = REALLOC(parser, tag->buf.raw, bufSize);
       if (temp == NULL)
         return XML_FALSE;
-      /* if tag->name.str points to tag->buf (only when namespace
+      /* if tag->name.str points to tag->buf.str (only when namespace
          processing is off) then we have to update it
       */
-      if (tag->name.str == (XML_Char *)tag->buf)
+      if (tag->name.str == tag->buf.str)
         tag->name.str = (XML_Char *)temp;
       /* if tag->name.localPart is set (when namespace processing is on)
          then update it as well, since it will always point into tag->buf
       */
       if (tag->name.localPart)
         tag->name.localPart
-            = (XML_Char *)temp + (tag->name.localPart - (XML_Char *)tag->buf);
-      tag->buf = temp;
+            = (XML_Char *)temp + (tag->name.localPart - tag->buf.str);
+      tag->buf.raw = temp;
       tag->bufEnd = temp + bufSize;
       rawNameBuf = temp + nameLen;
     }
@@ -3472,12 +3477,12 @@ doContent(XML_Parser parser, int startTa
         tag = MALLOC(parser, sizeof(TAG));
         if (! tag)
           return XML_ERROR_NO_MEMORY;
-        tag->buf = MALLOC(parser, INIT_TAG_BUF_SIZE);
-        if (! tag->buf) {
+        tag->buf.raw = MALLOC(parser, INIT_TAG_BUF_SIZE);
+        if (! tag->buf.raw) {
           FREE(parser, tag);
           return XML_ERROR_NO_MEMORY;
         }
-        tag->bufEnd = tag->buf + INIT_TAG_BUF_SIZE;
+        tag->bufEnd = tag->buf.raw + INIT_TAG_BUF_SIZE;
       }
       tag->bindings = NULL;
       tag->parent = parser->m_tagStack;
@@ -3490,31 +3495,32 @@ doContent(XML_Parser parser, int startTa
       {
         const char *rawNameEnd = tag->rawName + tag->rawNameLength;
         const char *fromPtr = tag->rawName;
-        toPtr = (XML_Char *)tag->buf;
+        toPtr = tag->buf.str;
         for (;;) {
-          int bufSize;
           int convLen;
           const enum XML_Convert_Result convert_res
               = XmlConvert(enc, &fromPtr, rawNameEnd, (ICHAR **)&toPtr,
                            (ICHAR *)tag->bufEnd - 1);
-          convLen = (int)(toPtr - (XML_Char *)tag->buf);
+          convLen = (int)(toPtr - tag->buf.str);
           if ((fromPtr >= rawNameEnd)
               || (convert_res == XML_CONVERT_INPUT_INCOMPLETE)) {
             tag->name.strLen = convLen;
             break;
           }
-          bufSize = (int)(tag->bufEnd - tag->buf) << 1;
+          if (SIZE_MAX / 2 < (size_t)(tag->bufEnd - tag->buf.raw))
+            return XML_ERROR_NO_MEMORY;
+          const size_t bufSize = (size_t)(tag->bufEnd - tag->buf.raw) * 2;
           {
-            char *temp = REALLOC(parser, tag->buf, bufSize);
+            char *temp = REALLOC(parser, tag->buf.raw, bufSize);
             if (temp == NULL)
               return XML_ERROR_NO_MEMORY;
-            tag->buf = temp;
+            tag->buf.raw = temp;
             tag->bufEnd = temp + bufSize;
             toPtr = (XML_Char *)temp + convLen;
           }
         }
       }
-      tag->name.str = (XML_Char *)tag->buf;
+      tag->name.str = tag->buf.str;
       *toPtr = XML_T('\0');
       result
           = storeAtts(parser, enc, s, &(tag->name), &(tag->bindings), account);
@@ -3878,7 +3884,7 @@ storeAtts(XML_Parser parser, const ENCOD
      * from -Wtype-limits on platforms where
      * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
 #if UINT_MAX >= SIZE_MAX
-    if ((unsigned)parser->m_attsSize > (size_t)(-1) / sizeof(ATTRIBUTE)) {
+    if ((unsigned)parser->m_attsSize > SIZE_MAX / sizeof(ATTRIBUTE)) {
       parser->m_attsSize = oldAttsSize;
       return XML_ERROR_NO_MEMORY;
     }
@@ -3897,7 +3903,7 @@ storeAtts(XML_Parser parser, const ENCOD
      * from -Wtype-limits on platforms where
      * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
 #  if UINT_MAX >= SIZE_MAX
-    if ((unsigned)parser->m_attsSize > (size_t)(-1) / sizeof(XML_AttrInfo)) {
+    if ((unsigned)parser->m_attsSize > SIZE_MAX / sizeof(XML_AttrInfo)) {
       parser->m_attsSize = oldAttsSize;
       return XML_ERROR_NO_MEMORY;
     }
@@ -4073,7 +4079,7 @@ storeAtts(XML_Parser parser, const ENCOD
        * from -Wtype-limits on platforms where
        * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
 #if UINT_MAX >= SIZE_MAX
-      if (nsAttsSize > (size_t)(-1) / sizeof(NS_ATT)) {
+      if (nsAttsSize > SIZE_MAX / sizeof(NS_ATT)) {
         /* Restore actual size of memory in m_nsAtts */
         parser->m_nsAttsPower = oldNsAttsPower;
         return XML_ERROR_NO_MEMORY;
@@ -4256,7 +4262,7 @@ storeAtts(XML_Parser parser, const ENCOD
      * from -Wtype-limits on platforms where
      * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
 #if UINT_MAX >= SIZE_MAX
-    if ((unsigned)(n + EXPAND_SPARE) > (size_t)(-1) / sizeof(XML_Char)) {
+    if ((unsigned)(n + EXPAND_SPARE) > SIZE_MAX / sizeof(XML_Char)) {
       return XML_ERROR_NO_MEMORY;
     }
 #endif
@@ -4502,7 +4508,7 @@ addBinding(XML_Parser parser, PREFIX *pr
        * from -Wtype-limits on platforms where
        * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
 #if UINT_MAX >= SIZE_MAX
-      if ((unsigned)(len + EXPAND_SPARE) > (size_t)(-1) / sizeof(XML_Char)) {
+      if ((unsigned)(len + EXPAND_SPARE) > SIZE_MAX / sizeof(XML_Char)) {
         return XML_ERROR_NO_MEMORY;
       }
 #endif
@@ -4529,7 +4535,7 @@ addBinding(XML_Parser parser, PREFIX *pr
      * from -Wtype-limits on platforms where
      * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
 #if UINT_MAX >= SIZE_MAX
-    if ((unsigned)(len + EXPAND_SPARE) > (size_t)(-1) / sizeof(XML_Char)) {
+    if ((unsigned)(len + EXPAND_SPARE) > SIZE_MAX / sizeof(XML_Char)) {
       return XML_ERROR_NO_MEMORY;
     }
 #endif
@@ -5920,15 +5926,18 @@ doProlog(XML_Parser parser, const ENCODI
              * from -Wtype-limits on platforms where
              * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
 #if UINT_MAX >= SIZE_MAX
-            if (parser->m_groupSize > (size_t)(-1) / sizeof(int)) {
+            if (parser->m_groupSize > SIZE_MAX / sizeof(int)) {
+              parser->m_groupSize /= 2;
               return XML_ERROR_NO_MEMORY;
             }
 #endif
 
             int *const new_scaff_index = REALLOC(
                 parser, dtd->scaffIndex, parser->m_groupSize * sizeof(int));
-            if (new_scaff_index == NULL)
+            if (new_scaff_index == NULL) {
+              parser->m_groupSize /= 2;
               return XML_ERROR_NO_MEMORY;
+            }
             dtd->scaffIndex = new_scaff_index;
           }
         } else {
@@ -7190,7 +7199,7 @@ defineAttribute(ELEMENT_TYPE *type, ATTR
        * from -Wtype-limits on platforms where
        * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
 #if UINT_MAX >= SIZE_MAX
-      if ((unsigned)count > (size_t)(-1) / sizeof(DEFAULT_ATTRIBUTE)) {
+      if ((unsigned)count > SIZE_MAX / sizeof(DEFAULT_ATTRIBUTE)) {
         return 0;
       }
 #endif
@@ -7666,8 +7675,7 @@ dtdCopy(XML_Parser oldParser, DTD *newDt
        * from -Wtype-limits on platforms where
        * sizeof(int) < sizeof(size_t), e.g. on x86_64. */
 #if UINT_MAX >= SIZE_MAX
-      if ((size_t)oldE->nDefaultAtts
-          > ((size_t)(-1) / sizeof(DEFAULT_ATTRIBUTE))) {
+      if ((size_t)oldE->nDefaultAtts > SIZE_MAX / sizeof(DEFAULT_ATTRIBUTE)) {
         return 0;
       }
 #endif
@@ -7869,7 +7877,7 @@ lookup(XML_Parser parser, HASH_TABLE *ta
       unsigned long newMask = (unsigned long)newSize - 1;
 
       /* Detect and prevent integer overflow */
-      if (newSize > (size_t)(-1) / sizeof(NAMED *)) {
+      if (newSize > SIZE_MAX / sizeof(NAMED *)) {
         return NULL;
       }
 
@@ -8105,7 +8113,7 @@ poolBytesToAllocateFor(int blockSize) {
 static XML_Bool FASTCALL
 poolGrow(STRING_POOL *pool) {
   if (pool->freeBlocks) {
-    if (pool->start == 0) {
+    if (pool->start == NULL) {
       pool->blocks = pool->freeBlocks;
       pool->freeBlocks = pool->freeBlocks->next;
       pool->blocks->next = NULL;
@@ -8217,7 +8225,7 @@ nextScaffoldPart(XML_Parser parser) {
      * from -Wtype-limits on platforms where
      * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
 #if UINT_MAX >= SIZE_MAX
-    if (parser->m_groupSize > ((size_t)(-1) / sizeof(int))) {
+    if (parser->m_groupSize > SIZE_MAX / sizeof(int)) {
       return -1;
     }
 #endif
@@ -8244,7 +8252,7 @@ nextScaffoldPart(XML_Parser parser) {
        * from -Wtype-limits on platforms where
        * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
 #if UINT_MAX >= SIZE_MAX
-      if (dtd->scaffSize > (size_t)(-1) / 2u / sizeof(CONTENT_SCAFFOLD)) {
+      if (dtd->scaffSize > SIZE_MAX / 2u / sizeof(CONTENT_SCAFFOLD)) {
         return -1;
       }
 #endif
@@ -8294,15 +8302,15 @@ build_model(XML_Parser parser) {
    * from -Wtype-limits on platforms where
    * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
 #if UINT_MAX >= SIZE_MAX
-  if (dtd->scaffCount > (size_t)(-1) / sizeof(XML_Content)) {
+  if (dtd->scaffCount > SIZE_MAX / sizeof(XML_Content)) {
     return NULL;
   }
-  if (dtd->contentStringLen > (size_t)(-1) / sizeof(XML_Char)) {
+  if (dtd->contentStringLen > SIZE_MAX / sizeof(XML_Char)) {
     return NULL;
   }
 #endif
   if (dtd->scaffCount * sizeof(XML_Content)
-      > (size_t)(-1) - dtd->contentStringLen * sizeof(XML_Char)) {
+      > SIZE_MAX - dtd->contentStringLen * sizeof(XML_Char)) {
     return NULL;
   }
 
Index: lib/libexpat/tests/basic_tests.c
===================================================================
RCS file: /cvs/src/lib/libexpat/tests/basic_tests.c,v
diff -u -p -r1.7 basic_tests.c
--- lib/libexpat/tests/basic_tests.c	17 Sep 2025 17:23:00 -0000	1.7
+++ lib/libexpat/tests/basic_tests.c	2 Feb 2026 22:11:26 -0000
@@ -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-2025 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2016-2026 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>
@@ -4570,6 +4570,46 @@ START_TEST(test_unknown_encoding_invalid
 }
 END_TEST
 
+START_TEST(test_unknown_encoding_user_data_primary) {
+  // This test is based on ideas contributed by Artiphishell Inc.
+  const char *const text = "<?xml version='1.0' encoding='x-unk'?>\n"
+                           "<root />\n";
+  XML_Parser parser = XML_ParserCreate(NULL);
+  XML_SetUnknownEncodingHandler(parser,
+                                user_data_checking_unknown_encoding_handler,
+                                (void *)(intptr_t)0xC0FFEE);
+
+  assert_true(_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE)
+              == XML_STATUS_OK);
+
+  XML_ParserFree(parser);
+}
+END_TEST
+
+START_TEST(test_unknown_encoding_user_data_secondary) {
+  // This test is based on ideas contributed by Artiphishell Inc.
+  const char *const text_main = "<!DOCTYPE r [\n"
+                                "  <!ENTITY ext SYSTEM 'ext.ent'>\n"
+                                "]>\n"
+                                "<r>&ext;</r>\n";
+  const char *const text_external = "<?xml version='1.0' encoding='x-unk'?>\n"
+                                    "<e>data</e>";
+  ExtTest2 test_data = {text_external, (int)strlen(text_external), NULL, NULL};
+  XML_Parser parser = XML_ParserCreate(NULL);
+  XML_SetExternalEntityRefHandler(parser, external_entity_loader2);
+  XML_SetUnknownEncodingHandler(parser,
+                                user_data_checking_unknown_encoding_handler,
+                                (void *)(intptr_t)0xC0FFEE);
+  XML_SetUserData(parser, &test_data);
+
+  assert_true(_XML_Parse_SINGLE_BYTES(parser, text_main, (int)strlen(text_main),
+                                      XML_TRUE)
+              == XML_STATUS_OK);
+
+  XML_ParserFree(parser);
+}
+END_TEST
+
 /* Test an external entity parser set to use latin-1 detects UTF-16
  * BOMs correctly.
  */
@@ -6420,6 +6460,8 @@ make_basic_test_case(Suite *s) {
   tcase_add_test(tc_basic, test_unknown_encoding_invalid_surrogate);
   tcase_add_test(tc_basic, test_unknown_encoding_invalid_high);
   tcase_add_test(tc_basic, test_unknown_encoding_invalid_attr_value);
+  tcase_add_test(tc_basic, test_unknown_encoding_user_data_primary);
+  tcase_add_test(tc_basic, test_unknown_encoding_user_data_secondary);
   tcase_add_test__if_xml_ge(tc_basic, test_ext_entity_latin1_utf16le_bom);
   tcase_add_test__if_xml_ge(tc_basic, test_ext_entity_latin1_utf16be_bom);
   tcase_add_test__if_xml_ge(tc_basic, test_ext_entity_latin1_utf16le_bom2);
Index: lib/libexpat/tests/handlers.c
===================================================================
RCS file: /cvs/src/lib/libexpat/tests/handlers.c,v
diff -u -p -r1.4 handlers.c
--- lib/libexpat/tests/handlers.c	17 Sep 2025 17:23:00 -0000	1.4
+++ lib/libexpat/tests/handlers.c	2 Feb 2026 22:11:26 -0000
@@ -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-2025 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2016-2026 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>
@@ -45,6 +45,7 @@
 #  undef NDEBUG /* because test suite relies on assert(...) at the moment */
 #endif
 
+#include <stdint.h>
 #include <stdio.h>
 #include <string.h>
 #include <assert.h>
@@ -405,6 +406,15 @@ long_encoding_handler(void *userData, co
   info->convert = NULL;
   info->release = NULL;
   return XML_STATUS_OK;
+}
+
+int XMLCALL
+user_data_checking_unknown_encoding_handler(void *userData,
+                                            const XML_Char *encoding,
+                                            XML_Encoding *info) {
+  const intptr_t number = (intptr_t)userData;
+  assert_true(number == 0xC0FFEE);
+  return long_encoding_handler(userData, encoding, info);
 }
 
 /* External Entity Handlers */
Index: lib/libexpat/tests/handlers.h
===================================================================
RCS file: /cvs/src/lib/libexpat/tests/handlers.h,v
diff -u -p -r1.3 handlers.h
--- lib/libexpat/tests/handlers.h	14 Mar 2025 20:20:30 -0000	1.3
+++ lib/libexpat/tests/handlers.h	2 Feb 2026 22:11:26 -0000
@@ -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-2024 Sebastian Pipping <sebastian@pipping.org>
+   Copyright (c) 2016-2026 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>
@@ -158,6 +158,9 @@ extern int XMLCALL MiscEncodingHandler(v
 extern int XMLCALL long_encoding_handler(void *userData,
                                          const XML_Char *encoding,
                                          XML_Encoding *info);
+
+extern int XMLCALL user_data_checking_unknown_encoding_handler(
+    void *userData, const XML_Char *encoding, XML_Encoding *info);
 
 /* External Entity Handlers */
 
