Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Views
DescriptionUse after free in Views
ComponentViews
Bug ClassUAF
Tracker497543810
Fix commit3fc1ee6676b7 (chromium/src) +41/-0
CISA KEVNot listed
CreditedGoogle
Disclosed2026-05-05

Changed Functions

FunctionChangeNotes
if
ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
modified
CloseOnActivationWidgetObserver
ui/views/widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc
modified
DesktopWindowTreeHostPlatformTest
ui/views/widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc
modified
VisibilityObserver
ui/views/widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc
modified

Files Changed

  • ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
  • ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h
  • ui/views/widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc
From 3fc1ee6676b729cd5b00c1a58bed103437c71aec Mon Sep 17 00:00:00 2001
From: Tom Anderson <[email protected]>
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 <[email protected]>
Auto-Submit: Thomas Anderson <[email protected]>
Commit-Queue: Lei Zhang <[email protected]>
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 {
Loading diff…

Regression Test / PoC

shipped with the fix
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 {
Loading diff…

Original Bug Report

reported by [email protected]

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.cc
  • ui/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:

  1. Widget::CloseNow() calls native_widget_->CloseNow(), which routes to DesktopWindowTreeHostPlatform::CloseNow().
  2. DesktopWindowTreeHostPlatform::CloseNow() calls platform_window()->Close().
  3. On Ozone/X11 or Wayland, the platform window synchronously notifies its delegate via OnClosed().
  4. DesktopWindowTreeHostPlatform::OnClosed() calls DesktopNativeWidgetAura::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.

  1. An attacker compromises a renderer process (e.g., via a v8 bug).
  2. The attacker uses the compromised renderer to open two popup windows (Popup A and Popup B).
  3. 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.
  4. DesktopWindowTreeHostPlatform::OnActivationChanged(true) is invoked for Popup B.
  5. During HandleActivationChanged(true), a synchronous UI event or observer cascade triggers Widget::CloseNow().
  6. The DesktopWindowTreeHostPlatform instance is destroyed via host_.reset(), and its memory is freed.
  7. Concurrently, the attacker sprays the browser process heap via IPC messages to reallocate the freed memory chunk with attacker-controlled data.
  8. The attacker forges the base::WeakPtr (native_widget_delegate_) within the sprayed memory to appear valid and points it to a fake object.
  9. Control returns to OnActivationChanged, and ScheduleRelayout() is executed.
  10. The forged WeakPtr check passes, and the virtual method AsWidget() 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.

View on issue tracker