Medium chrome Memory Corruption 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactMemory corruption in CSS
DescriptionMemory corruption in CSS
ComponentCSS
Sub-componentBlink>CSS
Bug ClassMemory Corruption
Tracker560664595
Fix commitf4dce9c0b732 (chromium/src) +62/-6
CISA KEVNot listed
Creditedcaveeroo
Disclosed2026-10-20

Changed Functions

FunctionChangeNotes
if
third_party/blink/renderer/core/css/css_uri_value.cc
modified
if
third_party/blink/renderer/core/svg/svg_resource.cc
modified

Files Changed

  • third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter.css
  • third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter.py
  • third_party/blink/renderer/core/svg/svg_resource.cc
  • third_party/blink/renderer/core/css/css_uri_value.cc
  • third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter-no-referrer.html
  • third_party/blink/renderer/core/svg/svg_resource.h
From f4dce9c0b732f5f4333933a72b3b1a3f48762d41 Mon Sep 17 00:00:00 2001
From: Rune Lillesveen <futhark@chromium.org>
Date: Tue, 15 Sep 2026 07:24:07 -0700
Subject: [PATCH] Pass referrer to ExternalSVGResourceDocumentContent

The document loading the stylesheet would otherwise leak as a referrer
to the SVG request.

Bug: 560664595
Change-Id: I5952fbf2282384384a8d7cebd7f63aa12b6ac16c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8410307
Reviewed-by: Fredrik Söderquist <fs@opera.com>
Commit-Queue: Rune Lillesveen <futhark@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1698068}
---
diff --git a/third_party/blink/renderer/core/css/css_uri_value.cc b/third_party/blink/renderer/core/css/css_uri_value.cc
index 4f353389..c7cbbae 100644
--- a/third_party/blink/renderer/core/css/css_uri_value.cc
+++ b/third_party/blink/renderer/core/css/css_uri_value.cc
@@ -20,7 +20,7 @@
 SVGResource* CSSURIValue::EnsureResourceReference() const {
   if (!resource_) {
     resource_ = MakeGarbageCollected<ExternalSVGResourceDocumentContent>(
-        AbsoluteUrl(), UrlData().GetModifiers());
+        AbsoluteUrl(), UrlData().GetModifiers(), UrlData().GetReferrer());
   }
   return resource_.Get();
 }
diff --git a/third_party/blink/renderer/core/svg/svg_resource.cc b/third_party/blink/renderer/core/svg/svg_resource.cc
index 5c73b0ac..242c83b1 100644
--- a/third_party/blink/renderer/core/svg/svg_resource.cc
+++ b/third_party/blink/renderer/core/svg/svg_resource.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/core/svg/svg_resource.h"
 
 #include "services/network/public/mojom/content_security_policy.mojom-blink.h"
+#include "third_party/blink/public/common/loader/referrer_utils.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/dom/id_target_observer.h"
@@ -235,8 +236,9 @@
 
 ExternalSVGResourceDocumentContent::ExternalSVGResourceDocumentContent(
     const KURL& url,
-    const CSSUrlRequestModifiers& modifiers)
-    : url_(url), modifiers_(modifiers) {}
+    const CSSUrlRequestModifiers& modifiers,
+    const Referrer& referrer)
+    : url_(url), modifiers_(modifiers), referrer_(referrer) {}
 
 void ExternalSVGResourceDocumentContent::Load(
     Document& document,
@@ -252,10 +254,15 @@
   options.initiator_info.name = fetch_initiator_type_names::kCSS;
   FetchParameters params(ResourceRequest(url_), options);
 
+  ResourceRequest& resource_request = params.MutableResourceRequest();
   if (modifiers_.referrer_policy) {
-    params.MutableResourceRequest().SetReferrerPolicy(
-        *modifiers_.referrer_policy);
+    resource_request.SetReferrerPolicy(*modifiers_.referrer_policy);
+  } else {
+    resource_request.SetReferrerPolicy(
+        ReferrerUtils::MojoReferrerPolicyResolveDefault(
+            referrer_.referrer_policy));
   }
+  resource_request.SetReferrerString(referrer_.referrer);
 
   CrossOriginAttributeValue effective_cross_origin =
       modifiers_.cross_origin != kCrossOriginAttributeNotSet
diff --git a/third_party/blink/renderer/core/svg/svg_resource.h b/third_party/blink/renderer/core/svg/svg_resource.h
index 7f5bc2e..0967b8a8 100644
--- a/third_party/blink/renderer/core/svg/svg_resource.h
+++ b/third_party/blink/renderer/core/svg/svg_resource.h
@@ -158,7 +158,8 @@
  public:
   explicit ExternalSVGResourceDocumentContent(
       const KURL&,
-      const CSSUrlRequestModifiers& modifiers);
+      const CSSUrlRequestModifiers& modifiers,
+      const Referrer& referrer);
 
   void Load(Document&, CrossOriginAttributeValue) override;
   void LoadWithoutCSP(Document&) override;
