Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Views
DescriptionUse after free in Views
ComponentViews
Bug ClassUAF
Tracker515465685
Fix commitfef5694ce5f0 (chromium/src) +93/-0
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-02

Changed Functions

FunctionChangeNotes
if
ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
modified
switch
ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
modified
CloseOnBoundsChangedWidgetObserver
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
MaximizeBoundsChangeStubWindow
ui/views/widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc
modified
MaximizeBoundsChangePlatformWindowFactoryDelegate
ui/views/widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc
modified
MaximizeBoundsChangePlatformWindowFactoryDelegate
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_unittest.cc
From fef5694ce5f0c3ab4da4af32754ff54bcde3d0f0 Mon Sep 17 00:00:00 2001
From: Tom Anderson <[email protected]>
Date: Tue, 26 May 2026 15:27:20 -0700
Subject: [PATCH] views: Fix use-after-free in DesktopWindowTreeHostPlatform Maximize/Show

DesktopWindowTreeHostPlatform::Maximize() and Show() can synchronously
notify observers/delegates during platform-level window operations,
which can in turn trigger synchronous host deletion. When this occurs,
subsequent code inside these methods continues executing with a freed
'this' pointer, leading to a UAF.

This CL adds base::WeakPtr guards inside Maximize() and Show() to safely
return early if the host is destroyed during platform operations,
matching the pattern used in SetFullscreen().

Fixed: 515465685
Change-Id: Ie8477d072fdf6775e83fbcd9c14ecfa1892ffc15
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7868863
Reviewed-by: Lei Zhang <[email protected]>
Auto-Submit: Thomas Anderson <[email protected]>
Commit-Queue: Thomas Anderson <[email protected]>
Commit-Queue: Lei Zhang <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1636525}
---

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 8ba7443..b958f49 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
@@ -463,9 +463,17 @@
 
   platform_window()->Show(DetermineInactivity(show_state));
 
+  auto weak_ptr = weak_factory_.GetWeakPtr();
+  if (!weak_ptr) {
+    return;
+  }
+
   switch (show_state) {
     case ui::mojom::WindowShowState::kMaximized:
       platform_window()->Maximize();
+      if (!weak_ptr) {
+        return;
+      }
       if (!restore_bounds.IsEmpty()) {
         // Enforce |restored_bounds_in_pixels_| since calling Maximize() could
         // have reset it.
@@ -482,6 +490,10 @@
       break;
   }
 
+  if (!weak_ptr) {
+    return;
+  }
+
   if (WidgetActivationDelegate::Get()) {
     WidgetActivationDelegate::Get()->MaybeActivate(GetWidget(), false);
   }
@@ -661,7 +673,11 @@
 }
 
 void DesktopWindowTreeHostPlatform::Maximize() {
+  auto weak_ptr = weak_factory_.GetWeakPtr();
   platform_window()->Maximize();
+  if (!weak_ptr) {
+    return;
+  }
   if (IsMinimized()) {
     Show(ui::mojom::WindowShowState::kNormal, gfx::Rect());
   }
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 3307ffa..5bdaf338 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
@@ -156,6 +156,23 @@
   base::ScopedObservation<Widget, WidgetObserver> observation_{this};
 };
 
+class CloseOnBoundsChangedWidgetObserver : public WidgetObserver {
+ public:
+  explicit CloseOnBoundsChangedWidgetObserver(Widget* widget) {
+    observation_.Observe(widget);
+  }
+  ~CloseOnBoundsChangedWidgetObserver() override = default;
+
+  void OnWidgetBoundsChanged(Widget* widget,
+                             const gfx::Rect& new_bounds) override {
+    observation_.Reset();
+    widget->CloseNow();
+  }
+
+ private:
+  base::ScopedObservation<Widget, WidgetObserver> observation_{this};
+};
+
 }  // namespace
 
 class DesktopWindowTreeHostPlatformTest : public ViewsTestBase {
@@ -722,4 +739,64 @@
   host_platform->GetContentWindow()->RemoveObserver(&observer);
 }
 
