CVE-2026-13783
Overview
Background
- `MenuController`
- the Views object that drives an open menu’s input and lifetime; its
OnMousePressed()handler (named in the patch’sSubject:line, not in the@@header) runs as a member method, so continued execution touchesthis. - `base::WeakPtr` / `AsWeakPtr()`
- a weak reference to
MenuController; after the pointee is destroyed theWeakPtrevaluates to false, letting a method detect that its ownthiswas freed mid-call. - `SetHotTrackedButton()`
- sets the newly hot-tracked
Buttonand fires an accessibility notification for it; per the commit message, a11y tools can respond by signaling the menu to close, which destroys theMenuController. - Reentrant destruction
- the pattern where a synchronous callback invoked deep inside a method deletes the very object whose method is still on the stack, so any later member access is a use-after-free.
Root Cause Analysis
Inside OnMousePressed(), when the pointed-at Button differs from hot_button_, the code calls SetHotTrackedButton(button), which emits an accessibility notification for the freshly hot-tracked button. As the commit message states, a11y tooling can react to that notification by closing the menu, which synchronously destroys the MenuController while OnMousePressed() is still executing. Before the patch, the method’s WeakPtr self-check (base::WeakPtr<MenuController> this_ref = AsWeakPtr();) was captured only later, inside the if (!IsViewClass<EmptyMenuMenuItem>(view)) block guarding ProcessMousePressed, so nothing detected destruction that happened during SetHotTrackedButton(); execution fell through and kept operating on the freed this.
The fix hoists the capture to auto this_ref = AsWeakPtr(); before the SetHotTrackedButton() call and adds if (!this_ref) { return true; } immediately after it, so a menu closed during hot-track notification causes an early, safe return instead of dereferencing freed memory. This works because the guard runs before any subsequent member use, and the previously redundant inner this_ref declaration is removed since the hoisted one now covers the later ProcessMousePressed path as well.
WeakPtr too late — after SetHotTrackedButton() rather than before it — leaving the window in which an accessibility-triggered menu close frees MenuController unguarded; the fix moves the capture earlier and adds an immediate if (!this_ref) return true; bailout.Attack Path
- Open a menu with a11y observing
With an accessibility tool active (that reacts to hot-track notifications), the user or attacker-influenced input opens a Views menu managed by
MenuController. - Trigger a hot-track change on mouse press
A mouse press lands over a button such that
hot_button_ != button, soOnMousePressed()enters the branch callingSetHotTrackedButton(button). - Reentrantly destroy the controller
The accessibility notification fired for the newly hot-tracked button causes the a11y tooling to signal the menu to close, synchronously destroying the
MenuControllerwhileOnMousePressed()is still on the stack. - Use the freed `this`
Pre-patch, control returns from
SetHotTrackedButton()and continues executingOnMousePressed()on the now-freedMenuController, accessing freed member state (use-after-free). - Escalate via the freed allocation
Grooming the freed
MenuControllerallocation to gain a controlled read/write or control-flow primitive, and any further escalation to code execution, is(inferred); the diff shows only the reentrant UAF, not a concrete sink or primitive.
Impact Assessment
MenuController in the browser process, reachable when an accessibility tool is active and reacts to hot-track notifications by closing the menu during a mouse press. The vendor advisory rates this critical severity and does not report in-the-wild exploitation; NVD scores it CVSS 8.8. Turning the freed-object access into a controlled memory-corruption primitive or code execution is (inferred) and not demonstrated by the patch itself.Changed Functions
| Function | Change | Notes |
|---|---|---|
ifui/views/controls/menu/menu_controller.cc |
modified |
Files Changed
ui/views/controls/menu/menu_controller.cc
Audit Directions
- Callback-before-self-check orderingLook for methods that capture an
AsWeakPtr()/weak_ptrguard after a call that can synchronously destroythis(notifications, observer callbacks,Process*handlers); the guard must be captured and checked before the first such call. - Accessibility-notification reentrancyAudit other Views input handlers (e.g., mouse/key/gesture paths in
MenuControllerand related controls) that emit a11y notifications likeSetHotTrackedButton(), since those can reenter and close/destroy the owning object mid-handler. - Fall-through after risky callsFlag handlers that continue touching member state after invoking code that “may close the menu”/destroy the object (as the removed comment warns) without an intervening liveness check on every such path.
Patch
From b2d9af47f62bf666797d3dc5a3383331a3bb62b9 Mon Sep 17 00:00:00 2001
From: Allen Bauer <kylixrd@chromium.org>
Date: Thu, 28 May 2026 12:35:18 -0700
Subject: [PATCH] Added additional `this` tracking in MenuController:OnMousePressed().
Guards against a11y tools signaling the menu to close during the
notification of the newly hot-tracked button.
Change-Id: Ida8121655c53bc1c4f59c0aaf9a25226ee73c40a
Bug: 516962178
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7882246
Auto-Submit: Allen Bauer <kylixrd@chromium.org>
Commit-Queue: Allen Bauer <kylixrd@chromium.org>
Reviewed-by: David Yeung <dayeung@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1637875}
---
diff --git a/ui/views/controls/menu/menu_controller.cc b/ui/views/controls/menu/menu_controller.cc
index 10649a65..00f21ac 100644
--- a/ui/views/controls/menu/menu_controller.cc
+++ b/ui/views/controls/menu/menu_controller.cc
@@ -907,13 +907,17 @@
View* view =
forward_to_root->GetEventHandlerForPoint(event_for_root.location());
Button* button = Button::AsButton(view);
+ auto this_ref = AsWeakPtr();
if (hot_button_ != button) {
SetHotTrackedButton(button);
}
+ if (!this_ref) {
+ return true;
+ }
+
// Empty menu items are always handled by the menu controller.
if (!IsViewClass<EmptyMenuMenuItem>(view)) {
- base::WeakPtr<MenuController> this_ref = AsWeakPtr();
bool processed = forward_to_root->ProcessMousePressed(event_for_root);
// This object may be destroyed as a result of a mouse press event (some
// item may close the menu).
Original Bug Report
Potential UAF in MenuController::OnMousePressed due to synchronous deletion in SetHotTrackedButton
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 potential Use-After-Free (UAF) vulnerability exists in the browser process inside MenuController::OnMousePressed. A call to SetHotTrackedButton can trigger synchronous menu deletion via platform-specific accessibility events before a safeguarding weak pointer is captured. This leads to subsequent operations, including a virtual method call on a stack raw pointer, executing on freed memory.
Affected files:
ui/views/controls/menu/menu_controller.cc
Estimated timestamp from git blame: 2016-03-24
Summary
A potential Use-After-Free (UAF) vulnerability has been identified in the browser process inside MenuController::OnMousePressed (located in ui/views/controls/menu/menu_controller.cc).
When a mouse press event targets a different button inside a menu, the controller invokes SetHotTrackedButton. However, this call occurs before the safeguarding AsWeakPtr() reference (this_ref) is established. Because SetHotTrackedButton dispatches synchronous accessibility events that can cause the menu widget and the controller to be deleted, returning from this call can result in executing subsequent code (including a virtual method call and a heap write) on freed memory.
Vulnerability Analysis
In ui/views/controls/menu/menu_controller.cc, inside MenuController::OnMousePressed:
Button* button = Button::AsButton(view);
if (hot_button_ != button) {
SetHotTrackedButton(button); // Line 911
}
// Empty menu items are always handled by the menu controller.
if (!IsViewClass<EmptyMenuMenuItem>(view)) { // Line 915 - Potential UAF read
base::WeakPtr<MenuController> this_ref = AsWeakPtr(); // Line 916 - Potential UAF read
bool processed = forward_to_root->ProcessMousePressed(event_for_root); // Line 917 - Potential UAF virtual call
if (!this_ref) {
return true;
}
- At line 911, the controller calls
SetHotTrackedButton(button)to update the hot-tracked button. - Within
SetHotTrackedButton(line 3796), an accessibility event is synchronously fired at line 3825:hot_button_->NotifyAccessibilityEventDeprecated(ax::mojom::Event::kSelection, true); - This notification is synchronously dispatched through platform-specific accessibility channels (e.g., using AppKit’s
NSAccessibilityPostNotificationon macOS, or GLib’sg_signal_emit_by_nameon Linux). - An external accessibility listener or client reacting to this event can synchronously trigger actions that close or dismiss the menu (e.g., destroying the widget or window).
- This synchronous destruction deletes the
MenuControllerand its associated view hierarchy (including the targetviewand the root viewforward_to_root). - Once
SetHotTrackedButtonreturns, the execution flow inOnMousePressedcontinues:- Line 915:
IsViewClass<EmptyMenuMenuItem>(view)checks if the now-freedviewis an empty menu item (UAF read). - Line 916:
AsWeakPtr()is called on the deletedMenuController(this), resulting in accessing the freedweak_ptr_factory_member (UAF read). - Line 917:
forward_to_root->ProcessMousePressed(...)executes a virtual method call on the freedMenuHostRootView(UAF virtual call). Sinceforward_to_rootis a stack-allocated raw pointer, it is not protected by MiraclePtr/BackupRefPtr, which could allow a virtual table hijack if the heap is groomed. - Line 927:
current_mouse_event_target_ = forward_to_rootwrites to a member of the deletedthis, causing a UAF heap write.
- Line 915:
Potential Attack Steps
Please note: Since our static-analysis tooling does not have the capability to execute code or run test cases, these are potential/suggested steps to reproduce the vulnerability based on code review.
- Enable system-level accessibility features (or a screen reader) to ensure platform-specific synchronous accessibility events are active.
- Open any Views-based menu containing clickable button elements (e.g., zoom/controls inside the application menu).
- Trigger a mouse-press event on a non-hot-tracked button inside the menu.
- Ensure the resulting accessibility selection notification (
ax::mojom::Event::kSelection) triggers a synchronous callback that dismisses the menu widget (such as by simulating a focus-shift or window state change inside the accessibility handler). - Observe if returning from
SetHotTrackedButtontriggers a crash due to UAF reads/writes inOnMousePressed.
Suggested Fix
To resolve this issue, the AsWeakPtr() reference must be acquired before SetHotTrackedButton is called, and checked immediately after.
--- ui/views/controls/menu/menu_controller.cc
+++ ui/views/controls/menu/menu_controller.cc
@@ -907,11 +907,15 @@
View* view =
forward_to_root->GetEventHandlerForPoint(event_for_root.location());
Button* button = Button::AsButton(view);
+ base::WeakPtr<MenuController> this_ref = AsWeakPtr();
if (hot_button_ != button) {
SetHotTrackedButton(button);
}
+ if (!this_ref) {
+ return true;
+ }
+
// Empty menu items are always handled by the menu controller.
if (!IsViewClass<EmptyMenuMenuItem>(view)) {
- base::WeakPtr<MenuController> this_ref = AsWeakPtr();
bool processed = forward_to_root->ProcessMousePressed(event_for_root);
Evaluated with Chrome root at commit: b1520ef4a76878853a31f0943b565e42060edec8
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.