CVE-2026-10007
Overview
Files Changed
third_party/blink/renderer/core/svg/svg_tree_scope_resources.cc
Patch
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(
Original Bug Report
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.ccthird_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
}
- At (1),
entryis a C++ reference (UntracedMember<LocalSVGResource>&) pointing to a location inside theresources_map’s backing store. Theresources_map is aHashMapusing the defaultPartitionAllocator. - At (2),
MakeGarbageCollectedcan trigger a synchronous Oilpan garbage collection. During this GC,SVGTreeScopeResources::ProcessCustomWeaknessis executed as a weak callback. ProcessCustomWeaknessremoves dead entries fromresources_viaRemoveAll. In a standardHashMapusingPartitionAllocator, theShouldShrink()check during deletion callsPartitionAllocator::IsAllocationAllowed(), which always returnstrue. This allows the map to perform aShrink()operation during the GC weak processing phase.Shrink()allocates a new backing store and immediately deallocates the old one. This causes theentryreference inResourceForIdto become dangling.- 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:
- Use a web page to create a large number of SVG resource references (e.g., via
clip-path: url(#id)), causingSVGTreeScopeResources::resources_to grow. - Remove most of those resources from the DOM, making them eligible for garbage collection.
- Trigger a call to
ResourceForIdwith a new ID. If this call triggers a synchronous GC (e.g., by ensuring the Oilpan heap is near its limit), theProcessCustomWeaknesscallback will shrink the map. - The subsequent write/read in
ResourceForIdwill 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.