Overview

Critical
Severity
9.6
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Aura
DescriptionUse after free in Aura
ComponentAura
Bug ClassUAF
Action tierwatch — corruption primitive but gated (uncommon) — not default-reachable — revised by independent review by AI
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Independent review by AIPlausible (tier revised from patch-now)
Reviewer notes: Blocker is added after old_dispatch_target_ is cleared, not at the block top; re-entrancy is from gesture cleanup only. Commit says scenario 'should never happen'; web reachability, default preconditions and code-execution impact are unsupported by diff/advisory.
A second, independent AI reviewer (not a human) re-derived this analysis from the fixing commit and the vendor advisory, looking for errors.
Tracker517518019
Fix commit7b842f1861b4 (chromium/src) +3/-0
CWECWE-416 (Use-after-free)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H
CISA KEVNot listed
CreditedGoogle
Reported2026-05-28 (per vendor advisory)
Fix landed2026-07-21 (public git)
Disclosed2026-08-25
Patch gap35 days — fix public in source before the advisory

Background

`WindowEventDispatcher`
The Aura component that routes input events to windows and lives alongside the WindowTreeHost that owns the root window.
`OnWindowHidden`
A WindowEventDispatcher method that tears down dispatch and gesture state when a window becomes invisible, and may synchronously run code that destroys other windows.
`ScopedDeleteBlocker`
An aura::Window RAII guard that pins a window alive for its lexical scope, preventing that window from being deleted until the guard is destroyed.
`WindowTreeHost`
The host object that owns the root window (host_->window()) and the dispatcher itself, so destroying the root window destroys the dispatcher too.

Root Cause Analysis

Verify before building a PoC

This analysis is consistent with the fixing commit but not fully provable from it: part of the vulnerable path lives in code the fix commit does not touch. Confirm the trigger and the sink against the source before relying on it for exploit development.
What the commit does not establish: Blocker is added after old_dispatch_target_ is cleared, not at the block top; re-entrancy is from gesture cleanup only. Commit says scenario 'should never happen'; web reachability, default preconditions and code-execution impact are unsupported by diff/advisory.

WindowEventDispatcher::OnWindowHidden runs cleanup logic — clearing old_dispatch_target_ and unwinding gesture state — while a window is being hidden. This cleanup can synchronously re-enter application and observer code (for example, gesture-state teardown that “may end up destroying the hidden window”), and in the buggy scenario that re-entrancy can cascade all the way up to destroying the root window owned by host_. Because the root window’s destruction also destroys its WindowTreeHost and therefore the very WindowEventDispatcher executing OnWindowHidden, the method continues touching this and host_ after they have been freed, yielding a use-after-free. The invariant violated is that OnWindowHidden implicitly assumes the dispatcher and its host root window outlive the method’s own body.

The fix constructs an aura::Window::ScopedDeleteBlocker blocker(host_->window()) at the top of the affected block, pinning the root window (and transitively the host and dispatcher) alive until the method returns, so any re-entrant attempt to delete the root window is deferred past the point where this is still in use.

Key insight
The core mistake is that OnWindowHidden re-enters window-destruction code that can free the dispatcher’s own root window — and thus this — mid-method; the fix blocks root-window deletion for the method’s duration with a ScopedDeleteBlocker, guaranteeing the dispatcher survives its own cleanup.

Attack Path

  1. Trigger a hide The attacker drives page or UI state (via script-controlled navigation, focus, or window changes) that causes an Aura window to be hidden, invoking WindowEventDispatcher::OnWindowHidden.
  2. Re-enter during cleanup Inside the method, gesture-state cleanup runs application/observer callbacks synchronously, as the code notes it “may end up destroying the hidden window.”
  3. Cascade to the root window The attacker arranges the callback chain so this teardown destroys the root window owned by host_, which also tears down the WindowTreeHost and the executing WindowEventDispatcher.
  4. Use freed dispatcher Control returns into the remainder of OnWindowHidden, which dereferences the now-freed this and host_, causing a use-after-free.
  5. Exploit the dangling state By shaping heap reallocation between the free and the subsequent dereference, the attacker corrupts controlled memory to escalate toward code execution.

Impact Assessment

An attacker who can drive window visibility changes gains a use-after-free on the browser-side Aura WindowEventDispatcher/WindowTreeHost objects, which run in the privileged browser process. Successful exploitation lets them read or write freed heap memory and potentially achieve code execution outside the renderer sandbox. The precondition is reaching the re-entrant root-window-destruction scenario during OnWindowHidden, which the patch author notes “should never happen” but is nonetheless reachable.

Files Changed

  • ui/aura/window_event_dispatcher.cc

Audit Directions

  • Re-entrant self-destruction
    Look for methods that run observer or application callbacks capable of destroying the object’s own owner (host_, parent, or root window) while continuing to use this afterward.
  • Missing scoped guards
    Audit WindowEventDispatcher and other Aura teardown paths for cleanup blocks that lack a ScopedDeleteBlocker or weak-pointer check around code that “may destroy” windows.
  • Root-window lifetime assumptions
    Search for code that dereferences host_->window() or the dispatcher after synchronous teardown steps, where the root window’s continued existence is assumed rather than enforced.
