CVE-2026-95319
Overview
Background
- `PrintViewManager`
- The
WebContents-scoped browser-process object that coordinates print and Print Preview requests for a tab. - `PrintPreviewDialogController::PrintPreview()`
- The call that opens the Print Preview dialog, and which may exit fullscreen as a side effect.
- Use-after-free (UAF)
- A memory-safety bug where code continues to touch an object (here
this) after it has been destroyed. - `weak_factory_`
- A
WeakPtrFactorymember whoseWeakPtrbecomes null once the owning object is destroyed, letting callers detect that destruction.
Root Cause Analysis
In PrintViewManager::OnRequestPrintPreviewCallback(), the code obtained the PrintPreviewDialogController and called dialog_controller->PrintPreview(), then continued executing on this (for example calling PrintPreviewAllowedForTesting()). The problem is that PrintPreview() may exit fullscreen, and exiting fullscreen can synchronously destroy the PrintViewManager that owns the current stack frame, violating the invariant that this remains valid for the remainder of the method. Because the initiator could be closed mid-call, any subsequent access to this was a use-after-free.
The fix captures auto weak_this = weak_factory_.GetWeakPtr(); before the PrintPreview() call and returns early when !weak_this, so no further work touches the freed object.
The patch also documents why the sibling OnScriptedPrintPreviewCallback() path is safe: it already exited fullscreen earlier, so its PrintPreview() cannot trigger the same re-entrant destruction.
this survives the call to PrintPreviewDialogController::PrintPreview(), when that call can re-entrantly exit fullscreen and destroy the PrintViewManager; the fix detects that destruction with a WeakPtr and bails out before touching freed memory.Attack Path
- Trigger a print preview request
An attacker-controlled page issues a request that reaches
PrintViewManager::OnRequestPrintPreviewCallback()in the browser process. - Enter a fullscreen state
The tab is in a fullscreen mode such that the ensuing
PrintPreview()will need to exit fullscreen. - Force re-entrant teardown
The call to
dialog_controller->PrintPreview()exits fullscreen, which synchronously closes the initiator and destroys thePrintViewManager(this). - Reach freed-object access
Control returns into
OnRequestPrintPreviewCallback(), which continues to use the now-freedthis, producing a use-after-free that a crafted heap layout can steer toward controlled corruption.
Impact Assessment
PrintViewManager in the privileged browser process, which can lead to memory corruption and potentially code execution outside the renderer sandbox. Exploitation requires driving the tab into a fullscreen state and issuing a print preview request whose PrintPreview() call exits fullscreen and destroys the manager mid-callback.Changed Functions
| Function | Change | Notes |
|---|---|---|
ifchrome/browser/printing/print_view_manager.cc |
modified |
Files Changed
chrome/browser/printing/print_view_manager.cc
Audit Directions
- Re-entrant destruction after callbacksAudit browser-process methods that call into UI or fullscreen-exiting code and then keep using
this; guard them with aWeakPtrcheck. - Fullscreen side effectsTrace any call path where exiting fullscreen can synchronously destroy
WebContents-scoped observers likePrintViewManager, and verify no member access follows. - Asymmetric sibling handlersCompare paired callbacks such as
OnRequestPrintPreviewCallbackandOnScriptedPrintPreviewCallback, confirming that a safety check applied to one is either replicated or provably unnecessary in the other.
Patch
From 16142cbd5000e21d08d6140f0c19d38c03231a7b Mon Sep 17 00:00:00 2001
From: Lei Zhang <thestig@chromium.org>
Date: Thu, 27 Aug 2026 11:32:16 -0700
Subject: [PATCH] printing: Handle initiator being closed during PrintPreview()
PrintViewManager::OnRequestPrintPreviewCallback() calls
PrintPreviewDialogController::PrintPreview(), which may result in
PrintViewManager destruction. Check for that and exit early if needed.
Add comments to explain why this is different from
OnScriptedPrintPreviewCallback(), which also called PrintPreview().
Bug: 523765972
Change-Id: Iee0a91c2a698de147f706ff66871c7e2dc578851
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8301136
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1687427}
---
diff --git a/chrome/browser/printing/print_view_manager.cc b/chrome/browser/printing/print_view_manager.cc
index 82a478d..1b70189 100644
--- a/chrome/browser/printing/print_view_manager.cc
+++ b/chrome/browser/printing/print_view_manager.cc
@@ -581,6 +581,8 @@
auto* dialog_controller = PrintPreviewDialogController::GetInstance();
CHECK(dialog_controller);
mojom::RequestPrintPreviewParams params;
+ // Note that this PrintPreview() call will not exit fullscreen, since the code
+ // above just did that. So no need to check if `this` got destroyed.
dialog_controller->PrintPreview(web_contents(), params,
print_preview_rfh_->GetProcess()->IsPdf());
@@ -632,8 +634,14 @@
auto* dialog_controller = PrintPreviewDialogController::GetInstance();
CHECK(dialog_controller);
+ // Note that this PrintPreview() call may exit fullscreen, so `this` may get
+ // destroyed.
+ auto weak_this = weak_factory_.GetWeakPtr();
dialog_controller->PrintPreview(web_contents(), *params,
render_frame_host->GetProcess()->IsPdf());
+ if (!weak_this) {
+ return;
+ }
PrintPreviewAllowedForTesting();
}
Original Bug Report
Potential Use-After-Free in PrintViewManager via ExitFullscreen nested loop
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 vulnerability exists in PrintViewManager when initiating a print preview. Showing the preview dialog forces the tab out of HTML fullscreen, which on Windows and macOS spins a nested message loop; closing the tab during this loop destroys the PrintViewManager, leading to a virtual function call on the freed object upon return.
Affected files:
chrome/browser/printing/print_view_manager.cc
Estimated timestamp from git blame: 2021-09-27
Description
A potential Use-After-Free (UAF) vulnerability has been identified in PrintViewManager::OnRequestPrintPreviewCallback (chrome/browser/printing/print_view_manager.cc).
When a renderer requests a print preview, PrintViewManager calls PrintPreviewDialogController::PrintPreview to display the modal preview dialog. Showing a modal dialog requires dropping HTML Fullscreen mode to ensure the user has context. To do this, BrowserWindowModalDialogDelegate::SetWebContentsBlocked calls web_contents->ExitFullscreen(true).
On Windows (via ::SetWindowPos) and macOS (via [NSWindow toggleFullScreen:]), exiting fullscreen spins a native nested message loop. If an attacker closes the tab while this nested loop is running, the WebContents and its associated WebContentsUserData (including the PrintViewManager instance) are synchronously destroyed.
When the nested loop finishes, the stack unwinds back to PrintViewManager::OnRequestPrintPreviewCallback. The function then calls PrintPreviewAllowedForTesting() using the implicit this pointer. Because this is a raw pointer on the execution stack, it is not protected by MiraclePtr (BackupRefPtr). Since the object has been destroyed, this results in a virtual method call on a freed object, which can be exploited for Control-Flow Hijacking and Remote Code Execution (RCE) in the Browser Process.
Potential Steps to Trigger
- An attacker-controlled renderer opens two tabs (Tab A and Tab B) in the same window.
- In Tab A, the attacker uses JavaScript to enter HTML Fullscreen (
document.documentElement.requestFullscreen()). - Tab A sends a
mojom::PrintManagerHost::RequestPrintPreviewIPC. The browser binds aWeakPtrand invokesPrintViewManager::OnRequestPrintPreviewCallback. - Inside the callback, the raw
thispointer is extracted from theWeakPtrto execute the method. The code callsdialog_controller->PrintPreview(). - The UI framework attempts to show the modal dialog, which triggers
web_contents->ExitFullscreen(true). - The platform-specific
ExitFullscreenimplementation spins a nested message loop. - During the nested loop, Tab B sends a
ClosePageIPC to close Tab A. Tab A’sWebContentsandPrintViewManagerare destroyed. - The attacker performs heap spraying (e.g., via other IPCs) to replace the freed
PrintViewManagerobject in memory with a fake vtable. - The nested loop finishes. The stack unwinds.
- Note: The code bypasses a
CHECKinTabStripModel::SetTabBlockedduring unwinding because Tab B is still open, keeping the stale tab index within bounds. - Note: The code bypasses a
CHECK(weak_widget)inShowWebModalDialogViewsbecause the native dialog widget is closed asynchronously and still exists during the unwind.
- Note: The code bypasses a
- Execution returns to
OnRequestPrintPreviewCallback. The code callsPrintPreviewAllowedForTesting()on the freedthispointer. - The virtual call uses the attacker’s fake vtable. On Windows, Control Flow Guard (CFG) only verifies that the target is a valid function start, allowing the attacker to jump to an arbitrary ROP gadget and achieve RCE.
(Note: These are suggested steps based on code analysis; our tooling agent does not have the ability to run code to verify an exploit end-to-end.)
Suggested Fix
Unlike OnScriptedPrintPreviewCallback, which checks print_preview_state_ and explicitly handles ExitFullscreen with a WeakPtr guard, OnRequestPrintPreviewCallback lacks these protections.
The fix should guard the execution after PrintPreview() returns by checking if this is still valid. You can acquire a WeakPtr before the call and check it afterward:
auto weak_this = weak_factory_.GetWeakPtr();
dialog_controller->PrintPreview(web_contents(), *params);
if (!weak_this) {
return;
}
PrintPreviewAllowedForTesting();
Alternatively, verify print_preview_state_ before proceeding, similar to how OnScriptedPrintPreviewCallback operates.
Evaluated with Chrome root at commit: 65b3256311f3ab6fb9870eaa522de7e6dd2663bb
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.