CVE-2026-10894
Overview
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 itswindows()list when they are destroyed, giving safe liveness checks. - D-Bus portal handshake
RequestXdgDesktopPortalplus 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.
aura::WindowTracker that self-clears on window destruction and makes the completion callbacks check windows() (cancelling when empty) before any dereference.Attack Path
- Initiate portal print
Code calls
PrintDialogLinuxPortal::ShowDialogwith a parentaura::Window, which (pre-patch) caches it inparent_view_and starts the asyncRequestXdgDesktopPortalhandshake. - Destroy the parent window
During the outstanding handshake the parent window is destroyed (e.g., the originating tab/window is closed), freeing the
aura::Windowwhile the rawparent_view_still points at it. - Completion callback fires
OnPortalAvailableruns and readsparent_view_, callingwindow->GetRootWindow()/ExportWindowHandle(or the fallback path passes it tofallback_dialog_->ShowDialog) on the freed object — a use-after-free. - 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
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
| Function | Change | Notes |
|---|---|---|
ifcomponents/printing/common/print_dialog_linux_portal.cc |
modified |
Files Changed
components/printing/common/print_dialog_linux_portal.cccomponents/printing/common/print_dialog_linux_portal.h
Audit Directions
- Raw native-window pointers across async boundariesFlag any
aura::Window*/gfx::NativeViewcached in a member and used after an async callback (D-Bus, Mojo, posted task); preferaura::WindowTrackeror aWeakPtr. - Portal / IPC completion callbacksReview xdg-desktop-portal and other async handshake completions that dereference window/view state captured at request time.
- "Provided but gone" handlingAudit async flows that must distinguish “no window supplied” from “window supplied but destroyed,” ensuring the latter cancels rather than proceeds with a stale pointer.
Patch
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_;
Original Bug Report
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.cccomponents/printing/common/print_dialog_linux_portal.hui/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)
- A compromised renderer opens a popup window.
- The renderer triggers a scripted print request for that popup.
- The browser process initiates the
PrintDialogLinuxPortaland the asynchronous D-Bus handshake. - The renderer closes the popup window, destroying the associated
aura::Windowwhile the handshake is pending. - The attacker attempts to reclaim the freed memory in the browser process using heap spraying techniques.
- Upon completion of the D-Bus call,
OnPortalAvailableexecutes, 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.