CVE-2026-79232
Overview
Background
- `RenderWidgetHostViewAura`
- The Aura-platform implementation of a render widget host view that owns an
aura::Windowand observes its lifecycle, including receivingOnWindowDestroyingwhen that window is torn down. - `RenderWidgetHostViewEventHandler`
- A helper object that routes Aura input events (such as
OnMouseEvent) and manages pointer locking, holding a rawwindow_pointer to the view’saura::Window. - Use-after-free (UAF)
- A memory-safety bug where code dereferences a pointer to an object that has already been destroyed, yielding undefined behavior an attacker can steer.
- Pointer lock
- A browser feature that captures the cursor to a window via
UnlockPointer/window_->GetHost()->UnlockMouse, whose teardown path dereferenceswindow_.
Root Cause Analysis
Verify before building a PoC
What the commit does not establish: Diff shows window_ cleared in OnWindowDestroying plus null checks, consistent with UAF. Claims that a page can orchestrate teardown via navigation timing, and sandbox-escape value, are stated as fact but not shown; trigger needs window destruction during pointer-lock/mouse events.
RenderWidgetHostViewEventHandler cached the view’s aura::Window in window_ but never cleared it when that window was destroyed. When RenderWidgetHostViewAura::OnWindowDestroying ran, it did not notify the event handler, leaving window_ dangling at the moment the aura::Window was freed. Any subsequent call into UnlockPointer (which read window_->GetRootWindow() and called window_->MoveCursorTo) or OnMouseEvent — for instance a synthesized event generated during window destruction — would dereference the stale window_, violating the invariant that window_ must point to a live object.
The fix makes OnWindowDestroying call event_handler_->set_window(nullptr) so the stale reference is cleared eagerly, and adds window_ null checks in UnlockPointer and an early return in OnMouseEvent, so those paths safely no-op once the window is gone.
RenderWidgetHostViewEventHandler::window_ outlived the aura::Window it pointed to because destruction was never propagated to the handler; the fix resets window_ to nullptr in OnWindowDestroying and guards every remaining window_ dereference with a null check.Attack Path
- Establish pointer lock
A malicious page requests pointer lock on the target render widget so that the
RenderWidgetHostViewEventHandlerenters themouse_locked_state and holds a livewindow_. - Trigger window destruction
The page induces teardown of the
aura::Window(for example by navigating or closing the associated view), causingRenderWidgetHostViewAura::OnWindowDestroyingto run and theaura::Windowto be freed whilewindow_still references it. - Re-enter through a synthesized event or unlock
During or after destruction, a synthesized
ui::MouseEventreachesOnMouseEvent, or the unlock teardown invokesUnlockPointer, both of which dereference the now-stalewindow_. - Achieve use-after-free
The stale
window_->GetRootWindow(),window_->GetHost(), orwindow_->MoveCursorToaccess reads freed memory, corrupting browser-process state under attacker influence.
Impact Assessment
aura::Window teardown that leaves RenderWidgetHostViewEventHandler::window_ dangling, then dereference it via a synthesized mouse event or pointer unlock to trigger a use-after-free in the privileged browser process. The precondition is reaching the pointer-lock/event path around window destruction, which a page can orchestrate through navigation or view teardown timing. Because the corruption occurs in the browser process on Aura platforms, it can undermine memory safety and potentially aid sandbox escape.Changed Functions
| Function | Change | Notes |
|---|---|---|
ifcontent/browser/renderer_host/render_widget_host_view_event_handler.cc |
modified |
Files Changed
content/browser/renderer_host/render_widget_host_view_aura.cccontent/browser/renderer_host/render_widget_host_view_event_handler.cc
Audit Directions
- Cached raw window pointersSearch other view and event-handler classes for raw
aura::Window*members that are not reset in the correspondingOnWindowDestroying/WindowObservercallbacks. - Destruction-time synthesized eventsReview input paths like
OnMouseEventand pointer-lock teardown for events or callbacks that can fire during object destruction and dereference lifecycle-bound pointers. - Missing null guards after teardown notificationAudit methods that dereference a member cleared on destruction to confirm every access is guarded once that member can legitimately be
nullptr.
Patch
From fd2f2f3bced2100c27f78f9a2fa9df92b4ea8073 Mon Sep 17 00:00:00 2001 From: Mitsuru Oshima <[email protected]> Date: Wed, 22 Jul 2026 09:18:43 -0700 Subject: [PATCH] Handle window destruction in RenderWidgetHostViewEventHandler - Reset window pointer in RenderWidgetHostViewEventHandler when RenderWidgetHostViewAura is destroying its window. - Added null checks in RenderWidgetHostViewEventHandler to handle cases where the window is destroyed. BUG=534591074 TAG=agy CONV=7731470c-3827-45f1-b883-03aa42a07d4f Change-Id: I0c1775088afc3373ae3a6093d7cdb1b3c5264193 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8127703 Reviewed-by: Colin Blundell <[email protected]> Reviewed-by: Alex Moshchuk <[email protected]> Commit-Queue: Mitsuru Oshima <[email protected]> Cr-Commit-Position: refs/heads/main@{#1666382} --- diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc index 10a8816..85835cbb 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.cc +++ b/content/browser/renderer_host/render_widget_host_view_aura.cc @@ -2387,6 +2387,8 @@ } void RenderWidgetHostViewAura::OnWindowDestroying(aura::Window* window) { + event_handler_->set_window(nullptr); + // Make sure that the input method no longer references to this object before // this object is removed from the root window (i.e. this object loses access // to the input method). diff --git a/content/browser/renderer_host/render_widget_host_view_event_handler.cc b/content/browser/renderer_host/render_widget_host_view_event_handler.cc index ae820a9..5fc3b632 100644 --- a/content/browser/renderer_host/render_widget_host_view_event_handler.cc +++ b/content/browser/renderer_host/render_widget_host_view_event_handler.cc @@ -192,13 +192,18 @@ void RenderWidgetHostViewEventHandler::UnlockPointer() { delegate_->SetTooltipsEnabled(true); - aura::Window* root_window = window_->GetRootWindow(); - if (!mouse_locked_ || !root_window) + if (!mouse_locked_) { return; + } mouse_locked_ = false; mouse_locked_unadjusted_movement_.reset(); + aura::Window* root_window = window_ ? window_->GetRootWindow() : nullptr; + if (!root_window) { + return; + } + window_->GetHost()->UnlockMouse(window_); // Ensure that the global mouse position is updated here to its original @@ -206,7 +211,9 @@ // after the cursor is moved ends up getting a large movement delta which is // not what sites expect. The delta is computed in the // ModifyEventMovementAndCoords function. - window_->MoveCursorTo(gfx::ToFlooredPoint(unlocked_mouse_position_)); + if (window_) { + window_->MoveCursorTo(gfx::ToFlooredPoint(unlocked_mouse_position_)); + } synthetic_move_position_ = gfx::ToFlooredPoint(unlocked_global_mouse_position_); @@ -318,6 +325,11 @@ void RenderWidgetHostViewEventHandler::OnMouseEvent(ui::MouseEvent* event) { TRACE_EVENT0("input", "RenderWidgetHostViewBase::OnMouseEvent"); + // A synthesized event may be generated during window destruction. + if (!window_) { + return; + } + // CrOS will send a mouse exit event to update hover state when mouse is // hidden which we want to filter out in renderer. crbug.com/723535. if (event->flags() & ui::EF_CURSOR_HIDE)
Original Bug Report
Potential Use-After-Free in WindowTreeHost::UnlockMouse and RenderWidgetHostViewEventHandler
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 exists in the browser process due to a raw window pointer being used after a synchronous capture-release call. Releasing capture can cause transient widgets to synchronously close, destroying the window, the parent window tree host, and the rendering event handler. This results in potential memory corruption when the execution resumes and accesses these freed objects.
Affected files:
ui/aura/window_tree_host.cccontent/browser/renderer_host/render_widget_host_view_event_handler.ccui/views/widget/desktop_aura/desktop_window_tree_host_linux.ccui/views/widget/desktop_aura/desktop_capture_client.ccui/aura/window_event_dispatcher.cc
Estimated timestamp from git blame: 2021-07-02
Summary
There is a potential Use-After-Free (UAF) vulnerability in WindowTreeHost::UnlockMouse and related event handlers. When window->ReleaseCapture() is called to unlock the mouse cursor, it synchronously dispatches capture change and mouse movement events. Under certain conditions (such as with transient widgets or popups designed to close upon capture loss), this dispatch can trigger synchronous widget and host destruction. When execution resumes, multiple components (WindowTreeHost, DesktopWindowTreeHostLinux, and RenderWidgetHostViewEventHandler) proceed to dereference members and execute virtual function calls on freed memory.
Root Cause Analysis
In ui/aura/window_tree_host.cc:
void WindowTreeHost::UnlockMouse(Window* window) {
Window* root_window = window->GetRootWindow();
DCHECK(root_window);
if (window->HasCapture())
window->ReleaseCapture(); // <--- Synchronous Teardown Sink
auto* cursor_client = client::GetCursorClient(root_window); // <--- UAF Read
if (cursor_client) {
cursor_client->UnlockCursor(); // <--- Potential Wild Virtual Call
cursor_client->ShowCursor(); // <--- Potential Wild Virtual Call
}
}
-
Synchronous Destruction Sink:
window->ReleaseCapture()dispatches a synchronousui::EventType::kMouseCaptureChangedevent to notify the window of capture loss. Views and widgets are explicitly permitted to self-destroy synchronously during capture loss (e.g., see the comment inWidget::OnMouseCaptureLostatui/views/widget/widget.cc:2296:// Widget may be deleted upon the capture lost event.). -
The Deallocation Chain: If the widget is synchronously closed via
CloseNow(),views::DesktopNativeWidgetAura::OnHostClosed()resets the unique pointer to the host (host_.reset();), which deletes theDesktopWindowTreeHostLinuxinstance. This in turn deletes the root window (delete window_;), which recursively deletes the childwindow_and destroys theRenderWidgetHostViewAura(and its nestedRenderWidgetHostViewEventHandlerowner). -
Post-Sink Use-After-Free Execution Paths:
- In
WindowTreeHost::UnlockMouse: Resuming on line 445,root_windowis now a dangling pointer.client::GetCursorClient(root_window)performs a property lookup on freed memory and can return the stale address ofcursor_manager_(which is deleted during root window teardown). CallingUnlockCursor()andShowCursor()executes virtual functions on a freed object. - In
DesktopWindowTreeHostLinux::UnlockMouse: Atui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc:530:If the base class call destroys the host, the subsequent calls toDesktopWindowTreeHostPlatform::UnlockMouse(window); if (SupportsMouseLock()) { // <--- UAF on `this` auto* wayland_extension = ui::GetWaylandToplevelExtension(*platform_window()); // <--- UAF on `this` wayland_extension->LockPointer(false); }SupportsMouseLock()andplatform_window()are executed on a freedthispointer. - In
RenderWidgetHostViewEventHandler::UnlockPointer: Atcontent/browser/renderer_host/render_widget_host_view_event_handler.cc:209-213:Since the event handler is deleted along withwindow_->GetHost()->UnlockMouse(window_); window_->MoveCursorTo(...); // <--- UAF Read on `this->window_` synthetic_move_position_ = ...; // <--- UAF Write to `this->synthetic_move_position_` host_->LostPointerLock(); // <--- UAF Read on `this->host_` and virtual callRenderWidgetHostViewAura, executing these lines results in UAF reads, a UAF write, and a virtual call via a deleted pointer.
- In
Potential Trigger Path
Note: These steps are logical and theoretical based on code-flow analysis; our tooling does not currently have the capability to execute and validate runtime behavior.
- A webpage or compromised renderer requests pointer lock on a transient widget/window (such as a dropdown menu or transient bubble) which has capture capabilities.
- The browser grants pointer lock, causing the Aura window to acquire capture.
- The renderer dispatches an IPC requesting to exit pointer lock.
- The browser process receives this request and invokes
RenderWidgetHostViewEventHandler::UnlockPointer(), which calls the capture-release sink. - During capture release, the transient widget processes capture loss and synchronously closes itself via
CloseNow(), freeing the host, window, and event handler. - Control unwinds, leading to the UAF reads, writes, and virtual calls detailed above.
Suggested Fixes
To prevent execution on freed pointers, lifetime tracking must be introduced at each layer of the call chain:
-
In
WindowTreeHost::UnlockMouse: Utilizeaura::WindowTrackerto track bothwindowandroot_windowbefore entering the release capture sink, and only proceed if they remain valid.void WindowTreeHost::UnlockMouse(Window* window) { Window* root_window = window->GetRootWindow(); DCHECK(root_window); aura::WindowTracker tracker; tracker.Add(window); tracker.Add(root_window); if (window->HasCapture()) window->ReleaseCapture(); if (!tracker.Contains(root_window)) return; auto* cursor_client = client::GetCursorClient(root_window); if (cursor_client) { cursor_client->UnlockCursor(); cursor_client->ShowCursor(); } } -
In
DesktopWindowTreeHostLinux::UnlockMouse: Guard the platform calls with the class’s existingweak_factory_:void DesktopWindowTreeHostLinux::UnlockMouse(aura::Window* window) { auto weak_this = weak_factory_.GetWeakPtr(); DesktopWindowTreeHostPlatform::UnlockMouse(window); if (!weak_this || !SupportsMouseLock()) return; auto* wayland_extension = ui::GetWaylandToplevelExtension(*platform_window()); wayland_extension->LockPointer(false /*enabled*/); } -
In
RenderWidgetHostViewEventHandler::UnlockPointer: Use abase::WeakPtr<RenderWidgetHostViewBase>to verify the view’s lifetime before accessing members:void RenderWidgetHostViewEventHandler::UnlockPointer() { delegate_->SetTooltipsEnabled(true); aura::Window* root_window = window_->GetRootWindow(); if (!mouse_locked_ || !root_window) return; mouse_locked_ = false; mouse_locked_unadjusted_movement_.reset(); base::WeakPtr<RenderWidgetHostViewBase> weak_view = host_view_->GetWeakPtr(); window_->GetHost()->UnlockMouse(window_); if (!weak_view) return; window_->MoveCursorTo(gfx::ToFlooredPoint(unlocked_mouse_position_)); synthetic_move_position_ = gfx::ToFlooredPoint(unlocked_global_mouse_position_); host_->LostPointerLock(); }
Evaluated with Chrome root at commit: b5b015ea5f690560237d1f0cff1405844cd12b8d
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.