Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Views
DescriptionUse after free in Views
ComponentViews
Bug ClassUAF
Tracker536067175
Fix commit355923849eca (chromium/src) +12/-1
CISA KEVNot listed
CreditedGoogle
Disclosed2026-08-06

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
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