412333a8f561fdfa49a0222655fa35fe4d2ff367 [M144] Add defensive copy in CollectFlattenedAssignedNodes
The bug at a glance
The commit message and diff describe a use-after-free: CollectFlattenedAssignedNodes bound a const reference to slot.AssignedNodes() and iterated it while synchronously recursing into slot-assignment recalculation, which can clear the outer slot’s assigned_nodes_ and free the backing HeapVector mid-iteration. A renderer heap UAF reachable from web content via shadow DOM slotting is a high-severity memory-safety bug.
An attacker controlling page DOM/shadow trees can force a nested slot recalculation that frees the vector being iterated, turning normal slot-assignment code into a dangling-pointer read/iteration primitive in the renderer.
Root cause
CollectFlattenedAssignedNodes took const HeapVector<Member<Node>>& assigned_nodes = slot.AssignedNodes();, a reference aliasing the slot’s member vector assigned_nodes_. The loop body can synchronously trigger recalculation of slot assignment, which clears the outer slot’s assigned nodes and frees/reallocates the backing storage of that very vector. Continued iteration over the stale reference then dereferences freed memory (UAF). The fix changes the reference to a value copy (const HeapVector<Member<Node>> assigned_nodes = slot.AssignedNodes();), so the iterated vector is an independent snapshot whose backing stays valid for the whole loop even if the slot’s member vector is cleared.
Key code
Reference-to-member replaced with a defensive 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— Converts the aliasing reference into a value copy of the assigned-nodes vector so that reentrant slot-assignment recalculation clearing assigned_nodes_ no longer frees the storage being iterated, eliminating the use-after-free during flattened-assigned-node collection.
Background
HTMLSlotElement / assigned nodes — A in shadow DOM holds the list of light-DOM nodes assigned to it in assigned_nodes_; flattened collection recurses through nested slots.
HeapVector<Member> — An Oilpan heap-allocated vector whose backing buffer can be freed/reallocated when the vector is cleared or resized.
Reentrant slot assignment recalculation — Recalculating one slot’s assignment can synchronously alter another slot’s assigned nodes, enabling mutation during iteration.
Vulnerability window
- Reachability — Reachable by ordinary web content in a stock build using shadow DOM with slotting (including manual/named assignment), no flag or special platform required.
- Trigger — Construct nested slots so that collecting flattened assigned nodes of an outer slot recurses and clears that outer slot’s assigned nodes mid-iteration (inferred from commit description; the bug noted a POC was reproduced locally with no committable repro).
- Primitive — Iteration continues over a freed HeapVector backing buffer, yielding a use-after-free read of Member pointers.
- Escalation — Freed-slot reuse could be groomed into a controlled dangling-pointer dereference and potential renderer code execution (inferred).
Proof of concept
Conceptually, build a shadow tree with nested slots whose assigned-node sets reference each other, then mutate slot assignment so that recalculation during flattened-node collection clears the outer slot’s assigned nodes, freeing the vector the loop is still iterating. No exploit code is provided.
Exploitation
- Setup — Create shadow roots with slots arranged so flattened collection of one slot recurses into recalculation of another (inferred).
- Free — Reentrant recalculation clears the outer slot’s assigned_nodes_, releasing the HeapVector backing store (inferred).
- Use — The outstanding reference-based loop reads the freed buffer, producing the UAF.
- Control — Heap grooming to place attacker-controlled data in the freed slot could turn the dangling read into a usable primitive (inferred).
Detection & hunting
For defenders and detection engineers:
- weggli/grep for
const HeapVector<...>& x = obj.SomeAccessor();patterns followed by a loop that can recurse or run script — Hunt for references bound to container members that are iterated while reentrant code can mutate the same member; flag slot/DOM collection loops especially.
Audit directions
- Other HTMLSlotElement / shadow DOM collection and assignment loops — Look for additional loops iterating assigned_nodes_ or similar member vectors by reference while calling DidSlotChange/RecalcAssignment or other reentrant callbacks.