CVE-2026-7976
Overview
Files Changed
ui/views/controls/menu/menu_controller.cc
Patch
From e2b78d75a6d18e68ce5c541d439e33962e493390 Mon Sep 17 00:00:00 2001
From: Takashi Sakamoto <tasak@google.com>
Date: Fri, 03 Apr 2026 01:07:02 -0700
Subject: [PATCH] Use `raw_ptr<SubmenuView>` to avoid destroying `source` while RunDragDropLoop().
Bug: 497736679
Change-Id: I40febebddc57373306e24cd93a46d4ed4bd06e53
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7719121
Reviewed-by: Keishi Hattori <keishi@chromium.org>
Commit-Queue: Takashi Sakamoto <tasak@google.com>
Owners-Override: Keishi Hattori <keishi@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1609709}
---
diff --git a/ui/views/controls/menu/menu_controller.cc b/ui/views/controls/menu/menu_controller.cc
index 1b779f7..63f140c6 100644
--- a/ui/views/controls/menu/menu_controller.cc
+++ b/ui/views/controls/menu/menu_controller.cc
@@ -1826,8 +1826,13 @@
SetSelection(part.menu, selection_types);
}
-void MenuController::StartDrag(SubmenuView* source,
+void MenuController::StartDrag(SubmenuView* source_raw,
const gfx::Point& location) {
+ // TODO(crbug.com/497736679): Intended to keep `source_raw` quarantined inside
+ // StartDrag(). Since `source_raw` might be destroyed while RunDrawDropLoop(),
+ // `source` will be sometimes dangling pointer. So detecting
+ // `source` is dangling is expected.
+ raw_ptr<SubmenuView, DisableDanglingPtrDetection> source(source_raw);
MenuItemView* item = state_.item;
DCHECK(item);
// Points are in the coordinates of the submenu, need to map to that of
Original Bug Report
Use-After-Free in MenuController::StartDrag via nested message loop
Project Fortify, an experimental security project, has identified the following potential security issue.
Overview: A potential Use-After-Free vulnerability exists in MenuController::StartDrag due to a raw pointer being held across a nested message loop. An attacker can trigger the destruction of the SubmenuView while the loop is running, bypassing MiraclePtr protections. When the loop returns, the freed pointer is dereferenced, potentially leading to a browser process sandbox escape.
Affected files:
ui/views/controls/menu/menu_controller.ccchrome/browser/ui/views/bookmarks/bookmark_menu_delegate.ccui/views/controls/menu/menu_item_view.ccui/views/controls/menu/submenu_view.ccui/views/widget/widget.cc
Estimated timestamp from git blame: 2024-11-04
Vulnerability Description
A potential Use-After-Free (UAF) vulnerability exists in the browser process within MenuController::StartDrag (ui/views/controls/menu/menu_controller.cc).
The StartDrag function takes a raw SubmenuView* source pointer. To initiate the drag-and-drop operation, it calls item->GetWidget()->RunShellDrag(...). This function enters a nested message loop (ScopedAllowApplicationTasksInNativeNestedLoop), which allows the browser to process other tasks and IPC messages while the drag is active.
If the underlying data model is modified during this nested loop (for example, a malicious extension moving or deleting the dragged bookmark’s parent folder), the UI elements including the SubmenuView are destroyed. When RunShellDrag returns, StartDrag attempts to access source->host(), dereferencing the freed SubmenuView memory.
Because source is a raw stack pointer, and all legitimate raw_ptr references are cleanly nulled out during teardown (e.g., MenuHost::submenu_ = nullptr), MiraclePtr (BackupRefPtr) reference counts drop to zero and the memory is freed to PartitionAlloc, completely bypassing BRP protections.
Potential Attack Steps
Note: These are suggested steps based on static analysis. Our tooling agent does not currently have the ability to run code to verify a working proof of concept.
- Setup: An attacker installs a malicious extension with
bookmarkspermissions. - User Interaction: The user opens a bookmark folder from the bookmarks bar and clicks and drags a bookmark to initiate a drag-and-drop operation.
- Nested Loop: The browser UI thread enters
MenuController::StartDrag. It recordshad_capture = source->host()->HasCapture()and callsRunShellDrag, entering a nested message loop. - Trigger Destruction: The malicious extension sends an asynchronous IPC (e.g.,
chrome.bookmarks.move()) to move the parent folder of the dragged item. - Teardown: The browser processes the IPC, triggering
BookmarkMenuDelegate::BookmarkNodeMoved. This deletes the parentMenuItemViewand its associatedSubmenuView(thesourcepointer). Allraw_ptrreferences are cleared, and the memory is freed. - Heap Spray: The attacker uses the extension to heap spray the browser process, reclaiming the freed
SubmenuViewmemory block and planting a fakehost_pointer. - Exploitation: The drag operation aborts/completes, and control returns to
StartDrag. The code checksshowing_ && had_capture(which remain true because the drag was active) and executessource->host()->SetCapture(nullptr). - Code Execution: The dereference uses the attacker’s fake
host_pointer (a spoofedWidget). The call toSetCapturereads a fakenative_widget_and performs a virtual method call (HasCapture()), granting the attacker a reliable primitive for a vtable hijack and Remote Code Execution (RCE) in the browser process.
Suggested Fix
Update MenuController::StartDrag to use a base::WeakPtr<SubmenuView> (or base::SafeRef if appropriate) to track the liveness of the source view across the RunShellDrag call.
Since SubmenuView already inherits a base::WeakPtrFactory, the fix can be implemented by capturing a weak pointer before the nested loop:
base::WeakPtr<SubmenuView> safe_source = source->AsWeakPtr();
item->GetWidget()->RunShellDrag(...);
// ...
if (showing_ && had_capture && safe_source) {
safe_source->host()->SetCapture(nullptr);
}
Alternatively, converting the raw pointer argument SubmenuView* source to a base::raw_ptr<SubmenuView> across the stack frame would trigger MiraclePtr protections and turn the vulnerability into a safe crash.
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.