Overview

Critical
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Printing
DescriptionUse after free in Printing
ComponentPrinting
Bug ClassUAF
Tracker513445101
Fix commit228627507b69 (chromium/src) +26/-5
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-02

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
From 228627507b69a7b7fd93b40334d47d2fe7e93072 Mon Sep 17 00:00:00 2001
From: Tom Anderson <[email protected]>
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 <[email protected]>
Reviewed-by: Lei Zhang <[email protected]>
Commit-Queue: Lei Zhang <[email protected]>
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 [email protected]

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