High CVSS 8.3 chrome UAF 📄 Reporter bug report 🔧 Commit mapped

Overview

High
Severity
8.3
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in UI
DescriptionUse after free in UI
ComponentUI
Sub-componentWm
Bug ClassUAF
Tracker503873388
Fix commite086e4b3cf43 (chromium/src) +3/-1
CWECWE-416 (Use-after-free)
CVSS vectorCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:H
CISA KEVNot listed
CreditedGoogle
Reported2026-04-17 (per vendor advisory)
Fix landed2026-04-22 (public git)
Disclosed2026-05-27
Patch gap35 days — fix public in source before the advisory

Background

`transient_children_`
a std::vector-like container of aura::Window*. Its begin()/end() are raw pointers into a heap backing store; a push_back that exceeds capacity reallocates and frees that store.
`aura::Window::AddChild`
reparenting primitive that synchronously dispatches observer notifications (OnWindowHierarchyChanging, OnWindowAdded, etc.) to arbitrary registered observers before returning.
`aura::WindowTracker`
a safe-iteration helper that stores tracked windows and automatically drops any that are destroyed; Pop() removes and returns one, so mutation of the original vector during iteration cannot invalidate the walk.

Root Cause Analysis

In TransientWindowManager::OnWindowHierarchyChanged the pre-patch code reparented transient children with a range-based for loop: for (aura::Window* transient_child : transient_children_). The loop body calls new_parent->AddChild(transient_child), which synchronously fans out hierarchy-change notifications to observers. An observer that reacts by adding a transient child (wm::AddTransientChild → push_back on transient_children_) or removing one can mutate transient_children_ while the loop is mid-iteration. A range-based for caches the begin()/end() iterators once; because those iterators are raw pointers into the vector’s backing store, a capacity-exceeding push_back reallocates the store, frees the old buffer, and leaves the cached iterators dangling. The next loop increment/dereference touches freed heap memory — a use-after-free on the vector backing store in the browser process.

The patch replaces the range-based for with aura::WindowTracker tracker(transient_children_) and a while (!tracker.windows().empty()) { aura::Window* transient_child = tracker.Pop(); ... } loop. The tracker snapshots the window set and is robust to concurrent add/remove during the synchronous callbacks, so no stale pointer into the live transient_children_ buffer is ever dereferenced. This mirrors the already-safe iteration pattern used elsewhere in the same class (OnWindowVisibilityChanged, OnWindowDestroying).

Key insight
Iterating a mutable vector by cached raw-pointer iterators while synchronously invoking reentrant observer callbacks that can reallocate that vector — the callbacks free the buffer out from under the loop.

Attack Path

  1. Set up transient children Via web/extension UI surfaces an attacker causes several transient windows (popups, PiP) to be created so transient_children_ sits exactly at its allocated capacity (inferred: the exact capacity is not shown by the diff).
  2. Register a reentrant observer Rely on an existing UI observer that reacts to hierarchy-change notifications by creating/destroying a transient child.
  3. Trigger reparenting Cause an action that reaches OnWindowHierarchyChanged (e.g. a fullscreen/reparent transition), entering the vulnerable loop.
  4. Reallocate during callback The in-loop AddChild fires the observer, which push_backs a new transient child; capacity is exceeded, the backing store is reallocated and the old buffer freed.
  5. Dereference dangling iterator The loop advances over the freed buffer, reading an aura::Window* from freed memory.
  6. Escalate (inferred) If the attacker reclaims the freed buffer with controlled data (e.g. IPC heap spray) and plants a fake aura::Window, a subsequent virtual call could yield browser-process code execution. Reclaim and control are not shown by the diff.

Impact Assessment

The bug is in the browser process (aura window manager), which is unsandboxed, so a reliable exploit here is a sandbox-escape-grade primitive. No special privilege is required beyond the ability to open and reparent transient windows, but exploitation is race-/layout-sensitive (CVSS AC:H, UI:R): the attacker must land the reallocation precisely inside the synchronous callback and win reclaim of the freed buffer. The diff proves the UAF read; conversion to code execution is (inferred) and not demonstrated by the patch.

Changed Functions

FunctionChangeNotes
for
ui/wm/core/transient_window_manager.cc
modified

Files Changed

  • ui/wm/core/transient_window_manager.cc

Audit Directions

  • Other `transient_children_` / child-list iterations
    Audit the remaining loops in transient_window_manager.cc (and TransientWindowController) that iterate child vectors while calling into AddChild/RemoveChild/StackChild, which all dispatch synchronous observer callbacks.
  • Range-based `for` over observer-visible containers
    Look across ui/wm, ui/aura, and ui/views for range-based loops over member vectors whose bodies dispatch observer notifications; these are the exact shape vulnerable to reentrant reallocation.
  • WindowTracker adoption
    Confirm every reparent/restack path that can trigger observer reentrancy uses WindowTracker (or an index-revalidating loop), not cached iterators.
From e086e4b3cf43f80c7bd3c24dd6d2ee886f772da0 Mon Sep 17 00:00:00 2001
From: Andrew Paseltiner <apaseltiner@chromium.org>
Date: Wed, 22 Apr 2026 05:10:28 -0700
Subject: [PATCH] wm: Fix UAF in TransientWindowManager::OnWindowHierarchyChanged

