CVE-2026-13954
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifthird_party/blink/renderer/core/xml/xsl_style_sheet_libxslt.cc |
modified |
Files Changed
third_party/blink/renderer/core/xml/parser/xml_document_parser.ccthird_party/blink/renderer/core/xml/parser/xml_document_parser.hthird_party/blink/renderer/core/xml/xsl_style_sheet_libxslt.cc
Patch
From ba55decbd6a55c8f519eccd5e9f6ee5937ea1843 Mon Sep 17 00:00:00 2001
From: David Baron <dbaron@chromium.org>
Date: Mon, 18 May 2026 10:41:27 -0700
Subject: [PATCH] Ensure we've initialized libxml before parsing XSLT.
Fixed: 513504934
Change-Id: Ide1f7bca5b6ea4e706bdf0fd4f327098205281ac
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7850598
Reviewed-by: Joey Arhar <jarhar@chromium.org>
Commit-Queue: David Baron <dbaron@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1632268}
---
diff --git a/third_party/blink/renderer/core/xml/parser/xml_document_parser.cc b/third_party/blink/renderer/core/xml/parser/xml_document_parser.cc
index aac93c5..1957e77 100644
--- a/third_party/blink/renderer/core/xml/parser/xml_document_parser.cc
+++ b/third_party/blink/renderer/core/xml/parser/xml_document_parser.cc
@@ -752,7 +752,8 @@
// FIXME: It would be nice to display error messages somewhere.
}
-static void EnsureLibXMLInitialized() {
+// static
+void XMLDocumentParser::EnsureLibXMLInitialized() {
static bool did_init = false;
if (did_init)
return;
@@ -766,7 +767,7 @@
scoped_refptr<XMLParserContext> XMLParserContext::CreateStringParser(
xmlSAXHandlerPtr handlers,
void* user_data) {
- EnsureLibXMLInitialized();
+ XMLDocumentParser::EnsureLibXMLInitialized();
xmlParserCtxtPtr parser =
xmlCreatePushParserCtxt(handlers, nullptr, nullptr, 0, nullptr);
@@ -790,7 +791,7 @@
xmlSAXHandlerPtr handlers,
void* user_data,
const std::string& chunk) {
- EnsureLibXMLInitialized();
+ XMLDocumentParser::EnsureLibXMLInitialized();
// appendFragmentSource() checks that the length doesn't overflow an int.
xmlParserCtxtPtr parser = xmlCreateMemoryParserCtxt(
@@ -1753,7 +1754,7 @@
// In situations where the XMLDocumentParserRs is used as the primary parser,
// this might be the first call into libxml2.
- EnsureLibXMLInitialized();
+ XMLDocumentParser::EnsureLibXMLInitialized();
// Parse in a single chunk into an xmlDocPtr
// FIXME: Hook up error handlers so that a failure to parse the main
diff --git a/third_party/blink/renderer/core/xml/parser/xml_document_parser.h b/third_party/blink/renderer/core/xml/parser/xml_document_parser.h
index 8bac86e1..6e09d417 100644
--- a/third_party/blink/renderer/core/xml/parser/xml_document_parser.h
+++ b/third_party/blink/renderer/core/xml/parser/xml_document_parser.h
@@ -85,6 +85,8 @@
~XMLDocumentParser() override;
void Trace(Visitor*) const override;
+ static void EnsureLibXMLInitialized();
+
// Exposed for callbacks:
void HandleError(XMLErrors::ErrorType, const char* message, TextPosition);
diff --git a/third_party/blink/renderer/core/xml/xsl_style_sheet_libxslt.cc b/third_party/blink/renderer/core/xml/xsl_style_sheet_libxslt.cc
index 64f72a6..731c9cd 100644
--- a/third_party/blink/renderer/core/xml/xsl_style_sheet_libxslt.cc
+++ b/third_party/blink/renderer/core/xml/xsl_style_sheet_libxslt.cc
@@ -29,6 +29,7 @@
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/loader/resource/xsl_style_sheet_resource.h"
+#include "third_party/blink/renderer/core/xml/parser/xml_document_parser.h"
#include "third_party/blink/renderer/core/xml/parser/xml_document_parser_scope.h"
#include "third_party/blink/renderer/core/xml/parser/xml_parser_input.h"
#include "third_party/blink/renderer/core/xml/xsl_style_sheet.h"
@@ -120,9 +121,13 @@
}
bool XSLStyleSheet::ParseString(const String& source) {
+ XMLDocumentParser::EnsureLibXMLInitialized();
+
// Parse in a single chunk into an xmlDocPtr
- if (!stylesheet_doc_taken_)
+ if (!stylesheet_doc_taken_) {
xmlFreeDoc(stylesheet_doc_);
+ stylesheet_doc_ = nullptr;
+ }
stylesheet_doc_taken_ = false;
FrameConsole* console = nullptr;
Original Bug Report
Information Disclosure via XXE in XSLTProcessor.importStylesheet
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. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.
Overview: A potential XML External Entity (XXE) vulnerability exists in Blink’s XSLT implementation due to missing security callback initialization and unescaped comment serialization. This could allow a malicious website to read local files from the renderer process’s filesystem on platforms like Android.
Affected files:
third_party/blink/renderer/core/xml/xsl_style_sheet_libxslt.ccthird_party/blink/renderer/core/editing/serializers/markup_formatter.ccthird_party/blink/renderer/core/xml/xslt_processor_libxslt.ccthird_party/blink/renderer/core/xml/parser/xml_document_parser.cc
Estimated timestamp from git blame: 2013-08-23
Summary
A potential vulnerability in Blink’s XSLT implementation may allow for XML External Entity (XXE) attacks, enabling an attacker to read files from the renderer’s local filesystem. The issue stems from a failure to initialize Blink’s secure libxml2 IO callbacks before parsing a stylesheet, combined with a lack of escaping when serializing XML comments.
Root Cause Analysis
Blink typically protects against libxml2’s default behavior of loading external entities by registering specialized IO callbacks (e.g., MatchFunc, OpenFunc) via EnsureLibXMLInitialized() in third_party/blink/renderer/core/xml/parser/xml_document_parser.cc. These callbacks enforce security policies like same-origin checks and prohibit direct filesystem access.
However, XSLStyleSheet::ParseString in third_party/blink/renderer/core/xml/xsl_style_sheet_libxslt.cc fails to call EnsureLibXMLInitialized(). If ParseString is invoked in a fresh renderer process before any other XML parsing has occurred, libxml2 will use its default, insecure IO handlers. On Android, the renderer’s seccomp policy explicitly allows the openat syscall (see sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.cc), allowing these default handlers to read local files.
Furthermore, the XSLTProcessor.importStylesheet() API uses CreateMarkup to serialize the provided DOM tree. In third_party/blink/renderer/core/editing/serializers/markup_formatter.cc, the AppendComment method (line 274) does not escape the comment closer -->, allowing an attacker to inject a malicious DTD directly into the serialized XML string that ParseString then parses.
Potential Attack Steps
An attacker could potentially trigger this vulnerability using the following steps:
- Create a malicious XML document containing a Comment node with a payload like:
--> <!DOCTYPE x [<!ENTITY f SYSTEM "file:///proc/self/status">]> <xsl:stylesheet ...> ... &f; ... </xsl:stylesheet> <!--. - Call
XSLTProcessor.importStylesheet(malicious_doc). This triggers the insecure serialization inAppendCommentand the subsequent vulnerable parse inParseString. - Call
transformToFragment()ortransformToDocument(). The XSLT engine will execute the injected stylesheet, resolving the&f;entity to the contents of/proc/self/statususing libxml2’s default IO handlers. - Read the leaked data from the resulting fragment or document’s text content.
Impact
This is an information disclosure vulnerability. On Android, an attacker can potentially read any file accessible to the renderer process (e.g., /proc/self/maps, /proc/self/status), which can be used to leak ASLR offsets and facilitate a full renderer exploit chain. On other platforms like Linux, while the logic flaw exists, seccomp typically blocks the open() syscall, mitigating direct file access.
Suggested Fix
- In
XSLStyleSheet::ParseString, ensureEnsureLibXMLInitialized()is called before any libxml2 parsing functions. - In
MarkupFormatter::AppendComment, implement proper escaping or validation to ensure that the comment data does not contain the-->sequence.
Evaluated with Chrome root at commit: b3153093eb3c78c3e88ccf562bcbc20437a04b0e
Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:
- If you are familiar with the severity guidelines, you may adjust the severity.
- If this is a false positive, and there’s no work to be done, please close as WAI.
- If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.
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.