Overview

Critical
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Views
DescriptionUse after free in Views
ComponentViews
Bug ClassUAF
Tracker524045160
Fix commitb17402fd2c84 (chromium/src) +9/-8
CISA KEVNot listed
CreditedGoogle
Disclosed2026-07-08

Changed Functions

FunctionChangeNotes
if
ui/views/focus/focus_manager.cc
modified

Files Changed

  • ui/views/focus/focus_manager.cc
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.
   }
 }
 
Loading diff…

Original Bug Report

reported by [email protected]

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():

  1. The view is removed from the hierarchy, and all raw_ptr<View> references to it (including FocusManager::focused_view_) are cleared.
  2. Because all raw_ptr references are removed, the MiraclePtr (BackupRefPtr) reference count drops to exactly zero.
  3. PartitionAlloc genuinely frees the memory, returning it to the thread cache.
  4. Execution returns to StoreFocusedView, where the raw stack pointers focused_view and v remain 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.)

  1. 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).
  2. Trigger Focus Change: The attacker uses Javascript (e.g., window.blur()) or user interaction to forcefully deactivate the browser window. This triggers DesktopNativeWidgetAura::HandleActivationChanged(), which calls GetWidget()->GetFocusManager()->StoreFocusedView(false);.
  3. Synchronous Destruction: As focus is cleared, the transient UI element receives the Blur() signal and synchronously destroys itself and its widget.
  4. Heap Spray: The attacker utilizes web-accessible heap spraying techniques to reclaim the freed View memory chunk before the vulnerable functions resume.
  5. Execution: When StoreFocusedView resumes, it executes v->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.

View on issue tracker
Links in the report