Critical watch CVSS 8.3 chrome UAF 📄 Reporter bug report 🔧 Commit mapped

Overview

Critical
Severity
8.3
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Printing
DescriptionUse after free in Printing
ComponentPrinting
Sub-componentPrinting
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.
Tracker513445101
Fix commit228627507b69 (chromium/src) +26/-5
CWECWE-416 (Use-after-free)
CVSS vectorCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:H
CISA KEVNot listed
CreditedGoogle
Reported2026-05-15 (per vendor advisory)
Fix landed2026-05-16 (public git)
Disclosed2026-06-02
Patch gap17 days — fix public in source before the advisory

Background

`PrintDialogLinuxPortal`
the Linux (xdg-desktop-portal) print-dialog backend that hands a parent window handle to the desktop portal over an asynchronous D-Bus handshake.
`aura::Window` / `gfx::NativeView`
the native window object (raw pointer) whose lifetime is independent of the print dialog’s and can end while the handshake is in flight.
`aura::WindowTracker`
a helper that holds observed aura::Windows and automatically drops them from its windows() list when they are destroyed, giving safe liveness checks.
D-Bus portal handshake
RequestXdgDesktopPortal plus the completion callback (OnPortalAvailable) run asynchronously, so any window pointer cached across them may dangle.

Root Cause Analysis

PrintDialogLinuxPortal::ShowDialog cached the caller-supplied parent window in a raw gfx::NativeView parent_view_ and then started the asynchronous RequestXdgDesktopPortal handshake. If the parent aura::Window was destroyed while the handshake was outstanding, the completion path (OnPortalAvailable, which reads parent_view_ to call window->GetRootWindow() / ExportWindowHandle, and the fallback path that passes parent_view_ to fallback_dialog_->ShowDialog) dereferenced a dangling pointer — a use-after-free. The invariant violated is that the cached window outlives the async round-trip, which the raw pointer could not guarantee.

The fix replaces parent_view_ with an aura::WindowTracker parent_view_tracker_ (plus a parent_view_provided_ bool recording whether a window was originally supplied); ShowDialog clears the tracker and adds the window to it, and the completion paths read parent_view_tracker_.windows() — an empty list means the window was destroyed, so they cancel the dialog with mojom::ResultCode::kCanceled instead of dereferencing freed memory.

Key insight
A raw parent-window pointer was held across an asynchronous D-Bus handshake with no lifetime tracking; the fix swaps it for an aura::WindowTracker that self-clears on window destruction and makes the completion callbacks check windows() (cancelling when empty) before any dereference.

Attack Path

  1. Initiate portal print Code calls PrintDialogLinuxPortal::ShowDialog with a parent aura::Window, which (pre-patch) caches it in parent_view_ and starts the async RequestXdgDesktopPortal handshake.
  2. Destroy the parent window During the outstanding handshake the parent window is destroyed (e.g., the originating tab/window is closed), freeing the aura::Window while the raw parent_view_ still points at it.
  3. Completion callback fires OnPortalAvailable runs and reads parent_view_, calling window->GetRootWindow() / ExportWindowHandle (or the fallback path passes it to fallback_dialog_->ShowDialog) on the freed object — a use-after-free.
  4. Downstream corruption Any escalation from the dangling dereference to a controllable read/write primitive or code execution is not shown by the diff (inferred).

Impact Assessment

An attacker able to tear down the parent window during the asynchronous portal handshake triggers a use-after-free on the cached aura::Window in the browser process on Linux desktops using xdg-desktop-portal printing. The vendor advisory rates this critical and labels it a use-after-free in Printing; the diff demonstrates the dangling-pointer dereference but any memory-corruption escalation to a primitive or code execution is (inferred). Preconditions are the Linux portal print path plus window destruction racing the D-Bus round-trip; the advisory does not state in-the-wild exploitation.

Changed Functions

FunctionChangeNotes
if
components/printing/common/print_dialog_linux_portal.cc
modified

Files Changed

  • components/printing/common/print_dialog_linux_portal.cc
  • components/printing/common/print_dialog_linux_portal.h

