Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in SVG
DescriptionUse after free in SVG
ComponentSVG
Bug ClassUAF
Tracker513754619
Fix commitca1a93a88114 (chromium/src) +11/-5
CISA KEVNot listed
CreditedGoogle
Disclosed2026-05-27

Files Changed

  • third_party/blink/renderer/core/svg/svg_tree_scope_resources.cc
From ca1a93a881145b48592541b57fc2c630970a9970 Mon Sep 17 00:00:00 2001
From: Fredrik Söderquist <[email protected]>
Date: Tue, 19 May 2026 09:37:47 -0700
Subject: [PATCH] Avoid shrinking the map via sync GC in SVGTreeScopeResources

Because `resources_` is a HashMap, the allocation of the new entry
can trigger GC and thus shrink the map's backing store.

Split the insert() into find()+Set() to avoid this.

Fixed: 513754619
Change-Id: I8c10cec062423bb616e2443339864fc202c78a55
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7855154
Auto-Submit: Fredrik Söderquist <[email protected]>
Commit-Queue: Philip Rogers <[email protected]>
Reviewed-by: Philip Rogers <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1632930}
---

diff --git a/third_party/blink/renderer/core/svg/svg_tree_scope_resources.cc b/third_party/blink/renderer/core/svg/svg_tree_scope_resources.cc
index 2d2ab9b48..8cb2207 100644
--- a/third_party/blink/renderer/core/svg/svg_tree_scope_resources.cc
+++ b/third_party/blink/renderer/core/svg/svg_tree_scope_resources.cc
@@ -15,12 +15,18 @@
     : tree_scope_(tree_scope) {}
 
 LocalSVGResource* SVGTreeScopeResources::ResourceForId(const AtomicString& id) {
-  if (id.empty())
+  if (id.empty()) {
     return nullptr;
-  auto& entry = resources_.insert(id, nullptr).stored_value->value;
-  if (!entry)
-    entry = MakeGarbageCollected<LocalSVGResource>(*tree_scope_, id);
-  return entry.Get();
+  }
+  auto it = resources_.find(id);
+  if (it != resources_.end()) {
+    return it->value;
+  }
+  // Use explicit Set() (rather than insert()) to avoid garbage collection
+  // shrinking the `resources_` map.
+  auto* new_entry = MakeGarbageCollected<LocalSVGResource>(*tree_scope_, id);
+  resources_.Set(id, new_entry);
+  return new_entry;
 }
 
 LocalSVGResource* SVGTreeScopeResources::ExistingResourceForId(
Loading diff…

Original Bug Report

reported by [email protected]

Potential Use-After-Free in SVGTreeScopeResources::ResourceForId

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 Use-After-Free (UAF) exists in SVGTreeScopeResources::ResourceForId because a C++ reference to a map entry is held across a garbage collection safepoint. If a synchronous GC triggers a map shrink during this safepoint, the reference becomes dangling, leading to a write and read of freed memory.

Affected files:

  • third_party/blink/renderer/core/svg/svg_tree_scope_resources.cc
  • third_party/blink/renderer/core/svg/svg_tree_scope_resources.h

Estimated timestamp from git blame: Unknown (Google3 checkout)

Summary

A potential Use-After-Free vulnerability has been identified in SVGTreeScopeResources::ResourceForId. The issue arises from maintaining a C++ reference to an element within a WTF::HashMap across an Oilpan garbage collection (GC) safepoint. In certain conditions, the map’s backing store can be reallocated and freed during the GC, leaving the reference dangling.

Vulnerability Details

In third_party/blink/renderer/core/svg/svg_tree_scope_resources.cc, the ResourceForId method is implemented as follows:

LocalSVGResource* SVGTreeScopeResources::ResourceForId(const AtomicString& id) {
  if (id.empty())
    return nullptr;
  auto& entry = resources_.insert(id, nullptr).stored_value->value;   // (1) Reference to backing store
  if (!entry)
    entry = MakeGarbageCollected<LocalSVGResource>(*tree_scope_, id); // (2) Potential synchronous GC
  return entry.Get();                                                 // (3) Use of potentially dangling reference
}
  1. At (1), entry is a C++ reference (UntracedMember<LocalSVGResource>&) pointing to a location inside the resources_ map’s backing store. The resources_ map is a HashMap using the default PartitionAllocator.
  2. At (2), MakeGarbageCollected can trigger a synchronous Oilpan garbage collection. During this GC, SVGTreeScopeResources::ProcessCustomWeakness is executed as a weak callback.
  3. ProcessCustomWeakness removes dead entries from resources_ via RemoveAll. In a standard HashMap using PartitionAllocator, the ShouldShrink() check during deletion calls PartitionAllocator::IsAllocationAllowed(), which always returns true. This allows the map to perform a Shrink() operation during the GC weak processing phase.
  4. Shrink() allocates a new backing store and immediately deallocates the old one. This causes the entry reference in ResourceForId to become dangling.
  5. At (3), the code resumes and writes the new resource pointer into the freed memory, then reads it back to return it.

Unlike HeapHashMap (which uses HeapAllocator), the use of PartitionAllocator in this context does not suppress shrinking during GC, creating the window for the UAF.

Potential Attack Scenario

While our analysis is based on code review and we have not yet developed a functional proof of concept, we suggest an attacker might follow these steps to trigger the vulnerability:

  1. Use a web page to create a large number of SVG resource references (e.g., via clip-path: url(#id)), causing SVGTreeScopeResources::resources_ to grow.
  2. Remove most of those resources from the DOM, making them eligible for garbage collection.
  3. Trigger a call to ResourceForId with a new ID. If this call triggers a synchronous GC (e.g., by ensuring the Oilpan heap is near its limit), the ProcessCustomWeakness callback will shrink the map.
  4. The subsequent write/read in ResourceForId will occur on freed PartitionAlloc memory. If this memory is reclaimed by other allocations during the GC finalization, memory corruption or remote code execution may be possible.

Suggested Fix

Avoid holding a reference to the map entry across the MakeGarbageCollected call. Instead, perform the insertion and then update the map after the allocation is complete, or store the result of insert in a temporary variable that is not a reference into the backing store.

LocalSVGResource* SVGTreeScopeResources::ResourceForId(const AtomicString& id) {
  if (id.empty())
    return nullptr;
  auto it = resources_.find(id);
  if (it != resources_.end() && it->value)
    return it->value.Get();

  auto* resource = MakeGarbageCollected<LocalSVGResource>(*tree_scope_, id);
  resources_.Set(id, resource);
  return resource;
}

Evaluated with Chrome root at commit: 1a8d40fc44df2088d5945c0bf53584038aa1614a


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.

View on issue tracker