Overview

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

Files Changed

  • ui/views/controls/menu/menu_controller.cc
From b721b402bb215359398430b066bdb080c78dbf0e Mon Sep 17 00:00:00 2001
From: Allen Bauer <[email protected]>
Date: Fri, 15 May 2026 07:14:06 -0700
Subject: [PATCH] Check for MenuController(this) deletion after ShowAt() call.

It is possible that the ShowAt() above can synchronously re-enter and
destroy `this` and the entire MenuItemView tree. We do a CHECK() here
instead of a early return. There are still other things up the stack
that would require additional guarding. It is also unknown what state
things would be left in should it be allowed to continue.

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

diff --git a/ui/views/controls/menu/menu_controller.cc b/ui/views/controls/menu/menu_controller.cc
index 530e10a2..10649a65 100644
--- a/ui/views/controls/menu/menu_controller.cc
+++ b/ui/views/controls/menu/menu_controller.cc
@@ -2575,7 +2575,14 @@
     } else {
       params.context = owner_;
     }
+    auto weak_this = AsWeakPtr();
     item->GetSubmenu()->ShowAt(params);
+    // It is possible that the ShowAt() above can synchronously re-enter and
+    // destroy `this` and the entire MenuItemView tree. We do a CHECK() here
+    // instead of a early return. There are still other things up the stack that
+    // would require additional guarding. It is also unknown what state things
+    // would be left in should it be allowed to continue.
+    CHECK(weak_this);
 
     // Figure out if the mouse is under the menu; if so, remember the mouse
     // location so we can ignore the first mouse move event(s) with that
Loading diff…

Original Bug Report

reported by [email protected]

Potential Use-After-Free in MenuController::OpenMenuImpl via synchronous menu destruction

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.

Overview: A potential Use-After-Free vulnerability exists in MenuController::OpenMenuImpl when opening a submenu. Synchronous OS events during the submenu’s display can cause the menu tree and controller to be destroyed while a raw pointer to the menu item is still on the stack. Resuming execution leads to virtual method calls on the freed memory, which may allow arbitrary code execution in the browser process.

Affected files:

  • ui/views/controls/menu/menu_controller.cc
  • ui/views/controls/menu/menu_runner_impl.cc
  • ui/views/controls/menu/submenu_view.cc
  • ui/views/controls/menu/menu_host.cc

Estimated timestamp from git blame: 2025-08-20

Summary

A potential Use-After-Free (UAF) vulnerability has been identified in MenuController::OpenMenuImpl (ui/views/controls/menu/menu_controller.cc). When a submenu is opened, a call to ShowAt() can synchronously pump OS events (particularly on macOS). If a window destruction or focus change event is processed during this nested loop, the entire menu tree and MenuController are deleted. Because the menu item is tracked via a raw C++ pointer on the stack, MiraclePtr protections are bypassed, and subsequent virtual method calls on the freed object can be hijacked to achieve Remote Code Execution (RCE) in the Browser Process.

Technical Details

  1. MenuController::OpenMenuImpl(MenuItemView* item, ...) receives item as a standard C++ raw pointer on the stack.
  2. The code calls item->GetSubmenu()->ShowAt(params), which propagates down to MenuHost::ShowMenuHost and calls ShowInactive().
  3. On macOS, ShowInactive() interacts with native window APIs (-[NSWindow addChildWindow:ordered:]) that can synchronously dispatch OS events like window focus loss or destruction.
  4. If an attacker triggers a window destruction or focus change at this exact moment, MenuRunnerImpl::OnMenuClosed is invoked synchronously.
  5. MenuRunnerImpl executes delete controller_.get(); (destroying the MenuController, which is this in OpenMenuImpl) and then delete this; (destroying the MenuRunnerImpl and its uniquely owned MenuItemView tree).
  6. During this teardown, all heap-based raw_ptr references to the item allocation are cleanly destroyed, causing the MiraclePtr (BackupRefPtr) reference count to drop to zero. The memory is immediately freed to the system allocator.
  7. Execution unwinds and returns to OpenMenuImpl immediately after the ShowAt() call.
  8. The code continues executing using the now-dangling item stack variable, evaluating item->GetSubmenu()->GetWidget().
  9. This performs a non-virtual fetch of the forged submenu_ pointer and invokes the virtual method GetWidget() (ui/views/view.h), allowing an attacker to hijack the control flow via a forged vtable.

Potential Steps to Trigger

Note: Our tooling agent does not currently have the ability to run code, so these are suggested steps based on static analysis, lacking a working Proof-of-Concept.

  1. An attacker serves a malicious web page that opens a Views-based menu (e.g., via a browser extension popup or custom UI element interaction).
  2. The attacker programmatically triggers a submenu to open.
  3. Racing the submenu’s ShowAt sequence, the attacker triggers an event that causes the parent window to close or lose focus (e.g., popping under another window or destroying the popup).
  4. The nested event loop processes the focus loss, synchronously destroying the menu.
  5. The attacker sprays the browser process heap to reclaim the freed MenuItemView and SubmenuView memory with forged objects containing a malicious vtable.
  6. When ShowAt returns, the browser dereferences the attacker’s vtable, achieving RCE.

Suggested Fix

To safely handle synchronous destruction, OpenMenuImpl should detect if the MenuController or the MenuItemView was destroyed during the ShowAt call.

  1. Obtain a weak pointer to the controller: base::WeakPtr<MenuController> weak_this = weak_factory_.GetWeakPtr();
  2. Use a views::ViewTracker or a base::WeakPtr<MenuItemView> (since View supports weak pointers) to track the item.
  3. After the item->GetSubmenu()->ShowAt(params) call, verify liveness:
    if (!weak_this || !weak_item) {
      return;
    }
    

Evaluated with Chrome root at commit: 096fc8fdbfacf2546485756d03f160a3d04fcc9b


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