CVE-2026-9966
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifthird_party/libxml/chromium/fix-xmlEscapeText-integer-overflow.patch |
modified | |
ifthird_party/libxml/src/xmlIO.c |
modified |
Files Changed
third_party/libxml/README.chromiumthird_party/libxml/chromium/fix-xmlEscapeText-integer-overflow.patchthird_party/libxml/chromium/roll.pythird_party/libxml/src/xmlIO.c
Patch
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)) {
Original Bug Report
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.cchrome/browser/notifications/win/notification_template_builder.ccchrome/browser/notifications/notification_platform_bridge_win.ccthird_party/libxml/src/entities.cthird_party/libxml/src/xmlwriter.cthird_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.
- Mojo Limit Bypass: Although Mojo defines a
max_message_num_bytesof 256 MB, this limit is only checked on the sending side by triggering aDumpWithoutCrashing(); it does not block the message. The receiving side in the browser process (mojo/core/channel.cc) does not enforce this limit during deserialization. - Notification Processing: After verifying the origin holds the Notifications permission,
NotificationPlatformBridgeWinImpl::DisplaycallsBuildNotificationTemplate. This invokesbase::UTF16ToUTF8, converting the attacker’s UTF-16 payload into a UTF-8 string. - Libxml2 Execution: The massive UTF-8 string is passed to
XmlWriter::AppendElementContent, which internally callsxmlEscapeText.
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
- Upstream (libxml2): In
third_party/libxml/src/xmlIO.c, change the type ofnewSizefrominttosize_tat line 268 to prevent truncation. - Chromium IPC: Enforce maximum string length limits for
titleandbodyinblink.mojom.NotificationServicebefore processing them in the browser process. Additionally, consider strictly enforcing Mojo’smax_message_num_byteson the receiving end inmojo/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.