CVE-2026-17894
Overview
fc4613ff876d), which an audit found was a preparatory/helper change, not the fix. This page now points to the commit whose diff removes the bug. No root-cause analysis is published for this CVE.Changed Functions
| Function | Change | Notes |
|---|---|---|
ifui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc |
modified | |
CenterWindowCloseWidgetScreenui/views/widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc |
modified | |
widget_ui/views/widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc |
modified | |
ifui/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_unittest.cc
Patch
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 d538135..df187a5 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
@@ -555,7 +555,11 @@
}
void DesktopWindowTreeHostPlatform::CenterWindow(const gfx::Size& size) {
+ auto weak_ptr = weak_factory_.GetWeakPtr();
gfx::Rect parent_bounds = GetWorkAreaBoundsInScreen();
+ if (!weak_ptr) {
+ return;
+ }
// If |window_|'s transient parent bounds are big enough to contain |size|,
// use them instead.
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 264680e..d255411 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
@@ -23,6 +23,7 @@
#include "ui/base/ui_base_features.h"
#include "ui/compositor/layer.h"
#include "ui/display/display_switches.h"
+#include "ui/display/test/test_screen.h"
#include "ui/display/types/display_constants.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/native_ui_types.h"
@@ -855,6 +856,53 @@
// This should not crash or trigger UAF.
host_platform->Restore();
}
+
+class CenterWindowCloseWidgetScreen : public display::test::TestScreen {
+ public:
+ explicit CenterWindowCloseWidgetScreen(Widget* widget)
+ : display::test::TestScreen(/*create_display=*/false,
+ /*register_screen=*/false),
+ widget_(widget) {
+ display_list().AddDisplay({0, gfx::Rect(0, 0, 800, 600)},
+ display::DisplayList::Type::PRIMARY);
+ old_screen_ = display::Screen::SetScreenInstance(nullptr);
+ display::Screen::SetScreenInstance(this);
+ }
+
+ ~CenterWindowCloseWidgetScreen() override {
+ display::Screen::SetScreenInstance(nullptr);
+ display::Screen::SetScreenInstance(old_screen_);
+ }
+
+ display::Display GetDisplayNearestWindow(
+ gfx::NativeWindow window) const override {
+ if (widget_ && !widget_closed_) {
+ widget_closed_ = true;
+ widget_->CloseNow();
+ }
+ return display::test::TestScreen::GetDisplayNearestWindow(window);
+ }
+
+ private:
+ raw_ptr<Widget> widget_;
+ raw_ptr<display::Screen> old_screen_ = nullptr;
+ mutable bool widget_closed_ = false;
+};
+
+TEST_F(DesktopWindowTreeHostPlatformTest,
+ CenterWindowSurvivesSynchronousClose) {
+ std::unique_ptr<Widget> widget = CreateWidgetWithNativeWidget();
+ widget->Show();
+
+ auto* host_platform = DesktopWindowTreeHostPlatform::GetHostForWidget(
+ widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget());
+ ASSERT_TRUE(host_platform);
+
+ CenterWindowCloseWidgetScreen test_screen(widget.get());
+
+ // This should not crash or trigger UAF.
+ host_platform->CenterWindow(gfx::Size(100, 100));
+}
#endif // !BUILDFLAG(IS_FUCHSIA)
} // namespace views
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 264680e..d255411 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
@@ -23,6 +23,7 @@
#include "ui/base/ui_base_features.h"
#include "ui/compositor/layer.h"
#include "ui/display/display_switches.h"
+#include "ui/display/test/test_screen.h"
#include "ui/display/types/display_constants.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/native_ui_types.h"
@@ -855,6 +856,53 @@
// This should not crash or trigger UAF.
host_platform->Restore();
}
+
+class CenterWindowCloseWidgetScreen : public display::test::TestScreen {
+ public:
+ explicit CenterWindowCloseWidgetScreen(Widget* widget)
+ : display::test::TestScreen(/*create_display=*/false,
+ /*register_screen=*/false),
+ widget_(widget) {
+ display_list().AddDisplay({0, gfx::Rect(0, 0, 800, 600)},
+ display::DisplayList::Type::PRIMARY);
+ old_screen_ = display::Screen::SetScreenInstance(nullptr);
+ display::Screen::SetScreenInstance(this);
+ }
+
+ ~CenterWindowCloseWidgetScreen() override {
+ display::Screen::SetScreenInstance(nullptr);
+ display::Screen::SetScreenInstance(old_screen_);
+ }
+
+ display::Display GetDisplayNearestWindow(
+ gfx::NativeWindow window) const override {
+ if (widget_ && !widget_closed_) {
+ widget_closed_ = true;
+ widget_->CloseNow();
+ }
+ return display::test::TestScreen::GetDisplayNearestWindow(window);
+ }
+
+ private:
+ raw_ptr<Widget> widget_;
+ raw_ptr<display::Screen> old_screen_ = nullptr;
+ mutable bool widget_closed_ = false;
+};
+
+TEST_F(DesktopWindowTreeHostPlatformTest,
+ CenterWindowSurvivesSynchronousClose) {
+ std::unique_ptr<Widget> widget = CreateWidgetWithNativeWidget();
+ widget->Show();
+
+ auto* host_platform = DesktopWindowTreeHostPlatform::GetHostForWidget(
+ widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget());
+ ASSERT_TRUE(host_platform);
+
+ CenterWindowCloseWidgetScreen test_screen(widget.get());
+
+ // This should not crash or trigger UAF.
+ host_platform->CenterWindow(gfx::Size(100, 100));
+}
#endif // !BUILDFLAG(IS_FUCHSIA)
} // namespace views
Original Bug Report
Potential Browser-Process UAF in DesktopWindowTreeHostPlatform::CenterWindow
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 DesktopWindowTreeHostPlatform::CenterWindow on Linux/X11 (Ozone). Querying the work area bounds can transitively dispatch pending X11 events synchronously via GeometryCache, which can trigger host destruction. When execution resumes in CenterWindow, the code dereferences the freed host instance, potentially leading to a wild virtual function call.
Affected files:
ui/views/widget/desktop_aura/desktop_window_tree_host_platform.ccui/ozone/platform/x11/x11_screen_ozone.ccui/ozone/platform/x11/x11_window.cc
Estimated timestamp from git blame: 2025-03-06
Root Cause Analysis
In ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc, the function DesktopWindowTreeHostPlatform::CenterWindow executes the following sequence:
void DesktopWindowTreeHostPlatform::CenterWindow(const gfx::Size& size) {
gfx::Rect parent_bounds = GetWorkAreaBoundsInScreen(); // <-- Potential synchronous deletion sink
if (wm::GetTransientParent(GetContentWindow())) { // <-- UAF read of freed |this|
gfx::Rect transient_parent_rect =
wm::GetTransientParent(GetContentWindow())->GetBoundsInScreen();
...
}
...
SetBoundsInDIP(window_bounds_in_screen); // <-- UAF virtual call on freed |this|
}
When GetWorkAreaBoundsInScreen() is called under Linux/Ozone/X11, it queries the display system which transitively invokes X11Window::GetBoundsInPixels().
If the window was recently created or reparented, the underlying GeometryCache may be un-Ready (i.e., waiting for responses from the X11 server). To resolve this, GeometryCache::GetBoundsPx() synchronously dispatches the pending futures via DispatchNow() to process incoming server replies. This runs the message loop synchronously.
Processing these events can trigger bounds-change notifications, causing registered WindowTreeHostObservers to run. If an observer synchronously closes the widget/host (e.g., during complex layout recalculations or window-snapping events), the DesktopWindowTreeHostPlatform instance is deleted via host_.reset(). When control returns to CenterWindow(), execution continues using the now-freed this pointer, causing a Use-After-Free read on desktop_native_widget_aura_ followed by a virtual method call on SetBoundsInDIP through a hijacked vtable pointer.
Potential Trigger Path
Note: The following are suggested/potential steps to trigger the behavior, as our tooling does not currently have the capability to run code to confirm live execution:
- From the renderer or browser UI, trigger the initialization of a top-level widget/dialog with default or empty bounds on Linux/X11.
- The window is initialized, and
Widget::SetInitialBoundscallsCenterWindow()before the X server has replied to the window geometry queries. - The synchronous
DispatchNow()loop is entered withinGeometryCache::GetBoundsPx()to retrieve the geometry replies. - An event handler or observer intercepts the bounds change and immediately calls
Widget::CloseNow(), which synchronously deletes theDesktopWindowTreeHostPlatformhost container. - Control flow returns to
CenterWindow(), resulting in a browser-process UAF.
MiraclePtr Status
This UAF is not protected by MiraclePtr. The dangling pointer is the implicit stack this pointer (not a raw_ptr<>). The member field desktop_native_widget_aura_ resides inside the freed DesktopWindowTreeHostPlatform allocation, so reading it is a UAF on the storage itself rather than a BRP-quarantined reference.
Suggested Fix
Add a base::WeakPtr guard in DesktopWindowTreeHostPlatform::CenterWindow to safely abort execution if the host is synchronously destroyed during the bounds query:
void DesktopWindowTreeHostPlatform::CenterWindow(const gfx::Size& size) {
base::WeakPtr<DesktopWindowTreeHostPlatform> weak_this = weak_factory_.GetWeakPtr();
gfx::Rect parent_bounds = GetWorkAreaBoundsInScreen();
if (!weak_this) {
return;
}
if (wm::GetTransientParent(GetContentWindow())) {
...
Evaluated with Chrome root at commit: 75203b87cbf6681eb7c7dda8e1d0bf781538c76a
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.