High watch CVSS 7.5 chrome UAF 📄 Reporter bug report 🔧 Commit mapped

Overview

High
Severity
7.5
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Views
DescriptionUse after free in Views
ComponentViews
Bug ClassUAF
Action tierwatch — corruption primitive but gated (uncommon) — not default-reachable
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker536067175
Fix commit355923849eca (chromium/src) +12/-1
CWECWE-416 (Use-after-free)
CVSS vectorCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H
CISA KEVNot listed
CreditedGoogle
Reported2026-07-17 (per vendor advisory)
Fix landed2026-07-22 (public git)
Disclosed2026-08-06
Patch gap15 days — fix public in source before the advisory

Background

`MenuItemView` / `submenu_`
A MenuItemView is one item in a Views menu; when it has a submenu, its children live in submenu_, a SubmenuView.
`EmptyMenuMenuItem`
A placeholder child (the “(empty)” item) inserted into a submenu that has no visible items, and removed again once real items appear.
`MenuController::SetSelection`
Moves the menu’s current selection, which can open or close submenus and run delegate and controller code synchronously, so menu objects can be destroyed before it returns.
`ViewTracker`
A Views helper that holds a View* and is cleared automatically when that view is destroyed, so if (tracker) tells you whether the tracked view still exists.

Root Cause Analysis

The patched code is a MenuItemView method that walks a copy of submenu_->children() to recompute visible items and remove any EmptyMenuMenuItem placeholders. The hunk header does not show the method’s name. When the placeholder being removed was selected, the old code called GetMenuController()->SetSelection(this, SELECTION_UPDATE_IMMEDIATELY) and then kept going: it used submenu_ and continued the loop. The new in-code comment states that changing the selection “can also delete this”, so the old code could touch members of a freed MenuItemView after SetSelection returned.

The fix adds a ViewTracker tracker(this) before the loop. It reads child_menu->IsSelected() into a local selected before RemoveChildViewT() detaches the child. It only calls SetSelection when tracker is still valid, and it returns immediately if tracker has been cleared by the selection change. This removes the access to the freed object on the selected-placeholder path.

Key insight
The code assumed this survives MenuController::SetSelection(), but selection changes can re-enter and destroy the calling MenuItemView. The fix tracks this with a ViewTracker and bails out as soon as it disappears.

Attack Path

  1. Open a menu with an empty submenu The user opens a browser Views menu (e.g. a context, bookmark or app menu) that has a submenu currently showing an EmptyMenuMenuItem placeholder.
  2. Select the placeholder The user moves selection onto the “(empty)” item, so IsSelected() is true for that child.
  3. Menu contents change The submenu’s model gains visible items while the menu is open, so the patched method runs to remove the placeholder (the trigger for re-running it is not shown in the diff; inferred).
  4. Selection update frees `this` Removing the selected placeholder calls MenuController::SetSelection(this, ...), which can synchronously destroy this MenuItemView (as the patch comment states). The pre-patch code then continued to use submenu_ and the loop state on the freed object, which is the use-after-free.

Impact Assessment

The UAF happens in the browser process’s menu UI, outside the renderer sandbox. Any route from it to controlled memory corruption or code execution is not shown (inferred). Reaching it needs an open browser menu with a selected EmptyMenuMenuItem, plus a contents update that removes the placeholder while selection changes tear down the item. This implies user interaction and likely a timing-dependent model change (for example, a bookmark folder or extension-driven menu entries changing while the menu is open; inferred). The diff shows no path for ordinary web content to drive this alone. The advisory gives only “Use after free in Views”, with no in-the-wild status.

Changed Functions

FunctionChangeNotes
for
ui/views/controls/menu/menu_item_view.cc
modified
if
ui/views/controls/menu/menu_item_view.cc
modified

Files Changed

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

