CVE-2026-15129
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifui/views/focus/focus_manager.cc |
modified |
Files Changed
ui/views/focus/focus_manager.cc
Patch
From b17402fd2c84ab1a58ff094c122a3c4f55c79b38 Mon Sep 17 00:00:00 2001 From: Dana Fried <[email protected]> Date: Mon, 29 Jun 2026 07:19:16 -0700 Subject: [PATCH] [Views] avoid possible UAF in FocusManager This replaces raw View* with ViewTracker in a couple of instances where it is (theoretically) possible for a View to disappear in the middle of a function, because events are potentially generated between capture and use. Fixed: 524045160 Change-Id: I4f9c42ca0e706c5042e08aa451d3944367b98a83 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8015894 Commit-Queue: Kaan Alsan <[email protected]> Commit-Queue: Dana Fried <[email protected]> Auto-Submit: Dana Fried <[email protected]> Reviewed-by: Kaan Alsan <[email protected]> Cr-Commit-Position: refs/heads/main@{#1654051} --- diff --git a/ui/views/focus/focus_manager.cc b/ui/views/focus/focus_manager.cc index 8c154ec..7bb61c0 100644 --- a/ui/views/focus/focus_manager.cc +++ b/ui/views/focus/focus_manager.cc @@ -389,11 +389,12 @@ void FocusManager::ClearFocus() { // SetFocusedView(nullptr) is going to clear out the stored view to. We need - // to persist it in this case. - views::View* focused_view = GetStoredFocusView(); + // to persist it in this case (assuming it survives the events sent out in the + // interim). + auto focused_view = std::make_unique<ViewTracker>(GetStoredFocusView()); SetFocusedView(nullptr); ClearNativeFocus(); - SetStoredFocusView(focused_view); + view_tracker_for_stored_view_ = std::move(focused_view); } void FocusManager::AdvanceFocusIfNecessary() { @@ -414,14 +415,14 @@ } void FocusManager::StoreFocusedView(bool clear_native_focus) { - View* focused_view = focused_view_; // Don't do anything if no focused view. Storing the view (which is nullptr), // in this case, would clobber the view that was previously saved. if (!focused_view_) { return; } - View* v = focused_view_; + // This could go away when sending events below, so guard access. + ViewTracker old_view(focused_view_); if (clear_native_focus) { // Temporarily disable notification. ClearFocus() will set the focus to the @@ -433,11 +434,11 @@ ClearFocus(); } else { SetFocusedView(nullptr); - SetStoredFocusView(focused_view); + SetStoredFocusView(old_view.view()); } - if (v) { - v->SchedulePaint(); // Remove focus border. + if (auto* const view = old_view.view()) { + view->SchedulePaint(); // Remove focus border. } }
Original Bug Report
Potential Heap-use-after-free in FocusManager::StoreFocusedView
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 heap-use-after-free vulnerability exists in FocusManager::StoreFocusedView and ClearFocus because raw View* pointers are cached on the stack. Operations like SetFocusedView(nullptr) can synchronously destroy the view via focus change callbacks, leaving the stack pointers dangling and bypassing MiraclePtr protections.
Affected files:
ui/views/focus/focus_manager.cc
Estimated timestamp from git blame: Unknown (Google3 checkout)
Summary
A potential heap-use-after-free (UAF) vulnerability has been identified in FocusManager::StoreFocusedView and FocusManager::ClearFocus within ui/views/focus/focus_manager.cc. These functions cache raw pointers to View objects on the stack and subsequently perform operations that can trigger the synchronous destruction of those views via focus change callbacks. Because the pointers are raw stack variables, they bypass MiraclePtr (BackupRefPtr) protections, leading to a UAF that could be leveraged for arbitrary code execution in the browser process.
Vulnerability Details
In FocusManager::StoreFocusedView, raw pointers to the currently focused view are cached on the stack (focused_view and v):
void FocusManager::StoreFocusedView(bool clear_native_focus) {
View* focused_view = focused_view_;
// ...
View* v = focused_view_;
if (clear_native_focus) {
// ...
ClearFocus();
} else {
SetFocusedView(nullptr);
SetStoredFocusView(focused_view); // <--- Potential Use-After-Free
}
if (v) {
v->SchedulePaint(); // <--- Potential Use-After-Free
}
}
A similar pattern exists in FocusManager::ClearFocus.
The vulnerability occurs because the call to SetFocusedView(nullptr) (or ClearFocus, which also sets focus to null) triggers SetFocusedViewWithReason. This method explicitly calls Blur() on the previously focused view.
In the Chromium UI framework, it is a supported and documented behavior for transient UI components (e.g., text selection handles, dropdown menus, or elements utilizing ash::SystemTextfield) to synchronously destroy themselves when losing focus. The framework anticipates this; SetFocusedViewWithReason itself uses a ViewTracker specifically to handle the view being destroyed during the Blur() call (crbug.com/1221133).
However, StoreFocusedView does not use ViewTracker for its local variables. If the view destroys itself during Blur():
- The view is removed from the hierarchy, and all
raw_ptr<View>references to it (includingFocusManager::focused_view_) are cleared. - Because all
raw_ptrreferences are removed, the MiraclePtr (BackupRefPtr) reference count drops to exactly zero. - PartitionAlloc genuinely frees the memory, returning it to the thread cache.
- Execution returns to
StoreFocusedView, where the raw stack pointersfocused_viewandvremain dangling.
Subsequently, SetStoredFocusView(focused_view) passes the dangling pointer to ViewTracker::SetView, which attempts to register an observer on the freed object. Additionally, v->SchedulePaint() calls a virtual method (GetWidget()) on the freed object.
Suggested Attacker Steps
(Note: These are potential steps based on code analysis; a working proof of concept has not been developed.)
- Setup: An attacker controls web content that triggers the display of a transient, focusable browser UI element (e.g., a specific prompt, bubble, or textfield).
- Trigger Focus Change: The attacker uses Javascript (e.g.,
window.blur()) or user interaction to forcefully deactivate the browser window. This triggersDesktopNativeWidgetAura::HandleActivationChanged(), which callsGetWidget()->GetFocusManager()->StoreFocusedView(false);. - Synchronous Destruction: As focus is cleared, the transient UI element receives the
Blur()signal and synchronously destroys itself and its widget. - Heap Spray: The attacker utilizes web-accessible heap spraying techniques to reclaim the freed
Viewmemory chunk before the vulnerable functions resume. - Execution: When
StoreFocusedViewresumes, it executesv->SchedulePaint(), invoking a virtual method on the reclaimed memory. By placing a fake vtable in the sprayed memory, the attacker can redirect execution to an arbitrary address, achieving an S0 sandbox escape.
Proposed Fix
Update StoreFocusedView and ClearFocus to use ViewTracker to track views instead of using raw stack pointers. This ensures that the local variables safely become null if the view is destroyed during synchronous callbacks, preventing the UAF.
Evaluated with Chrome root at commit: 65b3256311f3ab6fb9870eaa522de7e6dd2663bb
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.