High chrome Integer Overflow 📄 Reporter bug report 🔧 Commit mapped

Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInteger overflow in XML
DescriptionInteger overflow in XML
ComponentXML
Bug ClassInteger Overflow
Tracker506388321
Fix commit240a66356280 (chromium/src) +18/-1
CISA KEVNot listed
CreditedGoogle
Disclosed2026-05-27

Changed Functions

FunctionChangeNotes
if
third_party/libxml/chromium/fix-xmlEscapeText-integer-overflow.patch
modified
if
third_party/libxml/src/xmlIO.c
modified

Files Changed

  • third_party/libxml/README.chromium
  • third_party/libxml/chromium/fix-xmlEscapeText-integer-overflow.patch
  • third_party/libxml/chromium/roll.py
  • third_party/libxml/src/xmlIO.c
From 240a66356280ba79aabb6dbe1d27e1b273b3750f Mon Sep 17 00:00:00 2001
From: Andrew Paseltiner <[email protected]>
Date: Mon, 27 Apr 2026 11:26:17 -0700
Subject: [PATCH] Fix integer truncation in libxml2 xmlEscapeText

Changing newSize from int to size_t prevents a 32-bit signed integer
truncation that could lead to an undersized allocation and a subsequent
heap-buffer-overflow.

A regression test was manually verified under ASAN to detect the bug (it
triggers a heap-buffer-overflow without the fix and passes with it).
However, this test is not included in the commit as it requires a ~2GB
allocation which could cause flakiness or OOMs on CQ bots with limited
memory.

Fixed: 506388321
Change-Id: Ic46cccfbeaad277bf38413901be5357801abd6e6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7794884
Reviewed-by: Daniel Cheng <[email protected]>
Commit-Queue: Andrew Paseltiner <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1621216}
---

diff --git a/third_party/libxml/README.chromium b/third_party/libxml/README.chromium
index 2f87edb..1172eab8 100644
--- a/third_party/libxml/README.chromium
+++ b/third_party/libxml/README.chromium
@@ -20,6 +20,9 @@
 - xml-attr-extra.patch: add an `extra` member to struct _xmlAttr.
 - widen-extra-field-in-_xmlNode-to-an-int.patch widen
   the `extra` member in _xmlNode.
+- fix-xmlEscapeText-integer-overflow.patch: Fixes a 32-bit signed integer
+  truncation in xmlEscapeText that can lead to an undersized allocation and
+  heap-buffer-overflow.
 - Add helper classes in the chromium/ subdirectory.
 - Delete various unused files, see chromium/roll.py
 - Disable various unused libxml features:
