Medium watch CVSS 9.6 chrome UAF 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
9.6
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Views
DescriptionUse after free in Views
ComponentViews
Bug ClassUAF
Action tierwatch — corruption primitive but gated (uncommon) — not default-reachable
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker497948894
Fix commite3463127992e (chromium/src) +7/-8
CWECWE-416 (Use-after-free)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H
CISA KEVNot listed
CreditedGoogle
Reported2026-03-30 (per vendor advisory)
Fix landed2026-07-27 (public git)
Disclosed2026-08-25
Patch gap29 days — fix public in source before the advisory

Background

`RootView`
the top-level views::View of a Widget. It receives native mouse and gesture events and routes them to child views, remembering the view that took a mouse press in mouse_pressed_handler_.
`mouse_pressed_handler_`
the view that handled the last mouse press. The matching mouse release is delivered to it, even if the cursor has moved.
`ViewTracker`
a Views helper that observes one View and resets its stored pointer to null when that view is destroyed. view() therefore returns either a live view or nullptr.
`raw_ptr<..., DisableDanglingPtrDetection>`
a raw_ptr with dangling-pointer detection deliberately switched off. It gives no protection if the pointee is freed.

Root Cause Analysis

According to the commit message, the vulnerable code is the mouse-release branch of RootView::OnMouseReleased in ui/views/widget/root_view.cc. The hunk itself does not show the function header. Before the patch, the code copied mouse_pressed_handler_.get() into a local raw_ptr<View, DisableDanglingPtrDetection>, then called SetMouseAndGestureHandler(nullptr), then called DispatchEvent(mouse_pressed_handler, &mouse_released) with no check that the view still existed. The removed TODO(crbug.com/497948894) comment, which suggested ViewTracker, shows the dangling risk was already known. If the pressed-handler view was destroyed between taking the pointer and dispatching, DispatchEvent received a freed View*.

The patch’s stated trigger is “if the view is deleted or modified during event dispatch”.

The patch replaces the raw local with a ViewTracker and dispatches only if (mouse_pressed_handler_tracker.view()). A view deleted before dispatch is skipped instead of used. The existing dispatcher_destroyed early return is kept, inside the new guard.

Key insight
The release path cached a raw pointer to the pressed-handler view with dangling detection turned off, and assumed the view would survive until dispatch. The fix tracks the view with ViewTracker and does not dispatch if the view has been destroyed.

Attack Path

  1. Press on a browser-UI view The user presses the mouse on a Views control in a browser window, and RootView records it as mouse_pressed_handler_.
  2. Release begins the vulnerable sequence On release, RootView caches the handler pointer and calls SetMouseAndGestureHandler(nullptr) before dispatching the release event.
  3. Handler view destroyed in the window The cached view is deleted or reparented before the release is dispatched. The commit says this can happen during event dispatch. Which concrete UI flow does this, and whether web content can drive it, is not shown in the patch (inferred).
  4. Release dispatched to freed view Before the fix, DispatchEvent was called on the stale pointer, a use-after-free in the browser process. Any controlled reuse of the freed object is (inferred).

Impact Assessment

The advisory lists this as a use-after-free in Views. The code runs in the browser process’s UI layer, which is not sandboxed, so a controlled exploit could matter more than a renderer bug (inferred). The trigger needs real mouse input from the user on browser UI, plus some way to make the pressed view be destroyed during the release sequence. The patch does not say whether web content can cause that. The vendor advisory rates this medium severity and does not report in-the-wild exploitation; NVD scores it CVSS 9.6.

Changed Functions

FunctionChangeNotes
if
ui/views/widget/root_view.cc
modified

Files Changed

  • ui/views/widget/root_view.cc

Audit Directions

  • Raw locals cached across re-entrant calls
    In root_view.cc and other event routers (View::OnMouseEvent forwarding, gesture, drag-and-drop and capture code), look for local View*/raw_ptr copies held across calls like SetMouseAndGestureHandler, DispatchEvent, focus changes or callbacks. Check whether those copies are re-validated.
  • `DisableDanglingPtrDetection` and dangling TODOs
    Search for DisableDanglingPtrDetection and TODOs that mention ViewTracker or dangling pointers in ui/views. Each one marks a known spot where a pointee may be freed while still referenced.
  • Post-dispatch use of handlers
    After DispatchEvent, only dispatcher_destroyed is checked. Audit code that keeps using handler views (mouse_move_handler_, gesture_handler_, drag_info) after dispatch without a tracker or a re-lookup.
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;
+      }
     }
   }
 }
Loading diff…

Original Bug Report

reported by [email protected]

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.cc
  • ui/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):

  1. SetMouseHandler(nullptr) is called, which clears the mouse_pressed_handler_ raw_ptr. If this was the only raw_ptr holding the View, its BackupRefPtr count drops to 0.
  2. MaybeNotifyGestureHandlerBeforeReplacement() is called. If there is an active touch and a valid gesture_handler_, it calls gesture_recognizer->SendSynthesizedEndEvents().
  3. This synchronous event dispatch can invoke arbitrary UI logic. If the gesture handler deletes the mouse_pressed_handler View, the underlying memory is immediately freed and returned to the allocator, as MiraclePtr no longer observes any active raw_ptr references to it.
  4. When SetMouseAndGestureHandler returns, DispatchEvent is called with the dangling mouse_pressed_handler raw 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.

  1. The user interacts with a browser UI Widget (e.g., Omnibox or a WebUI surface) using both a mouse and a touchscreen simultaneously.
  2. A mouse press sets mouse_pressed_handler_ to View A.
  3. A simultaneous touch interaction sets gesture_handler_ to View B (which may be the same or a different View).
  4. The user releases the mouse while the touch is still active, triggering RootView::OnMouseReleased.
  5. RootView caches View A in a local raw pointer and clears its mouse_pressed_handler_ raw_ptr, stripping MiraclePtr protection.
  6. RootView synchronously dispatches a synthesized gesture end event to View B.
  7. View B’s event handler executes logic (e.g., closing a dialog or swapping a UI panel) that destroys View A. View A’s memory is freed.
  8. RootView::OnMouseReleased calls DispatchEvent on the dangling View A pointer, 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.

View on issue tracker