← Chrome — 2026-W34

e404fdbb247d1a58b5e8e6ccd85d653e2528a674  [M150] Add defensive copy in CollectFlattenedAssignedNodes

severity low class UAF CVSS 8.1 signal score 4.8 third_party/blink/renderer/core/ patch-now AI review CONFIRMED
Structural signals: msg_soft (+1.0), area:third_party/blink/renderer/core/ (+2.5), surgical (+1.3)

The bug at a glance

A renderer-process use-after-free of a HeapVector backing store during slot assignment is reachable from ordinary web content via shadow DOM / slots, and the commit message confirms a working POC existed; the only mitigating factor is no committable repro and the need to drive a nested recalculation.

A page using shadow DOM with elements can force a nested slot-assignment recalculation that clears the outer slot’s assigned nodes mid-iteration, freeing the vector being iterated and giving an attacker a UAF read/write primitive in the renderer.

Root cause

CollectFlattenedAssignedNodes bound a const reference to slot.AssignedNodes() (the slot’s assigned_nodes_ HeapVector) and iterated over it. Inside the loop, flattened-assignment collection can synchronously recurse into slot-assignment recalculation, which can clear the outer slot’s assigned nodes and free/reallocate the backing vector while the reference-based iteration is still live, producing a use-after-free. The fix changes the reference to a by-value copy of the HeapVector so the iterated backing store stays valid for the whole loop regardless of nested mutation.

Key code

Reference-to-backing-store replaced with a defensive by-value copy

-  const HeapVector<Member<Node>>& assigned_nodes = slot.AssignedNodes();
+  // Copy the array - code inside the loop can modify assigned nodes.
+  const HeapVector<Member<Node>> assigned_nodes = slot.AssignedNodes();

Patch walkthrough

  • third_party/blink/renderer/core/html/html_slot_element.cc — CollectFlattenedAssignedNodes now takes a by-value copy of slot.AssignedNodes() instead of a reference, so a nested slot-assignment recalculation that clears the outer slot’s assigned nodes (freeing the original backing vector) cannot invalidate the vector being iterated, eliminating the use-after-free.

Background

HTMLSlotElement / — A shadow-DOM element that projects (assigns) light-DOM nodes into a shadow tree; its AssignedNodes() returns the HeapVector of currently assigned nodes.

Flattened assigned nodes — CollectFlattenedAssignedNodes expands nested slots to compute the flattened list of nodes a slot ultimately renders, recursing through child slot assignments.

HeapVector backing store — An Oilpan-managed contiguous array; clearing/reassigning assigned_nodes_ can free or reallocate the backing memory that an outstanding reference still points at.

Vulnerability window

  1. Reachability — Reachable from ordinary web content in a stock build via shadow DOM with elements.
  2. Trigger — Script manipulates slot assignments so that collecting the outer slot’s flattened nodes synchronously recurses into a recalculation that clears the outer slot’s assigned nodes.
  3. Primitive — The nested clear frees/reallocates the HeapVector backing store while the outer loop still iterates by reference, yielding a use-after-free read (and potential write) in the renderer.
  4. Escalation — Reclaiming the freed backing allocation with controlled objects could upgrade the UAF toward type confusion / arbitrary read-write in the renderer (inferred).

Proof of concept

Conceptually: build a shadow tree with slots so that computing an outer slot’s flattened assigned nodes triggers a synchronous slot-assignment recalculation (e.g. via mutation during the walk) that clears the outer slot’s assigned_nodes_; the in-flight reference-based loop then reads freed memory. The bug report had a local POC but no committable repro; no exploit code is provided here.

Exploitation

  1. Setup — Construct shadow roots and nested assignments that recurse during flattened-node collection (inferred).
  2. Trigger — Cause a nested recalculation to clear the outer slot’s assigned nodes mid-iteration, freeing the backing vector (inferred).
  3. Groom — Spray replacement HeapVector-sized allocations to control the reclaimed memory read/written by the dangling iteration (inferred).

Detection & hunting

For defenders and detection engineers:

  • weggli: const HeapVector<...>& x = obj.SomeNodes(); followed by a loop that can call back into code mutating the same collection — Hunt reference bindings to Blink collection getters that are iterated while synchronous DOM recalculation can run.
  • grep for AssignedNodes()/assigned_nodes_ used by reference in iteration loops — Review slot/shadow-DOM iteration sites for reentrancy that can clear the collection.

Audit directions

  • third_party/blink/renderer/core/html/html_slot_element.cc and shadow-DOM assignment code — Check every iteration over AssignedNodes()/assigned nodes for reentrant mutation during the loop.
  • Blink layout/style/DOM loops binding references to member HeapVectors — Look for const HeapVector<...>& bindings iterated while the loop body can synchronously trigger recalculation or script that mutates that vector.

Before / after

Loading diff…