Audit Directions

  • Raw native-window pointers across async boundaries
    Flag any aura::Window*/gfx::NativeView cached in a member and used after an async callback (D-Bus, Mojo, posted task); prefer aura::WindowTracker or a WeakPtr.
  • Portal / IPC completion callbacks
    Review xdg-desktop-portal and other async handshake completions that dereference window/view state captured at request time.
  • "Provided but gone" handling
    Audit async flows that must distinguish “no window supplied” from “window supplied but destroyed,” ensuring the latter cancels rather than proceeds with a stale pointer.
From 228627507b69a7b7fd93b40334d47d2fe7e93072 Mon Sep 17 00:00:00 2001
From: Tom Anderson <thomasanderson@chromium.org>
Date: Fri, 15 May 2026 18:53:17 -0700
Subject: [PATCH] [Printing] Fix Use-After-Free in PrintDialogLinuxPortal

This change fixes a potential Use-After-Free (UAF) vulnerability in the
Linux printing component by replacing a raw aura::Window pointer with an
aura::WindowTracker.

In PrintDialogLinuxPortal::ShowDialog, the parent window pointer was
cached across an asynchronous D-Bus handshake. If the window was
destroyed during this period, subsequent dereferences in the completion
callback OnPortalAvailable could lead to memory corruption.

By using aura::WindowTracker, the window can be checked for validity
before dereferencing it.

Fixed: 513445101
Change-Id: Id97b2da5738bd64d490efdbc459da55d888adcc4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7849241
Auto-Submit: Thomas Anderson <thomasanderson@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1631694}
---

diff --git a/components/printing/common/print_dialog_linux_portal.cc b/components/printing/common/print_dialog_linux_portal.cc
index 4f54558a..6bd5d0ac 100644
--- a/components/printing/common/print_dialog_linux_portal.cc
+++ b/components/printing/common/print_dialog_linux_portal.cc
@@ -715,7 +715,11 @@
     bool has_selection,
     PrintingContextLinux::PrintSettingsCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  parent_view_ = parent_view;
+  parent_view_tracker_.RemoveAll();
+  parent_view_provided_ = parent_view != nullptr;
+  if (parent_view) {
+    parent_view_tracker_.Add(parent_view);
+  }
   callback_ = std::move(callback);
 
   dbus_xdg::RequestXdgDesktopPortal(
@@ -733,7 +737,9 @@
 
   // Export the window handle to the portal.
   if (auto* delegate = ui::LinuxUiDelegate::GetInstance()) {
-    aura::Window* window = parent_view_ ? parent_view_ : nullptr;
+    aura::Window* window = parent_view_tracker_.windows().empty()
+                               ? nullptr
+                               : parent_view_tracker_.windows()[0];
     if (window && window->GetRootWindow()) {
       // Assuming aura::Window* as NativeView.
       delegate->ExportWindowHandle(
@@ -742,6 +748,12 @@
                          weak_factory_.GetWeakPtr(), has_selection));
       return;
     }
+
+    if (parent_view_provided_) {
+      // Window was provided but is now gone. Cancel the dialog.
+      std::move(callback_).Run(mojom::ResultCode::kCanceled);
+      return;
+    }
   }
 
   // No window or no delegate, call with empty handle.
@@ -906,8 +918,15 @@
     fallback_dialog_->UseDefaultSettings();
   }
 
-  fallback_dialog_->ShowDialog(parent_view_, has_selection,
-                               std::move(callback_));
+  aura::Window* window = parent_view_tracker_.windows().empty()
+                             ? nullptr
+                             : parent_view_tracker_.windows()[0];
+  if (parent_view_provided_ && !window) {
+    std::move(callback_).Run(mojom::ResultCode::kCanceled);
+    return;
+  }
+
+  fallback_dialog_->ShowDialog(window, has_selection, std::move(callback_));
 }
 
 }  // namespace printing
diff --git a/components/printing/common/print_dialog_linux_portal.h b/components/printing/common/print_dialog_linux_portal.h
index 8066e1c4..fd7f3cc8 100644
--- a/components/printing/common/print_dialog_linux_portal.h
+++ b/components/printing/common/print_dialog_linux_portal.h
@@ -18,6 +18,7 @@
 #include "dbus/bus.h"
 #include "printing/print_dialog_linux_interface.h"
 #include "printing/printing_context_linux.h"