+#if !BUILDFLAG(IS_FUCHSIA)
+class MaximizeBoundsChangeStubWindow : public ui::StubWindow {
+ public:
+  explicit MaximizeBoundsChangeStubWindow(ui::PlatformWindowDelegate* delegate,
+                                          gfx::AcceleratedWidget widget,
+                                          const gfx::Rect& bounds)
+      : StubWindow(delegate, false, bounds) {
+    InitDelegateWithWidget(delegate, widget);
+  }
+
+  void Maximize() override {
+    delegate()->OnBoundsChanged({/*origin_changed=*/true});
+  }
+};
+
+class MaximizeBoundsChangePlatformWindowFactoryDelegate
+    : public aura::WindowTreeHostPlatform::
+          PlatformWindowFactoryDelegateForTesting {
+ public:
+  MaximizeBoundsChangePlatformWindowFactoryDelegate() {
+    aura::WindowTreeHostPlatform::SetPlatformWindowFactoryDelegateForTesting(
+        this);
+  }
+  MaximizeBoundsChangePlatformWindowFactoryDelegate(
+      const MaximizeBoundsChangePlatformWindowFactoryDelegate&) = delete;
+  MaximizeBoundsChangePlatformWindowFactoryDelegate& operator=(
+      const MaximizeBoundsChangePlatformWindowFactoryDelegate&) = delete;
+  ~MaximizeBoundsChangePlatformWindowFactoryDelegate() override {
+    aura::WindowTreeHostPlatform::SetPlatformWindowFactoryDelegateForTesting(
+        nullptr);
+  }
+
+  std::unique_ptr<ui::PlatformWindow> Create(
+      aura::WindowTreeHostPlatform* host) override {
+    return std::make_unique<MaximizeBoundsChangeStubWindow>(
+        host, ++last_accelerated_widget_, gfx::Rect(100, 100, 100, 100));
+  }
+
+  gfx::AcceleratedWidget last_accelerated_widget_ = gfx::kNullAcceleratedWidget;
+};
+
+TEST_F(DesktopWindowTreeHostPlatformTest,
+       MaximizeSurvivesSynchronousCloseDuringBoundsChange) {
+  auto scoped_platform_window_factory_delegate =
+      std::make_unique<MaximizeBoundsChangePlatformWindowFactoryDelegate>();
+
+  std::unique_ptr<Widget> widget = CreateWidgetWithNativeWidget();
+  widget->Show();
+
+  auto* host_platform = DesktopWindowTreeHostPlatform::GetHostForWidget(
+      widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget());
+  ASSERT_TRUE(host_platform);
+
+  CloseOnBoundsChangedWidgetObserver observer(widget.get());
+
+  // This should not crash or trigger UAF.
+  host_platform->Maximize();
+}
+#endif  // !BUILDFLAG(IS_FUCHSIA)
+
 }  // namespace views
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 3307ffa..5bdaf338 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
@@ -156,6 +156,23 @@
   base::ScopedObservation<Widget, WidgetObserver> observation_{this};
 };
 
+class CloseOnBoundsChangedWidgetObserver : public WidgetObserver {
+ public:
+  explicit CloseOnBoundsChangedWidgetObserver(Widget* widget) {
+    observation_.Observe(widget);
+  }
+  ~CloseOnBoundsChangedWidgetObserver() override = default;
+
+  void OnWidgetBoundsChanged(Widget* widget,
+                             const gfx::Rect& new_bounds) override {
+    observation_.Reset();
+    widget->CloseNow();
+  }
+
+ private:
+  base::ScopedObservation<Widget, WidgetObserver> observation_{this};
+};
+
 }  // namespace
 
 class DesktopWindowTreeHostPlatformTest : public ViewsTestBase {
@@ -722,4 +739,64 @@
   host_platform->GetContentWindow()->RemoveObserver(&observer);
 }
 
+#if !BUILDFLAG(IS_FUCHSIA)
+class MaximizeBoundsChangeStubWindow : public ui::StubWindow {
+ public:
+  explicit MaximizeBoundsChangeStubWindow(ui::PlatformWindowDelegate* delegate,
+                                          gfx::AcceleratedWidget widget,
+                                          const gfx::Rect& bounds)
+      : StubWindow(delegate, false, bounds) {
+    InitDelegateWithWidget(delegate, widget);
+  }
+
+  void Maximize() override {
+    delegate()->OnBoundsChanged({/*origin_changed=*/true});
+  }
+};
+
+class MaximizeBoundsChangePlatformWindowFactoryDelegate
+    : public aura::WindowTreeHostPlatform::
+          PlatformWindowFactoryDelegateForTesting {
+ public:
+  MaximizeBoundsChangePlatformWindowFactoryDelegate() {
+    aura::WindowTreeHostPlatform::SetPlatformWindowFactoryDelegateForTesting(
+        this);
+  }
+  MaximizeBoundsChangePlatformWindowFactoryDelegate(
+      const MaximizeBoundsChangePlatformWindowFactoryDelegate&) = delete;
+  MaximizeBoundsChangePlatformWindowFactoryDelegate& operator=(
+      const MaximizeBoundsChangePlatformWindowFactoryDelegate&) = delete;
+  ~MaximizeBoundsChangePlatformWindowFactoryDelegate() override {
+    aura::WindowTreeHostPlatform::SetPlatformWindowFactoryDelegateForTesting(
+        nullptr);
+  }
+
+  std::unique_ptr<ui::PlatformWindow> Create(
+      aura::WindowTreeHostPlatform* host) override {
+    return std::make_unique<MaximizeBoundsChangeStubWindow>(
+        host, ++last_accelerated_widget_, gfx::Rect(100, 100, 100, 100));
+  }
+
+  gfx::AcceleratedWidget last_accelerated_widget_ = gfx::kNullAcceleratedWidget;
+};
+
+TEST_F(DesktopWindowTreeHostPlatformTest,
+       MaximizeSurvivesSynchronousCloseDuringBoundsChange) {
+  auto scoped_platform_window_factory_delegate =
+      std::make_unique<MaximizeBoundsChangePlatformWindowFactoryDelegate>();
+
+  std::unique_ptr<Widget> widget = CreateWidgetWithNativeWidget();
+  widget->Show();
+
+  auto* host_platform = DesktopWindowTreeHostPlatform::GetHostForWidget(
+      widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget());
+  ASSERT_TRUE(host_platform);
+
+  CloseOnBoundsChangedWidgetObserver observer(widget.get());
+
+  // This should not crash or trigger UAF.
+  host_platform->Maximize();
+}
+#endif  // !BUILDFLAG(IS_FUCHSIA)
+
 }  // namespace views