Audit Directions

  • Post-callout use of `this` in menu code
    Throughout ui/views/controls/menu/ (MenuController, SubmenuView, MenuItemView), audit calls to SetSelection, Cancel, OpenMenu and delegate callbacks that are followed by member access. Each needs either a ViewTracker/WeakPtr guard or a guarantee that this survives.
  • Residual unguarded paths in the same loop
    In the patched code, the not-selected path after RemoveChildViewT() still reaches submenu_->InvalidateLayout() without checking tracker. Confirm that removing a child can never destroy this there; if it can, the same guard should cover that path.
  • Iterating copied child lists while mutating
    Look for other loops that copy children() and then remove or reselect views. The copy keeps iteration safe, but it does nothing to protect the owning object from re-entrant deletion.
From 355923849eca301965e0ce42a54d11308cd964a1 Mon Sep 17 00:00:00 2001
From: Dana Fried <dfried@chromium.org>
Date: Wed, 22 Jul 2026 07:53:13 -0700
Subject: [PATCH] [Menus, Views] Avoid potential UAF due to self-deletion.

Bug: 536067175
Change-Id: I107f88d2fc29a7cb5f936936acf3698b553b3af1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8127481
Reviewed-by: Dmytro Yeroshkin <yeroshkin@chromium.org>
Commit-Queue: Dmytro Yeroshkin <yeroshkin@chromium.org>
Auto-Submit: Dana Fried <dfried@chromium.org>
Commit-Queue: Dana Fried <dfried@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1666289}
---
diff --git a/ui/views/controls/menu/menu_item_view.cc b/ui/views/controls/menu/menu_item_view.cc
index 00a60ca..ee30f96d 100644
--- a/ui/views/controls/menu/menu_item_view.cc
+++ b/ui/views/controls/menu/menu_item_view.cc
@@ -64,6 +64,7 @@
 #include "ui/views/style/typography_provider.h"
 #include "ui/views/vector_icons.h"
 #include "ui/views/view_class_properties.h"
+#include "ui/views/view_tracker.h"
 #include "ui/views/view_utils.h"
 #include "ui/views/widget/widget.h"
 
@@ -1070,20 +1071,30 @@
   // visible items. Copy the children, since we may mutate them as we go.
   const Views children = submenu_->children();
   bool has_visible_menu_items = false;
