CVE-2026-79183
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifchrome/browser/ui/views/profiles/profile_menu_view_base.cc |
modified |
Files Changed
chrome/browser/ui/views/profiles/profile_menu_view_base.cc
Patch
From 4981bdc3d0be6ad328c35def583327b8a47fadf0 Mon Sep 17 00:00:00 2001 From: Lucas Radaelli <[email protected]> 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 <[email protected]> Commit-Queue: Lucas Radaelli <[email protected]> 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); }
Original Bug Report
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.ccchrome/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:
- It propagates through
ViewAccessibility::NotifyEventandViewAXPlatformNodeDelegate::FireNativeEventtoAXPlatformNodeWin::NotifyAccessibilityEvent. - On Windows, this fires native events using standard APIs like
::NotifyWinEvent(mappingkMenuPopupEndtoEVENT_SYSTEM_MENUPOPUPEND) and::UiaRaiseAutomationEvent(mapping toUIA_MenuClosedEventId). - 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.
- 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.
- During widget teardown, the associated
ProfileMenuViewBasedelegate is deleted. Asax_widget_observer_is owned byProfileMenuViewBase, its destructor is executed, deallocating theAXMenuWidgetObserverfrom the heap. - When execution returns from the OS API event dispatch, the program attempts to evaluate the second statement (
owner_->NotifyAccessibilityEventDeprecated(ax::mojom::Event::kMenuEnd, true)), dereferencingthis->owner_. Sincethishas been deallocated, this results in a Use-After-Free (UAF) read of theowner_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.
- 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. - The user switches focus or changes tabs (e.g., via script or keyboard shortcut), triggering
OnWidgetActivationChanged(active = false). - The first event (
kMenuPopupEnd) is dispatched. The Windows AT client’s callback intercepts it and synchronously pumps messages. - During the message pump, the bubble-destruction task runs, synchronously destroying
ProfileMenuViewBaseand deleting theAXMenuWidgetObserver. - Once the AT client callback returns, Chrome attempts to execute the second event dispatch and dereferences the freed
AXMenuWidgetObserverto look upowner_.
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.