From 7b842f1861b4deb1e1138eca555f56338d23b41c Mon Sep 17 00:00:00 2001
From: Mitsuru Oshima <oshima@chromium.org>
Date: Mon, 20 Jul 2026 18:54:20 -0700
Subject: [PATCH] Block the deletion of root window in OnWindowHidden

The scenario in the bug should never happen. (It would cause a
lot of other types of problems)

Fixed: 517518019
Change-Id: I5610b3b2366746a1c8511f029444f809eb5a5d39
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8126836
Reviewed-by: Achuith Bhandarkar <achuith@chromium.org>
Commit-Queue: Mitsuru Oshima <oshima@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1665117}
---
diff --git a/ui/aura/window_event_dispatcher.cc b/ui/aura/window_event_dispatcher.cc
index 002e39e2..35fd163f 100644
--- a/ui/aura/window_event_dispatcher.cc
+++ b/ui/aura/window_event_dispatcher.cc
@@ -379,6 +379,9 @@
   if (invisible->Contains(old_dispatch_target_))
     old_dispatch_target_ = nullptr;
 
+  // Block the deletion of the root window, its host thus this dispatcher.
+  aura::Window::ScopedDeleteBlocker blocker(host_->window());
+
   // Cleaning up gesture state may end up destroying the hidden window. We use a
   // weak pointer to detect this.
   base::WeakPtr<aura::Window> invisible_weak = invisible->GetWeakPtrAsWindow();
Loading diff…

Original Bug Report

reported by vm...@google.com

Potential Use-After-Free in WindowEventDispatcher::OnWindowHidden via reentrant destruction

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) vulnerability has been identified in the browser process within WindowEventDispatcher::OnWindowHidden. Synchronous gesture cleanup can trigger a nested event loop that synchronously destroys the dispatcher, leading to a UAF when member variables are accessed after the reentrant call returns.

Affected files:

  • ui/aura/window_event_dispatcher.cc

Estimated timestamp from git blame: 2013-11-26

Description

A potential Use-After-Free (UAF) vulnerability exists in ui/aura/window_event_dispatcher.cc. The function WindowEventDispatcher::OnWindowHidden invokes invisible->CleanupGestureState(). While it guards the target window with a weak pointer (invisible_weak), it lacks any self-liveness check or weak pointer guard for the WindowEventDispatcher instance itself (this).

// ui/aura/window_event_dispatcher.cc
base::WeakPtr<aura::Window> invisible_weak = invisible->GetWeakPtrAsWindow();
invisible->CleanupGestureState();  // <--- Re-enters and can synchronously free 'this'

if (reason != WINDOW_MOVING) {
  client::CaptureClient* capture_client =
      client::GetCaptureClient(host_->window());  // <--- Potential UAF read of host_
  Window* capture_window =
      capture_client ? capture_client->GetCaptureWindow() : nullptr; 
  if (!invisible_weak || invisible->Contains(event_dispatch_target_)) {
    event_dispatch_target_ = nullptr;             // <--- Potential UAF write to event_dispatch_target_
  }
  ...
}

During CleanupGestureState(), active touch points are cancelled, which can synchronously dispatch synthetic touch-cancelled events. On desktop platforms (such as Linux/Ozone/X11 and Windows), an event handler processing this event can synchronously close the top-level widget (e.g., via Widget::CloseNow()). This triggers a synchronous teardown sequence where the WindowTreeHost destroys the WindowEventDispatcher (this).

Upon return from CleanupGestureState(), this has been deallocated, but the code proceeds to dereference this->host_ and perform writes to this->event_dispatch_target_, resulting in a UAF read and write inside the privileged browser process.

Potential Trigger Steps

Note: Our analysis tool does not currently have the capability to run code or execute a live proof of concept; therefore, these are theoretical, potential steps to demonstrate reachability.

  1. A compromised renderer process establishes a cross-origin subframe and registers active touch state within its boundaries.
  2. The renderer detaches the subframe, triggering OnWindowRemovingFromRootWindow and subsequently WindowEventDispatcher::OnWindowHidden on the browser UI thread.
  3. OnWindowHidden invokes invisible->CleanupGestureState(), which generates and synchronously dispatches a synthetic touch-cancelled event.
  4. A registered event handler on the target chain processes the cancellation and synchronously calls Widget::CloseNow() on the top-level widget.
  5. The host destroys the dispatcher synchronously, returning its memory slot to PartitionAlloc.
  6. Control returns to OnWindowHidden, where the freed dispatcher memory is accessed when querying host_->window() and writing to event_dispatch_target_.

Suggested Fix

Add a self-liveness check using base::WeakPtr to ensure the WindowEventDispatcher is still valid after performing gesture cleanup:

base::WeakPtr<aura::Window> invisible_weak = invisible->GetWeakPtrAsWindow();
base::WeakPtr<WindowEventDispatcher> weak_this = weak_ptr_factory_.GetWeakPtr();
invisible->CleanupGestureState();

if (!weak_this)
  return;

Evaluated with Chrome root at commit: 5133b93d189b383c37805b1cf3a9d2dbfe8d7379


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.

View on issue tracker