@@ -177,6 +178,7 @@
   Member<SVGResourceDocumentContent> document_content_;
   KURL url_;
   CSSUrlRequestModifiers modifiers_;
+  Referrer referrer_;
 };
 
 // External resource reference (see SVGResource) with an ImageResourceContent
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter-no-referrer.html b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter-no-referrer.html
new file mode 100644
index 0000000..9a85588
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter-no-referrer.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<title>referrerpolicy=no-referrer should not leak referrer across stylesheet into svg resource fetch</title>
+<link rel="match" href="/css/reference/ref-filled-green-100px-square-only.html">
+<link rel="stylesheet" href="filter.css?pipe=sub" referrerpolicy="no-referrer">
+<p>Test passes if there is a filled green square.</p>
+<div id="target" style="width:100px;height:100px;background-color:red"></div>
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter.css b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter.css
new file mode 100644
index 0000000..55b90667
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter.css
@@ -0,0 +1,3 @@
+#target {
+  filter: url("filter.py#make-solid-green" cross-origin(anonymous) referrer-policy(unsafe-url));
+}
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter.py b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter.py
new file mode 100644
index 0000000..73fcb0b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter.py
@@ -0,0 +1,38 @@
+# Test runs following request chain:
+#
+# filter-no-referrer.html -> filter.css -> filter.py
+#
+# filter.py returns a filter resource to turn a rectangle green in
+# filter-no-referrer.html if the referer header passed to filter.py does not
+# contains "no-referrer". filter-no-referrer.html sets referrerpolicy to
+# no-referrer, so that should definitely not leak to the svg resource request
+# here.
+#
+def main(request, response):
+    response.add_required_headers = False
+    response.writer.write_status(200)
+    response.writer.write_header(b"access-control-allow-origin", b"*")
+    response.writer.write_header(b"content-type", b"image/svg+xml")
+    response.writer.write_header(b"cache-control",
+                                 b"no-cache; must-revalidate")
+
+    if request.headers.get(b'referer',
+                           b'').decode("utf-8").find('no-referrer') == -1:
+        body = (
+            b'<svg xmlns="http://www.w3.org/2000/svg">'
+            b'  <defs>'
+            b'    <filter id="make-solid-green" color-interpolation-filters="sRGB">'
+            b'      <feColorMatrix type="matrix" values="'
+            b'        0 0 0 0 0'
+            b'        0 0 0 0 0.502'
+            b'        0 0 0 0 0'
+            b'        0 0 0 1 0" />'
+            b'    </filter>'
+            b'  </defs>'
+            b'</svg>')
+    else:
+        body = b""
+
+    response.writer.write_header(b"content-length", len(body))
+    response.writer.end_headers()
+    response.writer.write(body)
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter-no-referrer.html b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter-no-referrer.html
new file mode 100644
index 0000000..9a85588
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter-no-referrer.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<title>referrerpolicy=no-referrer should not leak referrer across stylesheet into svg resource fetch</title>
+<link rel="match" href="/css/reference/ref-filled-green-100px-square-only.html">
+<link rel="stylesheet" href="filter.css?pipe=sub" referrerpolicy="no-referrer">
+<p>Test passes if there is a filled green square.</p>
+<div id="target" style="width:100px;height:100px;background-color:red"></div>
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter.css b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter.css
new file mode 100644
index 0000000..55b90667
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter.css
@@ -0,0 +1,3 @@
+#target {
+  filter: url("filter.py#make-solid-green" cross-origin(anonymous) referrer-policy(unsafe-url));
+}
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter.py b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter.py
new file mode 100644
index 0000000..73fcb0b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter.py
@@ -0,0 +1,38 @@
+# Test runs following request chain:
+#
+# filter-no-referrer.html -> filter.css -> filter.py
+#
+# filter.py returns a filter resource to turn a rectangle green in
+# filter-no-referrer.html if the referer header passed to filter.py does not
+# contains "no-referrer". filter-no-referrer.html sets referrerpolicy to
+# no-referrer, so that should definitely not leak to the svg resource request
+# here.
+#
+def main(request, response):
+    response.add_required_headers = False
+    response.writer.write_status(200)
+    response.writer.write_header(b"access-control-allow-origin", b"*")
+    response.writer.write_header(b"content-type", b"image/svg+xml")
+    response.writer.write_header(b"cache-control",
+                                 b"no-cache; must-revalidate")
+
+    if request.headers.get(b'referer',
+                           b'').decode("utf-8").find('no-referrer') == -1:
+        body = (
+            b'<svg xmlns="http://www.w3.org/2000/svg">'
+            b'  <defs>'
+            b'    <filter id="make-solid-green" color-interpolation-filters="sRGB">'
+            b'      <feColorMatrix type="matrix" values="'
+            b'        0 0 0 0 0'
+            b'        0 0 0 0 0.502'
+            b'        0 0 0 0 0'
+            b'        0 0 0 1 0" />'
+            b'    </filter>'
+            b'  </defs>'
+            b'</svg>')
+    else:
+        body = b""
+
+    response.writer.write_header(b"content-length", len(body))
+    response.writer.end_headers()
+    response.writer.write(body)
Loading diff…

