CVE-2026-7910
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc |
modified | |
CloseOnActivationWidgetObserverui/views/widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc |
modified | |
DesktopWindowTreeHostPlatformTestui/views/widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc |
modified | |
VisibilityObserverui/views/widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc |
modified |
Files Changed
ui/views/widget/desktop_aura/desktop_window_tree_host_platform.ccui/views/widget/desktop_aura/desktop_window_tree_host_platform.hui/views/widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc
Patch
From 3fc1ee6676b729cd5b00c1a58bed103437c71aec Mon Sep 17 00:00:00 2001
From: Tom Anderson <thomasanderson@chromium.org>
Date: Thu, 09 Apr 2026 12:12:20 -0700
Subject: [PATCH] views: Guard against UAF in DWTHP::OnActivationChanged
HandleActivationChanged() notifications can cause the widget to be
synchronously closed. On Linux/Ozone, this can result in the destruction
of the DesktopWindowTreeHostPlatform instance.
This change adds a base::WeakPtr guard around HandleActivationChanged()
to ensure 'this' is still valid before calling ScheduleRelayout().
This mirrors an analogous fix previously applied to the Windows
implementation in hwnd_message_handler.cc.
Fixed: 497543810
Change-Id: I58fcddf8542bc1aa30d0ad5508d16849fd56e3ac
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7737952
Reviewed-by: Lei Zhang <thestig@chromium.org>
Auto-Submit: Thomas Anderson <thomasanderson@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1612417}
---
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
index 942e685..e005cce1 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
@@ -1065,7 +1065,15 @@
}
is_active_ = active;
aura::WindowTreeHostPlatform::OnActivationChanged(active);
+
+ // HandleActivationChanged() notifications can cause the widget to be
+ // synchronously closed.
+ auto weak_this = weak_factory_.GetWeakPtr();
desktop_native_widget_aura_->HandleActivationChanged(active);
+ if (!weak_this) {
+ return;
+ }
+
ScheduleRelayout();
}
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h
index 185da8cd..efca27cd 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h
@@ -273,6 +273,7 @@
base::WeakPtrFactory<DesktopWindowTreeHostPlatform> close_widget_factory_{
this};
+ base::WeakPtrFactory<DesktopWindowTreeHostPlatform> weak_factory_{this};
};
} // namespace views
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc
index 1236cb1..950938b 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc
@@ -137,6 +137,22 @@
return CreateWidgetWithNativeWidgetWithParams(std::move(params));
}
+class CloseOnActivationWidgetObserver : public WidgetObserver {
+ public:
+ explicit CloseOnActivationWidgetObserver(Widget* widget) {
+ observation_.Observe(widget);
+ }
+ ~CloseOnActivationWidgetObserver() override = default;
+
+ void OnWidgetActivationChanged(Widget* widget, bool active) override {
+ observation_.Reset();
+ widget->CloseNow();
+ }
+
+ private:
+ base::ScopedObservation<Widget, WidgetObserver> observation_{this};
+};
+
} // namespace
class DesktopWindowTreeHostPlatformTest : public ViewsTestBase {
@@ -606,6 +622,22 @@
EXPECT_TRUE(host_platform->IsActive());
}
+TEST_F(DesktopWindowTreeHostPlatformTest,
+ OnActivationChangedSurvivesSynchronousClose) {
+ std::unique_ptr<Widget> widget = CreateWidgetWithNativeWidget();
+ widget->Show();
+
+ auto* host_platform = DesktopWindowTreeHostPlatform::GetHostForWidget(
+ widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget());
+ ASSERT_TRUE(host_platform);
+
+ CloseOnActivationWidgetObserver observer(widget.get());
+
+ // This should not crash.
+ static_cast<ui::PlatformWindowDelegate*>(host_platform)
+ ->OnActivationChanged(false);
+}
+
#endif // !BUILDFLAG(IS_FUCHSIA)
class VisibilityObserver : public aura::WindowObserver {
Regression Test / PoC
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc
index 1236cb1..950938b 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc
@@ -137,6 +137,22 @@
return CreateWidgetWithNativeWidgetWithParams(std::move(params));
}
+class CloseOnActivationWidgetObserver : public WidgetObserver {
+ public:
+ explicit CloseOnActivationWidgetObserver(Widget* widget) {
+ observation_.Observe(widget);
+ }
+ ~CloseOnActivationWidgetObserver() override = default;
+
+ void OnWidgetActivationChanged(Widget* widget, bool active) override {
+ observation_.Reset();
+ widget->CloseNow();
+ }
+
+ private:
+ base::ScopedObservation<Widget, WidgetObserver> observation_{this};
+};
+
} // namespace
class DesktopWindowTreeHostPlatformTest : public ViewsTestBase {
@@ -606,6 +622,22 @@
EXPECT_TRUE(host_platform->IsActive());
}
+TEST_F(DesktopWindowTreeHostPlatformTest,
+ OnActivationChangedSurvivesSynchronousClose) {
+ std::unique_ptr<Widget> widget = CreateWidgetWithNativeWidget();
+ widget->Show();
+
+ auto* host_platform = DesktopWindowTreeHostPlatform::GetHostForWidget(
+ widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget());
+ ASSERT_TRUE(host_platform);
+
+ CloseOnActivationWidgetObserver observer(widget.get());
+
+ // This should not crash.
+ static_cast<ui::PlatformWindowDelegate*>(host_platform)
+ ->OnActivationChanged(false);
+}
+
#endif // !BUILDFLAG(IS_FUCHSIA)
class VisibilityObserver : public aura::WindowObserver {
Original Bug Report
Potential Use-After-Free in DesktopWindowTreeHostPlatform::OnActivationChanged
Project Fortify, an experimental security project, has identified the following potential security issue.
Overview: A potential Use-After-Free (UAF) vulnerability exists in DesktopWindowTreeHostPlatform::OnActivationChanged on Linux/Ozone. Synchronous widget destruction during activation handling can free the this pointer, which is subsequently accessed when calling ScheduleRelayout(). This could potentially allow a compromised renderer to achieve Remote Code Execution (RCE) in the browser process.
Affected files:
ui/views/widget/desktop_aura/desktop_window_tree_host_platform.ccui/views/widget/desktop_aura/desktop_window_tree_host_platform.h
Estimated timestamp from git blame: 2024-05-31
Summary
A potential Use-After-Free (UAF) vulnerability has been identified in the browser process on Linux (Ozone platform) within DesktopWindowTreeHostPlatform::OnActivationChanged. The issue occurs because a synchronous call to desktop_native_widget_aura_->HandleActivationChanged(active) can trigger a cascade of events that results in the destruction of the DesktopWindowTreeHostPlatform instance. Upon returning from this call, the method unconditionally invokes ScheduleRelayout() on the now-freed this pointer.
Technical Details
In ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc, OnActivationChanged is implemented as follows:
void DesktopWindowTreeHostPlatform::OnActivationChanged(bool active) {
// ...
is_active_ = active;
aura::WindowTreeHostPlatform::OnActivationChanged(active);
desktop_native_widget_aura_->HandleActivationChanged(active);
ScheduleRelayout();
}
The call to HandleActivationChanged(active) notifies WidgetObservers and focus listeners of the activation change. A specifically crafted UI interaction (or observer behavior) could respond to this notification by synchronously calling Widget::CloseNow().
This triggers the following synchronous destruction path:
Widget::CloseNow()callsnative_widget_->CloseNow(), which routes toDesktopWindowTreeHostPlatform::CloseNow().DesktopWindowTreeHostPlatform::CloseNow()callsplatform_window()->Close().- On Ozone/X11 or Wayland, the platform window synchronously notifies its delegate via
OnClosed(). DesktopWindowTreeHostPlatform::OnClosed()callsDesktopNativeWidgetAura::OnHostClosed().
Inside DesktopNativeWidgetAura::OnHostClosed() (desktop_native_widget_aura.cc:378):
// WindowEventDispatcher owns |desktop_window_tree_host_|.
desktop_window_tree_host_ = nullptr;
// Delete host after resetting `desktop_window_tree_host_` and
// `content_window_` to avoid accessing the stale instance during deletion.
host_.reset();
First, the raw_ptr tracking the host object (desktop_window_tree_host_) is set to nullptr. This explicitly drops the reference count before destruction, meaning MiraclePtr (BackupRefPtr) will not quarantine the memory for this specific pointer.
Second, host_.reset() destroys the DesktopWindowTreeHostPlatform object (this). The memory is returned to PartitionAlloc.
When the stack unwinds back to OnActivationChanged, the code unconditionally calls ScheduleRelayout() on the freed this pointer.
Inside ScheduleRelayout():
void DesktopWindowTreeHostPlatform::ScheduleRelayout() {
if (!native_widget_delegate_) {
return;
}
Widget* widget = native_widget_delegate_->AsWidget();
// ...
The code reads native_widget_delegate_ (a base::WeakPtr) from the freed memory and subsequently calls AsWidget(), a pure virtual function. If the memory has been reallocated and controlled by an attacker, this virtual function call can be hijacked.
Potential Exploitation Steps
Note: These are potential steps, as our tooling agent doesn’t yet have the ability to run code to confirm a working PoC.
- An attacker compromises a renderer process (e.g., via a v8 bug).
- The attacker uses the compromised renderer to open two popup windows (Popup A and Popup B).
- The attacker programmatically closes Popup A, causing the OS window manager (Wayland/X11) to deliver an activation change notification to Popup B’s platform window.
DesktopWindowTreeHostPlatform::OnActivationChanged(true)is invoked for Popup B.- During
HandleActivationChanged(true), a synchronous UI event or observer cascade triggersWidget::CloseNow(). - The
DesktopWindowTreeHostPlatforminstance is destroyed viahost_.reset(), and its memory is freed. - Concurrently, the attacker sprays the browser process heap via IPC messages to reallocate the freed memory chunk with attacker-controlled data.
- The attacker forges the
base::WeakPtr(native_widget_delegate_) within the sprayed memory to appear valid and points it to a fake object. - Control returns to
OnActivationChanged, andScheduleRelayout()is executed. - The forged
WeakPtrcheck passes, and the virtual methodAsWidget()is called on the attacker-controlled pointer, granting Remote Code Execution (RCE) in the browser process.
Suggested Fix
The call to ScheduleRelayout() in DesktopWindowTreeHostPlatform::OnActivationChanged must be guarded to ensure the this pointer has not been destroyed during the synchronous call to HandleActivationChanged(). A base::WeakPtr check should be used.
void DesktopWindowTreeHostPlatform::OnActivationChanged(bool active) {
// ...
is_active_ = active;
aura::WindowTreeHostPlatform::OnActivationChanged(active);
auto weak_this = close_widget_factory_.GetWeakPtr();
desktop_native_widget_aura_->HandleActivationChanged(active);
if (!weak_this) {
return;
}
ScheduleRelayout();
}
A similar fix was previously applied to the Windows equivalent (ui/views/win/hwnd_message_handler.cc) but is missing in the Linux/Ozone implementation.
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.