Loading diff…

Original Bug Report

reported by [email protected]

Potential Use-After-Free in DesktopWindowTreeHostPlatform::Maximize and Show

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 vulnerability exists in the browser process due to synchronous object destruction during window maximization. Certain platform window events can trigger the deletion of the host object, leaving subsequent code executing with a dangling ’this’ pointer. This issue affects DesktopWindowTreeHostPlatform on Linux systems using Ozone/X11.

Affected files:

  • ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
  • ui/aura/window_tree_host_platform.h

Estimated timestamp from git blame: 2019-09-27

Summary

A potential Use-After-Free (UAF) vulnerability has been identified in DesktopWindowTreeHostPlatform::Maximize() and DesktopWindowTreeHostPlatform::Show() within the Chrome browser process. On Linux (Ozone/X11), these methods invoke platform-level window operations that can synchronously trigger observer events. If an observer destroys the window host during these events, the methods continue to execute using a freed this pointer, leading to memory corruption.

Technical Details

In ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc, the Maximize() method is implemented as follows:

void DesktopWindowTreeHostPlatform::Maximize() {
  platform_window()->Maximize();
  if (IsMinimized()) {
    Show(ui::mojom::WindowShowState::kNormal, gfx::Rect());
  }
}

When platform_window()->Maximize() is called on Ozone/X11, it invokes ui::X11Window::Maximize(). If the window is currently in a fullscreen state, X11Window::Maximize() synchronously calls SetFullscreen(false), which in turn notifies its delegate via OnBoundsChanged().

In ui/aura/window_tree_host_platform.cc, the OnBoundsChanged() implementation notifies aura::WindowTreeHostObserver objects. It is a known behavior in Aura that observers may synchronously trigger the destruction of the WindowTreeHost (e.g., via Widget::CloseNow()). While the base class WindowTreeHostPlatform uses base::WeakPtr guards to handle this possibility safely, the specialized DesktopWindowTreeHostPlatform::Maximize() method does not.

After platform_window()->Maximize() returns, the code immediately calls IsMinimized(). If the host was destroyed during the call, this is a dangling pointer. IsMinimized() dereferences this to access the platform_window_ member, leading to a UAF. A similar pattern exists in DesktopWindowTreeHostPlatform::Show(), where platform_window()->Maximize() is followed by an access to platform_window().

Potential Impact and Reachability

This is a potential high-severity UAF in the browser process. An attacker who can influence window state changes may be able to trigger this path. Potential attack vectors include:

  1. A malicious or compromised extension using the chrome.windows.update API to toggle a window between fullscreen and maximized states.
  2. A Progressive Web App (PWA) using the window.maximize() API (if the DesktopPWAsAdditionalWindowingControls feature is enabled).

If an attacker can groom the heap to control the memory of the freed host object, the virtual calls performed during the UAF (e.g., on the platform_window_ pointer) could potentially be leveraged for remote code execution (RCE) in the browser process. MiraclePtr does not mitigate this specific issue because the dangling reference is the implicit this pointer, and the member being accessed is a std::unique_ptr.

Suggested Fix

Apply a base::WeakPtr guard in both Maximize() and Show() to check if the object is still valid after the call to platform_window()->Maximize(). This pattern is already correctly implemented in DesktopWindowTreeHostPlatform::SetFullscreen().

Example fix for Maximize():

void DesktopWindowTreeHostPlatform::Maximize() {
  auto weak_ptr = GetWeakPtr();
  platform_window()->Maximize();
  if (!weak_ptr) {
    return;
  }
  if (IsMinimized()) {
    Show(ui::mojom::WindowShowState::kNormal, gfx::Rect());
  }
}

Evaluated with Chrome root at commit: 29093e11cf509e3593f6229e4b1b075cca356049


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