This CL replaces a range-based for loop with aura::WindowTracker when
iterating over transient_children_ in OnWindowHierarchyChanged.

The previous implementation was vulnerable to iterator invalidation if
synchronous observer callbacks triggered by AddChild mutated the
transient_children_ vector (e.g. by adding or removing transient
children).

Fixed: 503873388
Change-Id: I1cf130836e7922ca5ea7ffb682051bb55653611a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7782048
Commit-Queue: Andrew Paseltiner <apaseltiner@chromium.org>
Reviewed-by: Xiaoqian Dai <xdai@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1618785}
---

diff --git a/ui/wm/core/transient_window_manager.cc b/ui/wm/core/transient_window_manager.cc
index 6bc770d..ef261c6 100644
--- a/ui/wm/core/transient_window_manager.cc
+++ b/ui/wm/core/transient_window_manager.cc
@@ -198,7 +198,9 @@
     base::WeakAutoReset reset(
         weak_factory_.GetWeakPtr(),
         &TransientWindowManager::pause_transient_descendants_restacking_, true);
-    for (aura::Window* transient_child : transient_children_) {
+    aura::WindowTracker tracker(transient_children_);
+    while (!tracker.windows().empty()) {
+      aura::Window* transient_child = tracker.Pop();
       if (transient_child->parent() == old_parent) {
         new_parent->AddChild(transient_child);
         should_restack = true;
Loading diff…

Original Bug Report

reported by rj...@google.com

Potential Iterator Invalidation UAF in TransientWindowManager::OnWindowHierarchyChanged

Flapjack, 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 without the Chrome Security team. 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 TransientWindowManager::OnWindowHierarchyChanged due to iterator invalidation. Synchronous observer callbacks fired during a range-based for loop over transient_children_ can mutate the vector, invalidating iterators and potentially leading to a browser process UAF.

Affected files:

  • ui/wm/core/transient_window_manager.cc

Estimated timestamp from git blame: 2023-12-20

Summary

A potential Use-After-Free vulnerability was identified in TransientWindowManager::OnWindowHierarchyChanged (ui/wm/core/transient_window_manager.cc). The method iterates over the transient_children_ vector while performing operations that trigger synchronous window observer callbacks. If an observer mutates the window hierarchy, it can modify the transient_children_ vector, invalidating the loop’s iterators and causing a UAF when the loop advances.

Technical Details

In TransientWindowManager::OnWindowHierarchyChanged, the code uses a range-based for loop to reparent transient children:

// ui/wm/core/transient_window_manager.cc:201
for (aura::Window* transient_child : transient_children_) {
  if (transient_child->parent() == old_parent) {
    new_parent->AddChild(transient_child);
    should_restack = true;
  }
}

The call to new_parent->AddChild(transient_child) inside the loop is highly complex and synchronously dispatches several observer notifications, such as OnWindowHierarchyChanging and OnWindowAdded, to observers across the UI.

If any registered observer reacts to these notifications by altering the transient children of this window (e.g., by creating a new transient UI element which calls wm::AddTransientChild, or destroying an existing transient child which calls wm::RemoveTransientChild), the transient_children_ vector is mutated.

Specifically, if push_back is called and the vector’s capacity is exceeded, its backing store is reallocated and the old buffer is freed. Because the range-based for loop caches the begin() and end() iterators (which are raw pointers to the vector’s backing store), the reallocation invalidates them.

When the loop attempts to advance, it dereferences a dangling iterator, resulting in a Use-After-Free on the freed vector backing store.

Potential Attack Scenario

While our tooling agent does not have the ability to run code to confirm exploitability, an attacker might trigger this by:

  1. Using Web or Extension APIs to create multiple transient windows (e.g., PiP windows or popups) such that the transient_children_ vector is exactly at its allocated capacity.
  2. Triggering a window reparenting action (e.g., entering fullscreen) that invokes OnWindowHierarchyChanged.
  3. Relying on an existing UI observer to react to the reparenting by synchronously adding a new transient child.
  4. The vector reallocation frees the backing store, which the attacker reclaims using memory manipulation primitives (e.g., IPC sprays).
  5. The attacker places forged raw_ptr objects in the reclaimed backing store. When the loop dereferences these forged pointers, it accesses attacker-controlled fake aura::Window objects, potentially leading to virtual method calls and arbitrary code execution in the Browser process (Sandbox Escape).

To safely iterate over transient_children_ while permitting synchronous hierarchy modifications, aura::WindowTracker should be used. This pattern handles windows being added or removed during iteration and is already used safely elsewhere in the same class (e.g., OnWindowVisibilityChanged and OnWindowDestroying).

// Reparenting multiple sibling transient children will call back onto us...
base::WeakAutoReset reset(
    weak_factory_.GetWeakPtr(),
    &TransientWindowManager::pause_transient_descendants_restacking_, true);

aura::WindowTracker tracker(transient_children_);
while (!tracker.windows().empty()) {
  aura::Window* transient_child = tracker.Pop();
  if (transient_child->parent() == old_parent) {
    new_parent->AddChild(transient_child);
    should_restack = true;
  }
}

Evaluated with Chrome root at commit: c0eb5541aebfa4ea08806eaf6e94bcc69f87ab2f


Results so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; 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