Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in GTK
DescriptionUse after free in GTK
ComponentGTK
Bug ClassUAF
Tracker500033878
Fix commiteed57ee58f27 (chromium/src) +8/-4
CISA KEVNot listed
CreditedGoogle
Disclosed2026-05-12

Files Changed

  • ui/gtk/wayland/gtk_ui_platform_wayland.cc
  • ui/gtk/wayland/gtk_ui_platform_wayland.h
From eed57ee58f276d40af4c842d7d900f68482cc5f1 Mon Sep 17 00:00:00 2001
From: Tom Anderson <thomasanderson@chromium.org>
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 <thestig@chromium.org>
Commit-Queue: Thomas Anderson <thomasanderson@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
Auto-Submit: Thomas Anderson <thomasanderson@chromium.org>
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};
 };
Loading diff…

Original Bug Report

reported by vm...@google.com

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.cc
  • ui/ozone/platform/wayland/host/xdg_foreign_wrapper.cc
  • ui/gtk/printing/print_dialog_gtk.cc
  • ui/gtk/select_file_dialog_linux_gtk.cc
  • content/browser/file_system_access/file_system_chooser.cc
  • ui/gtk/gtk_util.cc
  • ui/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.

  1. An attacker hosts a malicious page with a cross-origin <iframe>.
  2. The attacker tricks the user into a gesture (e.g., a click) inside the iframe.
  3. The iframe uses the File System Access API (showOpenFilePicker()) to request a file chooser dialog.
  4. In the browser process, FileSystemChooser creates a native GTK dialog (SelectFileDialogLinuxGtk).
  5. 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 the base::BindOnce callback for this response.
  6. While the Wayland request is pending, the parent page removes the <iframe> from the DOM.
  7. This destroys the RenderFrameHost, causing the WebContentsBasedCanceller to cancel the file selection and destroy the FileSystemChooser.
  8. The SelectFileDialogLinuxGtk is destroyed, calling gtk_window_destroy(widget) and freeing the GtkWidget.
  9. The Wayland compositor replies with the exported handle. Chrome executes the pending callback, calling GtkUiPlatformWayland::OnHandleSetTransient with the dangling GtkWidget* pointer.
  10. 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.

View on issue tracker