Original Bug Report

reported by ca...@gmail.com

External stylesheet SVG filters can disclose the embedding document URL despite `no-referrer`


Report description

External stylesheet SVG filters can disclose the embedding document URL despite no-referrer


Bug location

Where do you want to report your vulnerability?

Chrome VRP – Report security issues affecting the Chrome browser. See program rules

Which URL (or repository) have you found the vulnerability in?

https://chromium.googlesource.com/chromium/src/+/aac36664dea8b69305103d736d14d413af1d33e2/third_party/blink/renderer/core/css/css_uri_value.cc#20


The problem

Please describe the technical details of the vulnerability

CSSURIValue::EnsureResourceReference() drops the referrer stored in CSSUrlData when it creates an ExternalSVGResourceDocumentContent. The SVG loader applies a referrer-policy() URL modifier, but it does not set the request’s referrer string. The remaining client-referrer sentinel is therefore resolved against the embedding document instead of the external stylesheet.

This differs from ordinary CSS images. CSSImageValue::PrepareFetch() sets the request’s referrer string from CSSUrlData::GetReferrer(). Its source comment also states that this value is the stylesheet URL for an external stylesheet, including when a URL modifier overrides the policy.

An attacker can use the mismatch as follows:

  1. A victim page with a sensitive value in its path or query loads a cross-origin stylesheet controlled by the attacker.
  2. The page and its <link> both use no-referrer, so the stylesheet request does not disclose the page URL.
  3. The stylesheet applies an external SVG filter with cross-origin(anonymous) referrer-policy(unsafe-url) to an element.
  4. Chrome requests the attacker’s SVG with the full embedding document URL in the Referer header.

The attached PoC uses two loopback origins. Port 8891 is the victim and port 8892 is the stylesheet and SVG origin. The server records only synthetic request metadata.

Tested with stock Google Chrome Stable 152.0.7977.83 on Windows 11. The browser binary was C:\Program Files\Google\Chrome\Application\chrome.exe, SHA-256 CAF423E184F0BCEFE2EE5BEF40539A3C005C63BEB44E3321C0834929A13AF733.

Reproduction:

  1. Place poc.html and server.py in the same directory.
  2. Run py -3 server.py.
  3. Open http://127.0.0.1:8891/victim/DOC_SECRET?token=URL_CANARY in Chrome.
  4. Click Run complete matrix.

The page reports PASS and shows:

Case Stylesheet Referer Nested-resource Referer
SVG filter with unsafe-url none full victim URL, including URL_CANARY
image with unsafe-url none external stylesheet URL
SVG filter with no-referrer none none

The image case confirms the expected external-stylesheet referrer source. The no-referrer filter case confirms that the server is not recovering the URL through another channel. All three stylesheet requests omit Referer.

Expected behavior: the SVG-filter request should use the external stylesheet URL as its referrer source before applying the URL-level policy, as the image request does. A narrow fix is to preserve CSSUrlData::GetReferrer() when creating the external SVG resource and set that referrer string on its ResourceRequest.

Relevant source at the tested revision:

Impact analysis

The attacker prerequisite is control of a cross-origin stylesheet loaded by the victim page. This can occur for a third-party stylesheet provider or after that provider is compromised.

The observed consequence is disclosure of the full embedding document URL to the stylesheet provider even when both the document and stylesheet link use no-referrer. The path and query can contain capability URLs, reset or login tokens, share identifiers, document identifiers, or other application data. The PoC demonstrates the browser primitive with a synthetic URL_CANARY; it does not access third-party data.

No script execution, user interaction, permission, browser flag, renderer compromise, or custom build is required.


The cause

What version of Chrome have you found the security issue in?

152.0.7977.83 Stable

No, it is not related to a crash.

Choose the type of vulnerability

Information Leak

How would you like to be publicly acknowledged for your report?

caveeroo

View on issue tracker