Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Accessibility
DescriptionUse after free in Accessibility
ComponentAccessibility
Bug ClassUAF
Tracker521942358
Fix commit4981bdc3d0be (chromium/src) +8/-0
CISA KEVNot listed
CreditedGoogle
Disclosed2026-08-25

Changed Functions

FunctionChangeNotes
if
chrome/browser/ui/views/profiles/profile_menu_view_base.cc
modified

Files Changed

  • chrome/browser/ui/views/profiles/profile_menu_view_base.cc
From 4981bdc3d0be6ad328c35def583327b8a47fadf0 Mon Sep 17 00:00:00 2001
From: Lucas Radaelli <lucasradaelli@google.com>
Date: Thu, 16 Jul 2026 07:21:58 -0700
Subject: [PATCH] [a11y] Guard ProfileMenuViewBase owner across activation change accessibility events

In ProfileMenuViewBase::AXMenuWidgetObserver::OnWidgetActivationChanged(),
two synchronous accessibility events (kMenuStart/kMenuPopupStart and
kMenuPopupEnd/kMenuEnd) are fired back-to-back when the profile menu
widget activates or deactivates.

On Windows, firing native accessibility events can theoretically spin a
COM message pump if assistive technologies are active, allowing queued UI
tasks (like closing the bubble on deactivation) to run reentrantly and destroy
the view and observer between the two event dispatches. While there is no
guarantee that this reentrancy actually occurs in practice, there is established
precedent in the codebase for wrapping against it: autofill::PopupBaseView
uses views::ViewTracker when firing these exact same menu event pairs.

To apply the same safeguards, we wrap owner_ in a views::ViewTracker and
verify that owner_ is still alive before dispatching the second accessibility
event in both the activation and deactivation branches.

