CVE-2026-8555
Overview
Files Changed
ui/gtk/wayland/gtk_ui_platform_wayland.ccui/gtk/wayland/gtk_ui_platform_wayland.h
Patch
From eed57ee58f276d40af4c842d7d900f68482cc5f1 Mon Sep 17 00:00:00 2001 From: Tom Anderson <[email protected]> Date: Tue, 07 Apr 2026 16:05:08 -0700 Subject: [PATCH] gtk: Fix Use-After-Free in GtkUiPlatformWayland::OnHandleSetTransient GtkUiPlatformWayland::SetGtkWidgetTransientFor binds a raw GtkWidget* to an asynchronous Wayland callback. If the dialog is closed before the callback executes, the GtkWidget is destroyed, leading to a Use-After-Free when the callback dereferences it. This CL fixes the issue by using ScopedGObject to hold a strong reference to the GtkWidget in the callback. This ensures the widget's memory remains valid until the callback completes, even if it has been destroyed/disposed by GTK. R=thestig Change-Id: I402b18c246c9192038418f1348ff9066089917eb Fixed: 500033878 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7737624 Reviewed-by: Lei Zhang <[email protected]> Commit-Queue: Thomas Anderson <[email protected]> Commit-Queue: Lei Zhang <[email protected]> Auto-Submit: Thomas Anderson <[email protected]> Cr-Commit-Position: refs/heads/main@{#1611064} --- diff --git a/ui/gtk/wayland/gtk_ui_platform_wayland.cc b/ui/gtk/wayland/gtk_ui_platform_wayland.cc index 1702e2d5..60049a3 100644 --- a/ui/gtk/wayland/gtk_ui_platform_wayland.cc +++ b/ui/gtk/wayland/gtk_ui_platform_wayland.cc @@ -45,7 +45,7 @@ gfx::AcceleratedWidget parent) { ui::LinuxUiDelegate::GetInstance()->ExportWindowHandle( parent, base::BindOnce(&GtkUiPlatformWayland::OnHandleSetTransient, - weak_factory_.GetWeakPtr(), widget)); + weak_factory_.GetWeakPtr(), WrapGObject(widget))); } void GtkUiPlatformWayland::ClearTransientFor(gfx::AcceleratedWidget parent) { @@ -58,8 +58,10 @@ gtk_window_present(window); } -void GtkUiPlatformWayland::OnHandleSetTransient(GtkWidget* widget, - std::string handle) { +void GtkUiPlatformWayland::OnHandleSetTransient( + ScopedGObject<GtkWidget> widget_ref, + std::string handle) { + GtkWidget* widget = widget_ref.get(); auto handle_no_prefix = base::RemovePrefix(handle, "wayland:"); if (!handle_no_prefix || handle_no_prefix->empty()) { return; diff --git a/ui/gtk/wayland/gtk_ui_platform_wayland.h b/ui/gtk/wayland/gtk_ui_platform_wayland.h index 296245ef..d8453ac 100644 --- a/ui/gtk/wayland/gtk_ui_platform_wayland.h +++ b/ui/gtk/wayland/gtk_ui_platform_wayland.h @@ -10,6 +10,7 @@ #include "base/functional/callback_forward.h" #include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" +#include "ui/base/glib/scoped_gobject.h" #include "ui/gtk/gtk_ui_platform.h" namespace gtk { @@ -36,7 +37,8 @@ private: // Called when xdg-foreign exports a parent window passed in // SetGtkWidgetTransientFor. - void OnHandleSetTransient(GtkWidget* widget, std::string handle); + void OnHandleSetTransient(ScopedGObject<GtkWidget> widget_ref, + std::string handle); base::WeakPtrFactory<GtkUiPlatformWayland> weak_factory_{this}; };
Original Bug Report
Potential Browser-Process UAF in GtkUiPlatformWayland::OnHandleSetTransient
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 without the security team.
Overview: A potential Use-After-Free (UAF) exists when setting a Wayland transient parent for GTK dialogs. If an attacker opens a dialog and immediately destroys the requesting iframe, the GTK dialog is freed while an asynchronous callback holds a raw pointer to it. When the Wayland compositor responds, the callback accesses the freed widget.
Affected files:
ui/gtk/wayland/gtk_ui_platform_wayland.ccui/ozone/platform/wayland/host/xdg_foreign_wrapper.ccui/gtk/printing/print_dialog_gtk.ccui/gtk/select_file_dialog_linux_gtk.cccontent/browser/file_system_access/file_system_chooser.ccui/gtk/gtk_util.ccui/shell_dialogs/select_file_dialog_linux_portal.cc
Estimated timestamp from git blame: 2025-11-11
Summary
A potential Use-After-Free (UAF) vulnerability exists in the browser process on Linux/Wayland when using the GTK UI backend. The issue stems from GtkUiPlatformWayland::SetGtkWidgetTransientFor, which binds a raw GtkWidget* into an asynchronous Wayland callback. If the dialog is closed before the callback executes, the GtkWidget is destroyed. When the Wayland compositor eventually responds, the callback dereferences the freed widget, leading to a UAF.
Potential Attacker Steps
Note: These are suggested/potential steps based on code analysis; our tooling has not executed a live proof-of-concept.
- An attacker hosts a malicious page with a cross-origin
<iframe>. - The attacker tricks the user into a gesture (e.g., a click) inside the iframe.
- The iframe uses the File System Access API (
showOpenFilePicker()) to request a file chooser dialog. - In the browser process,
FileSystemChoosercreates a native GTK dialog (SelectFileDialogLinuxGtk). - Chrome attempts to set the transient parent for the dialog. On Wayland, this requires an asynchronous roundtrip to the compositor to export the parent window handle. The raw
GtkWidget*is bound to thebase::BindOncecallback for this response. - While the Wayland request is pending, the parent page removes the
<iframe>from the DOM. - This destroys the
RenderFrameHost, causing theWebContentsBasedCancellerto cancel the file selection and destroy theFileSystemChooser. - The
SelectFileDialogLinuxGtkis destroyed, callinggtk_window_destroy(widget)and freeing theGtkWidget. - The Wayland compositor replies with the exported handle. Chrome executes the pending callback, calling
GtkUiPlatformWayland::OnHandleSetTransientwith the danglingGtkWidget*pointer. - The dangling pointer is passed to
gtk_widget_get_native(widget), triggering a virtual function call on attacker-controlled memory and potentially achieving browser-process Remote Code Execution (RCE).
MiraclePtr (BRP) Bypass
Chromium’s BackupRefPtr (MiraclePtr) mechanism usually protects raw pointers bound in callbacks. However, GtkWidget objects are allocated by GLib. On older GLib versions (e.g., GLib < 2.76, default on Ubuntu 20.04/22.04), memory is managed by the g_slice sub-allocator. g_slice maintains its own freelist and does not immediately return memory via free(). Because PartitionAlloc’s free() hook is bypassed, MiraclePtr is unaware that the object was destroyed. As a result, the dangling pointer detection in UnretainedWrapper fails to trigger, making the UAF fully exploitable.
Suggested Fix
Avoid binding a raw GtkWidget* to the asynchronous Wayland callback. Because GtkWidget is a GObject and does not support base::WeakPtr directly, a safe approach is to use g_object_weak_ref to track the widget’s lifetime, or pass a base::WeakPtr to the C++ wrapper class (e.g., SelectFileDialogLinuxGtk) that manages the dialog.
Alternatively, GtkUiPlatformWayland could store a mapping of active requests and provide a method to explicitly cancel the pending Wayland handle export when the caller destroys the GTK dialog.
Evaluated with Chrome root at commit: f200f57a19490707ff8bc7aa5de3cbc443a3afad
Results 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.