CVE-2026-13814
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifui/views/controls/menu/menu_controller.cc |
modified |
Files Changed
ui/views/controls/menu/menu_controller.cc
Patch
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);
Original Bug Report
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.
- Register a platform Assistive Technology (AT) observer (or use an accessibility client) that listens for
kActiveDescendantChangedevents on menus. - Configure the AT client to synchronously close or dismiss the menu upon receiving this event.
- Open a views-based menu in Chrome.
- Press the mouse and release it over a menu item that contains a submenu.
- The menu deletion occurs synchronously, and the subsequent call to
SendMouseCaptureLostToActiveView()uses the freedthispointer, 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.