Medium webkit UAF 🔧 Commit mapped

Overview

Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactProcessing maliciously crafted web content may lead to arbitrary code execution
DescriptionA use-after-free issue was addressed with improved memory management.
ComponentWebCore
Bug ClassUAF
Tracker322916
Fix commit91e125de4c03 (webkit) +2/-1
CISA KEVNot listed
CreditedOpenAI Codex Security - Amy Burnett, 은태 장
Disclosed2026-10-26

Background

`CSSStyleSheet::forEachStyleScope`
Iterates the tree scopes that have adopted a given CSSStyleSheet and invokes a caller-supplied callback (apply) on each one’s Style::Scope.
`m_adoptingTreeScopes`
The collection (a weak/hash set of tree scopes) stored on a CSSStyleSheet recording which TreeScopes currently adopt this stylesheet (e.g. via document.adoptedStyleSheets / shadow-root adopted stylesheets).
`Ref` / `copyToVectorOf`
Ref<T> is a non-null ref-counted smart pointer; copyToVectorOf<Ref<ContainerNode>>(collection) materializes a snapshot Vector of strong references over the collection’s current members.
WebKit lifetime model
Lifetime is managed with Ref/RefPtr/WeakPtr (not Oilpan). Iterating a container directly exposes the caller to iterator/backing-store invalidation if the callback mutates that container.

Root Cause Analysis

CSSStyleSheet::forEachStyleScope (Source/WebCore/css/CSSStyleSheet.cpp) iterated the live member collection directly: for (Ref treeScope : m_adoptingTreeScopes) apply(protect(styleScopeFor(treeScope)));. Although Ref treeScope holds a strong reference to each element it visits, the loop still iterates over m_adoptingTreeScopes’s own backing storage. The callback apply(protect(styleScopeFor(treeScope))) runs arbitrary style-engine work that can re-enter stylesheet machinery and mutate m_adoptingTreeScopes — adding or removing adopting tree scopes — while the loop is mid-iteration. Mutating the collection reallocates/rehashes its backing store, invalidating the in-progress iterator, so the loop then reads from freed hash-table storage: a use-after-free.

The patch snapshots the collection before iterating: for (const auto& treeScope : copyToVectorOf<Ref<ContainerNode>>(m_adoptingTreeScopes)) apply(protect(styleScopeFor(treeScope)));. copyToVectorOf<Ref<ContainerNode>> builds a standalone Vector of Refs — both decoupling iteration from the mutable member container (no dangling iterator if the set changes) and keeping each referenced tree scope alive for the duration of the loop. Apple’s advisory labels this a use-after-free leading to arbitrary code execution; the one-line diff is consistent with that class: the fix is the canonical “iterate a copy, not the live container” remedy for iterator-invalidation UAF.

Key insight
The loop iterated the live m_adoptingTreeScopes container while its own callback could mutate that container, so a reentrant add/remove invalidated the iterator and the loop read from freed backing storage.

Attack Path

  1. Adopt a stylesheet widely Script creates a CSSStyleSheet and adopts it into multiple tree scopes (document + shadow roots) via adoptedStyleSheets, populating m_adoptingTreeScopes with several entries.
  2. Trigger `forEachStyleScope` Cause a style operation that walks the stylesheet’s adopting scopes (e.g. mutating the stylesheet’s rules, which must propagate to every adopting scope).
  3. Reenter from the callback During the per-scope apply callback, run script/DOM work (inferred: via a reachable mutation hook) that adds or removes an adopted stylesheet, mutating m_adoptingTreeScopes and reallocating its backing store.
  4. Dangling iterator The outer loop continues from an invalidated iterator pointing into freed hash-table memory.
  5. Groom and reuse Attacker arranges a controlled allocation to occupy the freed storage.
  6. UAF primitive Continued iteration dereferences attacker-influenced freed memory as tree-scope entries, yielding a use-after-free usable toward type confusion / code execution (escalation inferred; the diff proves the invalidation, not the full exploit).

Impact Assessment

This is a renderer-process (WebCore style engine) bug reachable from unprivileged JavaScript that manipulates adopted stylesheets. The primitive is a use-after-free of the adopting-tree-scopes collection’s backing store, driven by reentrant mutation during iteration. Apple’s advisory states arbitrary code execution; the diff substantiates a UAF consistent with that, though the precise reentrancy path that mutates the collection mid-iteration is not shown by the one-line diff (inferred). No special privilege is required beyond running script on a malicious page.

Files Changed

  • Source/WebCore/css/CSSStyleSheet.cpp

Audit Directions

  • Iterate-a-copy pattern
    Grep WebCore for for (Ref ... : m_...) / for (auto& ... : m_...) loops whose body calls out to callbacks or style/DOM code that could mutate the same member collection; these are candidate iterator-invalidation UAFs.
  • Adopted-stylesheet lifecycle
    Audit other methods touching m_adoptingTreeScopes (registration/unregistration, notification walks) for reentrancy during mutation.
  • Style `Scope` notification walks
    Review other forEach/notify loops in the style engine that invoke scope callbacks while holding iterators into live containers.
diff --git a/Source/WebCore/css/CSSStyleSheet.cpp b/Source/WebCore/css/CSSStyleSheet.cpp
index 4a58423df7b8..3d4d2f2f6cb1 100644
--- a/Source/WebCore/css/CSSStyleSheet.cpp
+++ b/Source/WebCore/css/CSSStyleSheet.cpp
@@ -274,7 +274,8 @@ void CSSStyleSheet::forEachStyleScope(NOESCAPE const Function<void(Style::Scope&
         apply(*scope);
         return;
     }
-    for (Ref treeScope : m_adoptingTreeScopes)
+
+    for (const auto& treeScope : copyToVectorOf<Ref<ContainerNode>>(m_adoptingTreeScopes))
         apply(protect(styleScopeFor(treeScope)));
 }
 
Loading diff…