Bug: 521942358
Change-Id: I846da826e7dac4d45d105983bcd5e2fb4b1f459c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8086800
Reviewed-by: Ramin Halavati <rhalavati@chromium.org>
Commit-Queue: Lucas Radaelli <lucasradaelli@google.com>
Cr-Commit-Position: refs/heads/main@{#1663127}
---

diff --git a/chrome/browser/ui/views/profiles/profile_menu_view_base.cc b/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
index 641aa87..14954c2 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
+++ b/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
@@ -71,6 +71,7 @@
 #include "ui/views/style/typography.h"
 #include "ui/views/view.h"
 #include "ui/views/view_class_properties.h"
+#include "ui/views/view_tracker.h"
 
 #if !BUILDFLAG(IS_CHROMEOS)
 #endif  // !BUILDFLAG(IS_CHROMEOS)
@@ -397,14 +398,21 @@
   ~AXMenuWidgetObserver() override = default;
 
   void OnWidgetActivationChanged(views::Widget* widget, bool active) override {
+    views::ViewTracker tracker(owner_.get());
     if (active) {
       owner_->NotifyAccessibilityEventDeprecated(ax::mojom::Event::kMenuStart,
                                                  true);
+      if (!tracker.view()) {
+        return;
+      }
       owner_->NotifyAccessibilityEventDeprecated(
           ax::mojom::Event::kMenuPopupStart, true);
     } else {
       owner_->NotifyAccessibilityEventDeprecated(
           ax::mojom::Event::kMenuPopupEnd, true);
+      if (!tracker.view()) {
+        return;
+      }
       owner_->NotifyAccessibilityEventDeprecated(ax::mojom::Event::kMenuEnd,
                                                  true);
     }
Loading diff…

Original Bug Report

reported by vm...@google.com

Potential Use-After-Free in ProfileMenuViewBase::AXMenuWidgetObserver::OnWidgetActivationChanged

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 Use-After-Free (UAF) vulnerability exists in the browser process within ProfileMenuViewBase::AXMenuWidgetObserver::OnWidgetActivationChanged. Firing back-to-back synchronous platform accessibility events on Windows can cause reentrant UI message loop execution. If the profile menu widget is destroyed during the first event’s dispatch, the observer is freed, leading to a UAF when dispatching the subsequent event.

Affected files:

  • chrome/browser/ui/views/profiles/profile_menu_view_base.cc
  • chrome/browser/ui/views/profiles/profile_menu_view_base.h

Estimated timestamp from git blame: 2020-11-02

Root Cause Analysis

In chrome/browser/ui/views/profiles/profile_menu_view_base.cc, the nested class ProfileMenuViewBase::AXMenuWidgetObserver is responsible for observing activation changes on the profile menu bubble’s widget. When the widget’s activation state changes, OnWidgetActivationChanged is called and fires two back-to-back synchronous platform accessibility events with no liveness guard between them:

void OnWidgetActivationChanged(views::Widget* widget, bool active) override {
  if (active) {
    owner_->NotifyAccessibilityEventDeprecated(ax::mojom::Event::kMenuStart,
                                               true);
    owner_->NotifyAccessibilityEventDeprecated(
        ax::mojom::Event::kMenuPopupStart, true);
  } else {
    owner_->NotifyAccessibilityEventDeprecated(
        ax::mojom::Event::kMenuPopupEnd, true); // <--- Synchronous & Reentrant
    owner_->NotifyAccessibilityEventDeprecated(ax::mojom::Event::kMenuEnd,
                                               true); // <--- UAF here if 'this' is freed above
  }
}

The AXMenuWidgetObserver is heap-allocated and exclusively owned by a std::unique_ptr<AXMenuWidgetObserver> member in ProfileMenuViewBase (profile_menu_view_base.h:273):

std::unique_ptr<AXMenuWidgetObserver> ax_widget_observer_;

Reentrancy & Lifetime Mechanics

When owner_->NotifyAccessibilityEventDeprecated(ax::mojom::Event::kMenuPopupEnd, true) is called on Windows:

  1. It propagates through ViewAccessibility::NotifyEvent and ViewAXPlatformNodeDelegate::FireNativeEvent to AXPlatformNodeWin::NotifyAccessibilityEvent.
  2. On Windows, this fires native events using standard APIs like ::NotifyWinEvent (mapping kMenuPopupEnd to EVENT_SYSTEM_MENUPOPUPEND) and ::UiaRaiseAutomationEvent (mapping to UIA_MenuClosedEventId).
  3. Active Assistive Technology (AT) clients (like screen readers or automation hooks) intercept these events synchronously and can run nested message loops or perform synchronous COM operations.
  4. If a widget destruction or deactivation task (e.g., due to focus loss, window close, or tab-switch) runs during this reentrant pump, the widget is synchronously destroyed.
  5. During widget teardown, the associated ProfileMenuViewBase delegate is deleted. As ax_widget_observer_ is owned by ProfileMenuViewBase, its destructor is executed, deallocating the AXMenuWidgetObserver from the heap.
  6. When execution returns from the OS API event dispatch, the program attempts to evaluate the second statement (owner_->NotifyAccessibilityEventDeprecated(ax::mojom::Event::kMenuEnd, true)), dereferencing this->owner_. Since this has been deallocated, this results in a Use-After-Free (UAF) read of the owner_ pointer.

Why MiraclePtr / BackupRefPtr Does Not Mitigate This

owner_ is a raw_ptr<ProfileMenuViewBase>, but the UAF occurs because the AXMenuWidgetObserver object (this) is destroyed. The observer is owned by a std::unique_ptr and has no active raw_ptr pointing to it, meaning the heap slot is not quarantined by PartitionAlloc’s BackupRefPtr and can be immediately reallocated or overwritten.

Potential Trigger Path (Suggested)

Note: These are potential steps as our tooling does not have the ability to run or execute code.

  1. On Windows with an active screen reader or an automation hook listening to EVENT_SYSTEM_MENUPOPUPEND, the user clicks the profile avatar button to open the Profile Menu bubble.
  2. The user switches focus or changes tabs (e.g., via script or keyboard shortcut), triggering OnWidgetActivationChanged(active = false).
  3. The first event (kMenuPopupEnd) is dispatched. The Windows AT client’s callback intercepts it and synchronously pumps messages.
  4. During the message pump, the bubble-destruction task runs, synchronously destroying ProfileMenuViewBase and deleting the AXMenuWidgetObserver.
  5. Once the AT client callback returns, Chrome attempts to execute the second event dispatch and dereferences the freed AXMenuWidgetObserver to look up owner_.

Suggested Fix

To prevent the UAF, use a liveness guard to ensure that the view and the observer are still alive between accessibility event dispatches. This can be done by tracking the owner view’s lifetime with views::ViewTracker or employing the TrackAndRun utility (defined in chrome/browser/ui/views/autofill/popup/popup_view_utils.h) which is already used to resolve identical reentrancy UAFs in other Views:

void OnWidgetActivationChanged(views::Widget* widget, bool active) override {
  views::ViewTracker tracker(owner_);
  if (active) {
    owner_->NotifyAccessibilityEventDeprecated(ax::mojom::Event::kMenuStart, true);
    if (tracker.view()) {
      owner_->NotifyAccessibilityEventDeprecated(ax::mojom::Event::kMenuPopupStart, true);
    }
  } else {
    owner_->NotifyAccessibilityEventDeprecated(ax::mojom::Event::kMenuPopupEnd, true);
    if (tracker.view()) {
      owner_->NotifyAccessibilityEventDeprecated(ax::mojom::Event::kMenuEnd, true);
    }
  }
}

Evaluated with Chrome root at commit: 3947e01999a53d4e2382e39736cb79d79c7dffcf


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