From 576b61e42feeea704253cb7c7bedb2eeb3754387 Mon Sep 17 00:00:00 2001
From: laserbear <10689391+Laserbear@users.noreply.github.com>
Date: Sun, 8 Mar 2026 17:28:06 -0700
Subject: [PATCH 1/2] copy prefix name to pool before lookup

.. so that we cannot end up with a zombie PREFIX in the pool
that has NULL for a name.

Co-authored-by: Sebastian Pipping <sebastian@pipping.org>
---
 expat/lib/xmlparse.c | 43 +++++++++++++++++++++++++++++++++++--------
 1 file changed, 35 insertions(+), 8 deletions(-)

diff -urp firefox-115.4.0.orig/parser/expat/lib/xmlparse.c firefox-115.4.0/parser/expat/lib/xmlparse.c
--- firefox-115.4.0.orig/parser/expat/lib/xmlparse.c	2026-04-25 05:11:04.630386451 -0500
+++ firefox-115.4.0/parser/expat/lib/xmlparse.c	2026-04-25 05:15:19.059008170 -0500
@@ -6395,13 +6395,24 @@ setContext(XML_Parser parser, const XML_
           return XML_FALSE;
         prefix = (PREFIX *)lookup(parser, &dtd->prefixes, poolStart(&tempPool),
                                   sizeof(PREFIX));
-        if (!prefix)
+        const XML_Char *const prefixName = poolCopyStringNoFinish(
+            &dtd->pool, poolStart(&parser->m_tempPool));
+        if (! prefixName) {
           return XML_FALSE;
-        if (prefix->name == poolStart(&tempPool)) {
-          prefix->name = poolCopyString(&dtd->pool, prefix->name);
-          if (!prefix->name)
-            return XML_FALSE;
         }
+
+        prefix = (PREFIX *)lookup(parser, &dtd->prefixes, prefixName,
+                                  sizeof(PREFIX));
+
+        const bool prefixNameUsed = prefix && prefix->name == prefixName;
+        if (prefixNameUsed)
+          poolFinish(&dtd->pool);
+        else
+          poolDiscard(&dtd->pool);
+
+        if (! prefix)
+          return XML_FALSE;
+
         poolDiscard(&tempPool);
       }
       for (context = s + 1;
@@ -7016,6 +7027,23 @@ poolCopyString(STRING_POOL *pool, const
   return s;
 }
 
+// A version of `poolCopyString` that does not call `poolFinish`
+// and reverts any partial advancement upon failure.
+static const XML_Char *FASTCALL
+poolCopyStringNoFinish(STRING_POOL *pool, const XML_Char *s) {
+  const XML_Char *const original = s;
+  do {
+    if (! poolAppendChar(pool, *s)) {
+      // Revert any previously successful advancement
+      const ptrdiff_t advancedBy = s - original;
+      if (advancedBy > 0)
+        pool->ptr -= advancedBy;
+      return NULL;
+    }
+  } while (*s++);
+  return pool->start;
+}
+
 static const XML_Char *
 poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n)
 {