+#include "ui/aura/window_tracker.h"
 #include "ui/gfx/native_ui_types.h"
 
 namespace base {
@@ -78,7 +79,8 @@
 
   // Temporary storage for ShowDialog arguments while checking portal
   // availability.
-  gfx::NativeView parent_view_ = nullptr;
+  aura::WindowTracker parent_view_tracker_;
+  bool parent_view_provided_ = false;
 
   scoped_refptr<dbus::Bus> bus_;
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
Loading diff…

Original Bug Report

reported by vm...@google.com

Browser-process Use-After-Free in PrintDialogLinuxPortal via D-Bus Handshake

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 Use-After-Free (UAF) vulnerability exists in the Linux printing component where a raw aura::Window pointer is cached across an asynchronous D-Bus handshake. If the window is destroyed during this period, subsequent dereferences in the completion callback can lead to memory corruption. This issue affects Chrome on Linux and potentially allows for code execution in the browser process.

Affected files:

  • components/printing/common/print_dialog_linux_portal.cc
  • components/printing/common/print_dialog_linux_portal.h
  • ui/gtk/printing/print_dialog_gtk.cc

Estimated timestamp from git blame: 2025-12-11

Summary

A potential Use-After-Free (UAF) vulnerability has been identified in PrintDialogLinuxPortal. The class caches a raw aura::Window* (stored as gfx::NativeView) across an asynchronous D-Bus operation. If the WebContents or its associated window is closed before the handshake completes, the pointer becomes dangling, leading to a UAF when the completion callback is executed.

Root Cause Analysis

In components/printing/common/print_dialog_linux_portal.cc, the ShowDialog method initiates an asynchronous portal check:

void PrintDialogLinuxPortal::ShowDialog(gfx::NativeView parent_view, ...) {
  parent_view_ = parent_view; // Caches raw aura::Window*
  dbus_xdg::RequestXdgDesktopPortal(
      bus_.get(), base::BindOnce(&PrintDialogLinuxPortal::OnPortalAvailable,
                                 weak_factory_.GetWeakPtr(), has_selection));
}

The PrintDialogLinuxPortal is kept alive by a temporary ownership cycle involving PrinterQuery and the print callback. If an attacker closes the window (e.g., via window.close() in a renderer) while the D-Bus request is pending, parent_view_ becomes a dangling pointer. When OnPortalAvailable is eventually called, it dereferences this pointer:

void PrintDialogLinuxPortal::OnPortalAvailable(bool has_selection, uint32_t version) {
  // ...
  aura::Window* window = parent_view_ ? parent_view_ : nullptr;
  if (window && window->GetRootWindow()) {
    delegate->ExportWindowHandle(
        window->GetHost()->GetAcceleratedWidget(), // <--- Potential UAF
        ...);

GetHost() and GetAcceleratedWidget() involve accessing the memory of the freed aura::Window and performing virtual function calls. An attacker who can reclaim this memory via heap spraying could potentially hijack the control flow to achieve code execution in the unsandboxed browser process.

Potential Attack Sequence (Unverified)

  1. A compromised renderer opens a popup window.
  2. The renderer triggers a scripted print request for that popup.
  3. The browser process initiates the PrintDialogLinuxPortal and the asynchronous D-Bus handshake.
  4. The renderer closes the popup window, destroying the associated aura::Window while the handshake is pending.
  5. The attacker attempts to reclaim the freed memory in the browser process using heap spraying techniques.
  6. Upon completion of the D-Bus call, OnPortalAvailable executes, dereferencing the dangling pointer and triggering the virtual call on attacker-controlled memory.

Suggested Fix

The PrintDialogLinuxPortal should not store a raw aura::Window*. Instead, it should use an aura::WindowTracker to safely monitor the lifetime of the parent window. Before dereferencing the window in OnPortalAvailable, the code should check the tracker to ensure the window is still valid. Alternatively, using a base::WeakPtr to the aura::WindowTreeHost (as seen in SelectFileDialogLinuxPortal) would also mitigate this risk.

Evaluated with Chrome root at commit: b3153093eb3c78c3e88ccf562bcbc20437a04b0e


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.

View on issue tracker