Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Views
DescriptionUse after free in Views
ComponentViews
Bug ClassUAF
Tracker511712766
Fix commit80bd90c906e4 (chromium/src) +8/-0
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-30

Changed Functions

FunctionChangeNotes
if
ui/views/controls/menu/menu_controller.cc
modified

Files Changed

  • ui/views/controls/menu/menu_controller.cc
From 80bd90c906e400df9549fa46c90ff06a3725053a Mon Sep 17 00:00:00 2001
From: Allen Bauer <[email protected]>
Date: Tue, 12 May 2026 11:20:04 -0700
Subject: [PATCH] Check for MenuController deletion in OnMouseReleased.

In the rare, off-chance that an Accessibility tool causes the
MenuController to be deleted while in OnMouseReleased, check for that
case and exit immediately. This guards against a potential UaF of
"this".

Change-Id: Icdd4898c7ca38d00ac9839f4b8e48b86830ca92b
Bug: 511712766
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7839811
Reviewed-by: Thomas Lukaszewicz <[email protected]>
Commit-Queue: Allen Bauer <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1629464}
---

diff --git a/ui/views/controls/menu/menu_controller.cc b/ui/views/controls/menu/menu_controller.cc
index 73d04447d..30ac16b8 100644
--- a/ui/views/controls/menu/menu_controller.cc
+++ b/ui/views/controls/menu/menu_controller.cc
@@ -956,6 +956,8 @@
                                      const ui::MouseEvent& event) {
   current_mouse_pressed_state_ &= ~event.changed_button_flags();
 
+  auto this_ref = AsWeakPtr();
+
   if (current_mouse_event_target_) {
     // If this was the final mouse button, then remove the forwarding target.
     // We need to do this *before* dispatching the event to the root view
@@ -1031,6 +1033,12 @@
     // User either clicked on empty space, or a menu that has children.
     SetSelection(part.menu ? part.menu.get() : state_.item.get(),
                  SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY);
+    // On the rare, off chance that an accessibility event is fired as a result
+    // of the selection changing *and* the Accessibility tool causes the menu to
+    // be closed, `this` will be otherwise dangling. Guard against that.
+    if (!this_ref) {
+      return;
+    }
   }
   SendMouseCaptureLostToActiveView();
   MaybeForwardToAnnotation(source, event);
Loading diff…

Original Bug Report

reported by [email protected]

Potential Use-After-Free in MenuController::OnMouseReleased via AT synchronous menu closure

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 without the Chrome Security team. 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) exists in MenuController::OnMouseReleased. The MenuController can be synchronously destroyed during a call to SetSelection triggered by accessibility events. The caller function then accesses the destroyed this pointer, potentially leading to Remote Code Execution (RCE) in the browser process.

Affected files:

  • ui/views/controls/menu/menu_controller.cc

Estimated timestamp from git blame: 2026-02-10

Description

A potential Use-After-Free (UAF) vulnerability has been identified in MenuController::OnMouseReleased within ui/views/controls/menu/menu_controller.cc. The vulnerability occurs because the MenuController can be synchronously destroyed by an active Assistive Technology (AT) tool during a call to SetSelection, but the calling function continues to access its members after SetSelection returns.

Technical Details

In MenuController::OnMouseReleased, when a user releases the mouse on a menu item that has a submenu, the code executes the following:

1032:     SetSelection(part.menu ? part.menu.get() : state_.item.get(),
1033:                  SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY);
1034:   }
1035:   SendMouseCaptureLostToActiveView();
1036:   MaybeForwardToAnnotation(source, event);

The SELECTION_UPDATE_IMMEDIATELY flag causes SetSelection to execute CommitPendingSelection(), followed by accessibility updates. Specifically, at line 1760, it updates the accessibility active descendant:

1760:         submenu->GetViewAccessibility().SetActiveDescendant(*menu_item);

This call synchronously fires an accessibility event (ax::mojom::Event::kActiveDescendantChanged) to the platform. If an active accessibility tool or an attacker-controlled mock AT responds to this event by dismissing the menu (e.g., moving focus elsewhere), MenuRunnerImpl::OnMenuClosed is invoked. This function deletes the MenuController object.

While SetSelection contains several base::WeakPtr guards (if (!this_ref) return;) to protect against synchronous deletion in earlier parts of the function, there is no guard after the SetActiveDescendant call.

When the stack unwinds back to OnMouseReleased, the local this pointer is now dangling. At line 1035, the code executes SendMouseCaptureLostToActiveView(); using the freed this pointer. Inside this method:

3531:   View* active_mouse_view = active_mouse_view_tracker_->view();
...
3539:   active_mouse_view->OnMouseCaptureLost();

active_mouse_view_tracker_ is read from the freed memory. If an attacker reclaims this memory, they can forge a fake ViewTracker object that returns a fake View*. The virtual call to OnMouseCaptureLost() on this attacker-controlled pointer allows for instruction pointer hijack, leading to RCE in the highly privileged browser process.

Furthermore, this vulnerability is not mitigated by MiraclePtr (BRP). The this pointer used in OnMouseReleased is a raw stack pointer. MiraclePtr does not protect stack pointers, and since the MenuController and its owned members (like MenuPreTargetHandler) are fully destroyed without leaving any lingering raw_ptr references, the memory is successfully returned to the allocator and can be reclaimed by an attacker.

Potential Reproduction Steps

Please note: These are theoretical steps, as our analysis tooling cannot currently execute code to verify them.

  1. Register a platform Assistive Technology (AT) observer (or use an accessibility client) that listens for kActiveDescendantChanged events on menus.
  2. Configure the AT client to synchronously close or dismiss the menu upon receiving this event.
  3. Open a views-based menu in Chrome.
  4. Press the mouse and release it over a menu item that contains a submenu.
  5. The menu deletion occurs synchronously, and the subsequent call to SendMouseCaptureLostToActiveView() uses the freed this pointer, resulting in a crash or potential RCE.

Suggested Fix

To resolve this issue, add a WeakPtr survival check in MenuController::OnMouseReleased before calling methods on this.

  auto this_ref = AsWeakPtr();
  if (part.type == MenuPartType::kMenuItem) {
    SetSelection(part.menu ? part.menu.get() : state_.item.get(),
                 SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY);
  }
  if (!this_ref) return;
  SendMouseCaptureLostToActiveView();
  MaybeForwardToAnnotation(source, event);

Additionally, a similar check should be added at the end of SetSelection just after the SetActiveDescendant call to prevent any subsequent additions to that function from triggering similar UAFs.

Evaluated with Chrome root at commit: eca8648a4e1cdfdda68c495a6003059fed641955


Results 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