CVE-2026-79232
Overview
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
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.