diff --git a/third_party/libxml/chromium/fix-xmlEscapeText-integer-overflow.patch b/third_party/libxml/chromium/fix-xmlEscapeText-integer-overflow.patch
new file mode 100644
index 0000000..51ad1c0
--- /dev/null
+++ b/third_party/libxml/chromium/fix-xmlEscapeText-integer-overflow.patch
@@ -0,0 +1,13 @@
+diff --git a/xmlIO.c b/xmlIO.c
+index 789fc5e72cd20..d1fb6934df8a5 100644
+--- a/xmlIO.c
++++ b/xmlIO.c
+@@ -265,7 +265,7 @@ xmlEscapeText(const xmlChar *string, int flags) {
+ 
+         if (totalSize > size - used) {
+             xmlChar *tmp;
+-            int newSize;
++            size_t newSize;
+ 
+             if ((size > (SIZE_MAX - 1) / 2) ||
+                 (totalSize > (SIZE_MAX - 1) / 2 - size)) {
diff --git a/third_party/libxml/chromium/roll.py b/third_party/libxml/chromium/roll.py
index dfb9753..40cb99d 100755
--- a/third_party/libxml/chromium/roll.py
+++ b/third_party/libxml/chromium/roll.py
@@ -75,6 +75,7 @@
     'remove-getentropy.patch',
     'xml-attr-extra.patch',
     'widen-extra-field-in-_xmlNode-to-an-int.patch',
+    'fix-xmlEscapeText-integer-overflow.patch',
 ]
 
 
diff --git a/third_party/libxml/src/xmlIO.c b/third_party/libxml/src/xmlIO.c
index 789fc5e7..d1fb693 100644
--- a/third_party/libxml/src/xmlIO.c
+++ b/third_party/libxml/src/xmlIO.c
@@ -265,7 +265,7 @@
 
         if (totalSize > size - used) {
             xmlChar *tmp;
-            int newSize;
+            size_t newSize;
 
             if ((size > (SIZE_MAX - 1) / 2) ||
                 (totalSize > (SIZE_MAX - 1) / 2 - size)) {
Loading diff…

Original Bug Report

reported by [email protected]

Potential High: Heap Buffer Overflow in libxml2 `xmlEscapeText` via Browser Notifications

Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports without the Chrome Security team. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.

Overview: A 32-bit signed integer truncation in libxml2’s xmlEscapeText function can cause xmlRealloc to return an undersized buffer. This can potentially be exploited by a compromised renderer via the NotificationService.DisplayPersistentNotification Mojo IPC, leading to an out-of-bounds write and a sandbox escape in the browser process.

Affected files:

  • third_party/libxml/src/xmlIO.c
  • chrome/browser/notifications/win/notification_template_builder.cc
  • chrome/browser/notifications/notification_platform_bridge_win.cc
  • third_party/libxml/src/entities.c
  • third_party/libxml/src/xmlwriter.c
  • third_party/libxml/chromium/xml_writer.cc

Estimated timestamp from git blame: 2025-12-04

Vulnerability Description

There is a potential heap buffer overflow in libxml2’s xmlEscapeText function (located at third_party/libxml/src/xmlIO.c:175), which is reachable from the browser process via Windows notifications. The vulnerability stems from a 32-bit signed integer truncation when calculating a reallocation size for an internal buffer.

During string processing, xmlEscapeText calculates newSize for reallocation:

// third_party/libxml/src/xmlIO.c:266-286
if (totalSize > size - used) {
    xmlChar *tmp;
    int newSize; // Vulnerable: declared as signed 32-bit int

    // Insufficient check: only checks against SIZE_MAX, not INT_MAX
    if ((size > (SIZE_MAX - 1) / 2) ||
        (totalSize > (SIZE_MAX - 1) / 2 - size)) {
        xmlFree(buffer);
        return(NULL);
    }
    newSize = size + totalSize; // Truncation from size_t to int
    if (*cur != 0)
        newSize *= 2; // Signed integer overflow/wrap-around
    tmp = xmlRealloc(buffer, newSize + 1);
    // ...
    buffer = tmp;
    size = newSize;
    out = buffer + used;
}

Although the preceding checks ensure size and totalSize do not overflow SIZE_MAX, newSize is declared as a signed 32-bit int. If the sum of size and totalSize exceeds INT_MAX (2.147 GB), the assignment to newSize truncates. Furthermore, if *cur != 0, the multiplication by 2 will cause the negative int to wrap around (in 2’s complement arithmetic) to a small positive value (e.g., -2147483640 * 2 becomes 16).

xmlRealloc will then successfully allocate this tiny buffer. However, the used variable (the offset of data written so far) retains its massive value. The output pointer out = buffer + used will point far beyond the bounds of the newly allocated buffer, resulting in a massive out-of-bounds write during the subsequent memcpy(out, base, unescapedSize).

Reachability and Attack Vector

A compromised renderer can trigger this vulnerability by sending an extremely large string via the NotificationService.DisplayPersistentNotification Mojo IPC.

  1. Mojo Limit Bypass: Although Mojo defines a max_message_num_bytes of 256 MB, this limit is only checked on the sending side by triggering a DumpWithoutCrashing(); it does not block the message. The receiving side in the browser process (mojo/core/channel.cc) does not enforce this limit during deserialization.
  2. Notification Processing: After verifying the origin holds the Notifications permission, NotificationPlatformBridgeWinImpl::Display calls BuildNotificationTemplate. This invokes base::UTF16ToUTF8, converting the attacker’s UTF-16 payload into a UTF-8 string.
  3. Libxml2 Execution: The massive UTF-8 string is passed to XmlWriter::AppendElementContent, which internally calls xmlEscapeText.

By carefully structuring the input string with chunks of normal text interspersed with XML-escapable characters, an attacker can precisely steer the size and used variables in xmlEscapeText up to ~2.145 GB (just under PartitionAlloc’s MaxDirectMapped limit), triggering the truncation and the subsequent undersized allocation.

Exploitability

This vulnerability provides an attacker with a controlled out-of-bounds write deep into the browser process’s heap. Because Chromium’s 64-bit address space relies on predictable heap layouts, an attacker could use standard heap spraying techniques prior to triggering the vulnerability to place target objects (e.g., C++ objects with vftables) at the destination offset. Overwriting these function pointers would lead to arbitrary Remote Code Execution (RCE) in the unsandboxed browser process, constituting a full sandbox escape.

(Note: These are suggested steps; a working proof-of-concept has not been run.)

Suggested Fix

  1. Upstream (libxml2): In third_party/libxml/src/xmlIO.c, change the type of newSize from int to size_t at line 268 to prevent truncation.
  2. Chromium IPC: Enforce maximum string length limits for title and body in blink.mojom.NotificationService before processing them in the browser process. Additionally, consider strictly enforcing Mojo’s max_message_num_bytes on the receiving end in mojo/core/channel.cc.

Evaluated with Chrome root at commit: a1e33f5848218e21d4a16ae2c1bc94e815c30c7f


Results so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.

View on issue tracker