CVE-2026-78945
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifui/views/widget/root_view.cc |
modified |
Files Changed
ui/views/widget/root_view.cc
Patch
From e3463127992e74aadb45161c851d9642f6964336 Mon Sep 17 00:00:00 2001 From: David Yeung <[email protected]> Date: Mon, 27 Jul 2026 12:23:52 -0700 Subject: [PATCH] Use ViewTracker for mouse_pressed_handler in OnMouseReleased Replace raw pointer usage with a ViewTracker for `mouse_pressed_handler` during the `OnMouseReleased` event handling in RootView. This ensures that if the view is deleted or modified during event dispatch, safe access patterns are maintained, preventing potential dangling pointer issues and crashes. Bug: 497948894 Change-Id: I03969feffe5d6cdb2da30b8ec512d738ecfe82c1 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8143940 Reviewed-by: Allen Bauer <[email protected]> Commit-Queue: David Yeung <[email protected]> Cr-Commit-Position: refs/heads/main@{#1668880} --- diff --git a/ui/views/widget/root_view.cc b/ui/views/widget/root_view.cc index ab508f4..cc90464 100644 --- a/ui/views/widget/root_view.cc +++ b/ui/views/widget/root_view.cc @@ -616,19 +616,18 @@ mouse_pressed_handler_.get()); // We allow the view to delete us from the event dispatch callback. As such, // configure state such that we're done first, then call View. - // TODO(crbug.com/497948894): according to the suggestion, use `ViewTracker` - // instead. - raw_ptr<View, DisableDanglingPtrDetection> mouse_pressed_handler = - mouse_pressed_handler_.get(); + ViewTracker mouse_pressed_handler_tracker(mouse_pressed_handler_.get()); // During mouse event handling, `SetMouseAndGestureHandler()` may be called // to set the gesture handler. Therefore we should reset the gesture handler // when mouse is released. SetMouseAndGestureHandler(nullptr); - ui::EventDispatchDetails dispatch_details = - DispatchEvent(mouse_pressed_handler, &mouse_released); - if (dispatch_details.dispatcher_destroyed) { - return; + if (mouse_pressed_handler_tracker.view()) { + ui::EventDispatchDetails dispatch_details = + DispatchEvent(mouse_pressed_handler_tracker.view(), &mouse_released); + if (dispatch_details.dispatcher_destroyed) { + return; + } } } }
Original Bug Report
Use-After-Free in RootView::OnMouseReleased via synchronous gesture dispatch
Project Fortify, an experimental security project, has identified the following potential security issue.
Overview: A potential Use-After-Free vulnerability exists in RootView::OnMouseReleased during simultaneous mouse and touch interactions. The method caches a raw pointer to a View, prematurely drops its raw_ptr protection, and triggers synchronous gesture events that can destroy the View before the cached pointer is used.
Affected files:
ui/views/widget/root_view.ccui/views/widget/root_view.h
Estimated timestamp from git blame: 2024-12-22
Vulnerability Details
A potential Use-After-Free (UAF) vulnerability exists in ui/views/widget/root_view.cc within the RootView::OnMouseReleased method. The vulnerability allows an attacker to bypass MiraclePtr (BackupRefPtr) protections because a raw_ptr reference is cleared right before a synchronous event dispatch that can trigger object destruction.
In RootView::OnMouseReleased, the following sequence occurs:
void RootView::OnMouseReleased(const ui::MouseEvent& event) {
// ...
if (mouse_pressed_handler_) {
ui::MouseEvent mouse_released(...);
// 1. A local raw C++ pointer is created. This does NOT increment the MiraclePtr refcount.
View* mouse_pressed_handler = mouse_pressed_handler_;
// 2. This call nullifies `mouse_pressed_handler_` (the raw_ptr), dropping the MiraclePtr refcount.
// It then synchronously dispatches gesture events if a touch is active.
SetMouseAndGestureHandler(nullptr);
// 3. The raw pointer is used. If the view was destroyed in step 2, this is a UAF.
ui::EventDispatchDetails dispatch_details =
DispatchEvent(mouse_pressed_handler, &mouse_released);
// ...
}
}
Inside SetMouseAndGestureHandler(nullptr):
SetMouseHandler(nullptr)is called, which clears themouse_pressed_handler_raw_ptr. If this was the onlyraw_ptrholding the View, its BackupRefPtr count drops to 0.MaybeNotifyGestureHandlerBeforeReplacement()is called. If there is an active touch and a validgesture_handler_, it callsgesture_recognizer->SendSynthesizedEndEvents().- This synchronous event dispatch can invoke arbitrary UI logic. If the gesture handler deletes the
mouse_pressed_handlerView, the underlying memory is immediately freed and returned to the allocator, as MiraclePtr no longer observes any activeraw_ptrreferences to it. - When
SetMouseAndGestureHandlerreturns,DispatchEventis called with the danglingmouse_pressed_handlerraw pointer, leading to a UAF.
Potential Steps to Reproduce
Note: These are suggested theoretical steps based on static analysis. Our tooling agent does not currently have the capability to execute code to produce a live Proof-of-Concept.
- The user interacts with a browser UI Widget (e.g., Omnibox or a WebUI surface) using both a mouse and a touchscreen simultaneously.
- A mouse press sets
mouse_pressed_handler_toView A. - A simultaneous touch interaction sets
gesture_handler_toView B(which may be the same or a different View). - The user releases the mouse while the touch is still active, triggering
RootView::OnMouseReleased. RootViewcachesView Ain a local raw pointer and clears itsmouse_pressed_handler_raw_ptr, stripping MiraclePtr protection.RootViewsynchronously dispatches a synthesized gesture end event toView B.View B’s event handler executes logic (e.g., closing a dialog or swapping a UI panel) that destroysView A.View A’s memory is freed.RootView::OnMouseReleasedcallsDispatchEventon the danglingView Apointer, triggering the Use-After-Free.
Suggested Fix
To fix this, the code should safely track the lifetime of the mouse_pressed_handler across the synchronous call to SetMouseAndGestureHandler(). In the Views framework, this is idiomatically done using views::ViewTracker.
// Use a ViewTracker to safely observe the view's lifetime.
views::ViewTracker tracker(mouse_pressed_handler_.get());
// During mouse event handling, `SetMouseAndGestureHandler()` may be called
// to set the gesture handler. Therefore we should reset the gesture handler
// when mouse is released.
SetMouseAndGestureHandler(nullptr);
if (tracker.view()) {
ui::EventDispatchDetails dispatch_details =
DispatchEvent(tracker.view(), &mouse_released);
if (dispatch_details.dispatcher_destroyed) {
return;
}
}
Alternatively, declaring the local variable as raw_ptr<View> mouse_pressed_handler = mouse_pressed_handler_; would maintain the MiraclePtr refcount across the dispatch, turning an exploitable UAF into a safe crash, but ViewTracker is the preferred pattern here to gracefully handle View destruction.
Evaluated with Chrome root at commit: a9cbf6e8b275fe4147435aa905f3b7f5a656f5f0
Results from 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.