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 5bdaf3387fbb1..b45411b37af35 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 @@ -752,6 +752,14 @@ class MaximizeBoundsChangeStubWindow : public ui::StubWindow { void Maximize() override { delegate()->OnBoundsChanged({/*origin_changed=*/true}); } + + // Mirrors X11Window::Restore() when the window is fullscreen: it calls + // SetFullscreen(false) whose final action is + // platform_window_delegate_->OnBoundsChanged(), explicitly documented as + // potentially deleting `this` (ui/ozone/platform/x11/x11_window.cc). + void Restore() override { + delegate()->OnBoundsChanged({/*origin_changed=*/true}); + } }; class MaximizeBoundsChangePlatformWindowFactoryDelegate @@ -797,6 +805,33 @@ TEST_F(DesktopWindowTreeHostPlatformTest, // This should not crash or trigger UAF. host_platform->Maximize(); } + +// Regression test: DesktopWindowTreeHostPlatform::Restore() must survive the +// platform window synchronously deleting the host during the OnBoundsChanged +// observer fan-out. On X11, X11Window::Restore() from a fullscreen window calls +// SetFullscreen(false) which dispatches OnBoundsChanged() and is documented as +// potentially deleting `this`. Without a weak-pointer guard, the trailing call +// to Show() in Restore() runs on a freed object. +TEST_F(DesktopWindowTreeHostPlatformTest, + RestoreSurvivesSynchronousCloseDuringBoundsChange) { + auto scoped_platform_window_factory_delegate = + std::make_unique(); + + std::unique_ptr 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, but currently does: + // platform_window()->Restore() -> OnBoundsChanged -> observer CloseNow() + // frees |host_platform|, then Restore() unconditionally calls Show() on the + // freed |this|. + host_platform->Restore(); +} #endif // !BUILDFLAG(IS_FUCHSIA) } // namespace views