High watch CVSS 8.8 chrome UAF 📄 Reporter bug report 🔧 Commit mapped

Overview

High
Severity
8.8
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in GTK
DescriptionUse after free in GTK
ComponentGTK
Sub-componentGtk
Bug ClassUAF
Action tierwatch — corruption primitive but gated (uncommon) — not default-reachable
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker500033878
Fix commiteed57ee58f27 (chromium/src) +8/-4
CWECWE-416 (Use-after-free)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CISA KEVNot listed
CreditedGoogle
Reported2026-04-06 (per vendor advisory)
Fix landed2026-04-07 (public git)
Disclosed2026-05-12
Patch gap35 days — fix public in source before the advisory

Background

`GtkUiPlatformWayland`
Chrome’s GTK integration layer for Wayland, which sets up native GTK dialogs and their relationship to parent windows.
xdg-foreign / `ExportWindowHandle`
a Wayland mechanism to export a parent window’s handle asynchronously; Chrome calls it and continues work in a callback once the handle string is produced.
`ScopedGObject<T>` / `WrapGObject`
an RAII smart pointer that takes a strong GObject reference, keeping the object alive while the wrapper is held.

Root Cause Analysis

GtkUiPlatformWayland::SetGtkWidgetTransientFor bound the raw widget pointer into a base::BindOnce callback (OnHandleSetTransient) passed to the asynchronous ExportWindowHandle. Because the bound value was a bare GtkWidget* with no ownership, nothing kept the widget alive across the async gap: if the dialog was closed and the GtkWidget destroyed before the handle callback ran, OnHandleSetTransient dereferenced a dangling pointer, the use-after-free the advisory describes. The weak_factory_.GetWeakPtr() on the receiver only protects the GtkUiPlatformWayland instance, not the separately-owned widget argument.

The fix wraps the widget with WrapGObject(widget) so the callback now carries a ScopedGObject<GtkWidget> widget_ref that holds a strong reference; OnHandleSetTransient obtains the raw pointer via widget_ref.get() only after the reference guarantees the object is still alive. This keeps the widget’s memory valid until the callback completes even if GTK has otherwise disposed the dialog, closing the UAF.

Key insight
The core mistake was binding a raw, unowned GtkWidget* into an asynchronous Wayland callback, so a dialog closed before the callback fired left a dangling pointer; the fix binds a ScopedGObject<GtkWidget> strong reference (WrapGObject) that keeps the widget alive until the callback runs.

Attack Path

  1. Open a transient dialog Trigger a flow that calls SetGtkWidgetTransientFor, which kicks off the asynchronous ExportWindowHandle with OnHandleSetTransient bound to the raw widget (pre-patch).
  2. Close before the callback Cause the dialog/widget to be destroyed before the xdg-foreign handle is produced, so GTK disposes the GtkWidget while the pending callback still holds its raw pointer (inferred: attacker influence over dialog timing/dismissal).
  3. Callback dereferences freed widget When ExportWindowHandle finally invokes OnHandleSetTransient, it uses the now-freed widget, producing the use-after-free.
  4. Post-patch The ScopedGObject<GtkWidget> reference bound into the callback keeps the widget alive until OnHandleSetTransient finishes, so the dereference is valid.

Impact Assessment

The advisory classifies this as a use after free in GTK, rated high. Triggering it requires a Wayland/GTK Chrome build and a dialog that is dismissed during the async window-handle export window; the resulting UAF occurs in the browser process. The patch demonstrates the lifetime bug and its fix but not any further primitive; escalation of the UAF to code execution is not shown by the diff and is (inferred). No exploited-in-the-wild status is stated in the provided advisory.

Files Changed

  • ui/gtk/wayland/gtk_ui_platform_wayland.cc
  • ui/gtk/wayland/gtk_ui_platform_wayland.h

Audit Directions

  • Raw pointers bound into async callbacks
    Flag base::BindOnce/BindRepeating that capture bare GtkWidget*/GObject*/other externally-owned pointers; across an async boundary they need a ScopedGObject/scoped_refptr/strong-ref wrapper, not just a WeakPtr on the receiver.
  • WeakPtr covers the wrong object
    Review callbacks that use weak_factory_.GetWeakPtr() for self but pass additional unowned arguments by raw pointer; the weak guard does not protect those arguments.
  • GTK/Wayland async export paths
    Audit other ExportWindowHandle/xdg-foreign and similar asynchronous GTK integration callbacks for widget/window lifetime assumptions when the dialog can be closed before completion.
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};
 };
Loading diff…

Original Bug Report

reported by [email protected]

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