+
+  // Making changes to selection, etc. can cause `this` to be deleted. Track the
+  // continued existence of this object while updating.
+  ViewTracker tracker(this);
+
   for (View* child : children) {
     MenuItemView* const child_menu = AsViewClass<MenuItemView>(child);
     if (!child_menu) {
       continue;
     }
     if (IsViewClass<EmptyMenuMenuItem>(child)) {
+      const bool selected = child_menu->IsSelected();
       // Prevent view destruction until selection is updated.
       // We remove the child before updating selection in case of re-entrancy.
       std::unique_ptr<View> removed_child = submenu_->RemoveChildViewT(child);
-      if (child_menu->IsSelected()) {
+      if (tracker && selected) {
         // Update selection to this menu before deleting the currently
         // selected child.
         GetMenuController()->SetSelection(
             this, MenuController::SELECTION_UPDATE_IMMEDIATELY);
+        // This can also delete `this`, so check for that.
+        if (!tracker) {
+          return;
+        }
       }
       submenu_
           ->InvalidateLayout();  // Ideally the submenu would have a layout
Loading diff…

Original Bug Report

reported by vm...@google.com

Potential Use-After-Free in MenuItemView::UpdateEmptyMenusAndMetrics via SetSelection Reentrancy

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 browser-process Use-After-Free (UAF) memory safety vulnerability exists in MenuItemView::UpdateEmptyMenusAndMetrics. Synchronous platform accessibility notifications triggered during MenuController::SetSelection can synchronously delete the parent MenuItemView tree. When execution resumes, accessing the unique_ptr member submenu_ of the freed MenuItemView instance leads to a heap Use-After-Free sink.

Affected files:

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

Estimated timestamp from git blame: 2025-01-27

Summary

A potential heap Use-After-Free (UAF) memory safety issue exists in MenuItemView::UpdateEmptyMenusAndMetrics inside the browser process. This is caused by executing synchronous layout and sizing logic on the unique_ptr member submenu_ of a freed MenuItemView immediately after invoking MenuController::SetSelection with SELECTION_UPDATE_IMMEDIATELY, which can synchronously delete this due to platform accessibility re-entrancy.

Vulnerability Details

Inside ui/views/controls/menu/menu_item_view.cc:1060, UpdateEmptyMenusAndMetrics() executes a loop that modifies the children of a submenu:

// ui/views/controls/menu/menu_item_view.cc:1060-1098
void MenuItemView::UpdateEmptyMenusAndMetrics() {
  CHECK(HasSubmenu());
  const Views children = submenu_->children();
  bool has_visible_menu_items = false;
  for (View* child : children) {
    MenuItemView* const child_menu = AsViewClass<MenuItemView>(child);
    ...
    if (IsViewClass<EmptyMenuMenuItem>(child)) {
      std::unique_ptr<View> removed_child = submenu_->RemoveChildViewT(child);
      if (child_menu->IsSelected()) {
        GetMenuController()->SetSelection(
            this, MenuController::SELECTION_UPDATE_IMMEDIATELY); // <-- Can trigger synchronous deletion of `this`
      }
      submenu_
          ->InvalidateLayout();  // <-- UAF: Access of `this->submenu_` after `this` is freed
    }
    ...
  }
  ...
  submenu_->UpdateMenuPartSizes(); // <-- UAF: Always reached
}

If the placeholder EmptyMenuMenuItem is currently selected, calling SetSelection fires multiple synchronous platform accessibility events (via SetHotTrackedButton(nullptr) or SetPopupFocusOverride()). When assistive technologies or screen readers are active (native platform accessibility is enabled on the browser UI thread), the platform COM or AT event loops can synchronously re-enter the Chromium UI thread and request the menu tree be closed/destroyed.

This re-entrant path executes MenuRunnerImpl::Release() setting delete_after_run_ = true, which ultimately deletes the MenuController and performs delete this; on the MenuRunnerImpl itself (see ui/views/controls/menu/menu_runner_impl.cc:235-247). Deleting MenuRunnerImpl destructs its owned menu_ (which is the root MenuItemView), resulting in cascading destruction that deletes the parent MenuItemView on which UpdateEmptyMenusAndMetrics is running.

When execution control resumes and returns from SetSelection, UpdateEmptyMenusAndMetrics immediately dereferences this->submenu_ to call InvalidateLayout(). Because submenu_ is stored as std::unique_ptr<SubmenuView> (not raw_ptr<>), it is not protected by MiraclePtr and can be loaded from a freed (or potentially attacker-reclaimed) memory region, causing a browser-process control flow hijack through subsequent virtual function calls (e.g. GetLayoutManager()->InvalidateLayout()).

Potential Attack Vector / Reproduction Flow

  1. The user launches Chromium with native platform accessibility active (e.g., NVDA, Microsoft Narrator, or Orca screen reader running).
  2. The user opens an empty nested submenu (which displays the selected (empty) placeholder item).
  3. Web content triggers a dynamic menu model mutation (e.g., using history.pushState() to fire a NavigationEntryCommitted navigation on a back/forward long-press model, or modifying an observed bookmark structure via extensions).
  4. This invokes OnMenuStructureChanged(), triggering ChildrenChanged() and subsequently UpdateEmptyMenusAndMetrics() on the active submenu item.
  5. During the placeholder removal, SetSelection(this, SELECTION_UPDATE_IMMEDIATELY) is invoked, triggering synchronous re-entrancy and closing the menu.
  6. Both the MenuController and MenuItemView tree are deleted on the stack.
  7. When SetSelection returns, the execution flow reaches the UAF sink submenu_->InvalidateLayout(), causing memory corruption.

Note: These steps are based on static analysis and automated verification of the Chromium views lifecycle, as our tooling cannot run or compile production binaries dynamically.

Suggested Fix

To resolve this issue, the execution lifetime of the MenuItemView needs to be safely guarded. The safest approach is to use a views::ViewTracker to track the liveness of this across the call to SetSelection:

      if (child_menu->IsSelected()) {
        views::ViewTracker tracker(this);
        GetMenuController()->SetSelection(
            this, MenuController::SELECTION_UPDATE_IMMEDIATELY);
        if (!tracker.view()) {
          return;
        }
      }

Evaluated with Chrome root at commit: bf775e5d75cb9e1767e2cd02cc93efa0077d14a5


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.

View on issue tracker