Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Navigation
DescriptionUse after free in Navigation
ComponentNavigation
Bug ClassUAF
Tracker497769116
Fix commit8e95aab414bf (chromium/src) +13/-0
CISA KEVNot listed
CreditedGoogle
Disclosed2026-04-28

Changed Functions

FunctionChangeNotes
if
content/browser/renderer_host/navigator.cc
modified
if
content/browser/web_contents/web_contents_impl.cc
modified

Files Changed

  • content/browser/renderer_host/navigator.cc
  • content/browser/web_contents/web_contents_impl.cc
From 8e95aab414bfce3dd56947c3464b5d8465e4b2bb Mon Sep 17 00:00:00 2001
From: Jordan Bayles <[email protected]>
Date: Wed, 15 Apr 2026 15:26:47 -0700
Subject: [PATCH] Fix UAF in fullscreen exit during cross-document navigation

This change addresses a Use-After-Free (UAF) vulnerability that occurs
when exiting fullscreen during a cross-document navigation, by
introducing `base::WeakPtr` checks in
`WebContentsImpl::DidNavigateAnyFramePreCommit` and
`Navigator::DidNavigate` to ensure that early destruction of
`WebContentsImpl` or `RenderFrameHostImpl` during nested message loops
does not result in dereferencing freed memory.

Bug: 497769116
Change-Id: Ife5e726df78beb2f1d51d8d782f3b87b53d65fdb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7750093
Commit-Queue: Jordan Bayles <[email protected]>
Reviewed-by: Arthur Sonzogni <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1615462}
---

diff --git a/content/browser/renderer_host/navigator.cc b/content/browser/renderer_host/navigator.cc
index 1819408..8fcff3f 100644
--- a/content/browser/renderer_host/navigator.cc
+++ b/content/browser/renderer_host/navigator.cc
@@ -525,9 +525,16 @@
 #endif  // BUILDFLAG(IS_ANDROID)
 
   // Run tasks that must execute just before the commit.
+  base::WeakPtr<RenderFrameHostImpl> weak_rfh = render_frame_host->GetWeakPtr();
   delegate_->DidNavigateAnyFramePreCommit(navigation_request.get(),
                                           was_within_same_document);
 
+  // NOTE: the pre commit tasks may result in the destruction of the render
+  // frame host, in which case we should exit this method early.
+  if (!weak_rfh) {
+    return;
+  }
+
   if (ui::PageTransitionIsMainFrame(params.transition)) {
     delegate_->DidNavigateMainFramePreCommit(navigation_request.get(),
                                              was_within_same_document);
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 736e23bf..76c3b0c5 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -7925,7 +7925,13 @@
   }
 
   if (should_exit_fullscreen) {
+    base::WeakPtr<WebContentsImpl> weak_this = weak_factory_.GetWeakPtr();
     ExitFullscreen(false);
+
+    // If `this` gets destructed due to ExitFullscreen(), we need to exit early.
+    if (!weak_this) {
+      return;
+    }
     CancelKeyboardLock(keyboard_lock_widget_);
   }
 }
Loading diff…

Original Bug Report

reported by [email protected]

Use-After-Free in Navigator::DidNavigate via ExitFullscreenModeForTab

Project Fortify, an experimental security project, has identified the following potential security issue.

Overview: A potential Use-After-Free exists when exiting fullscreen during a cross-document navigation. This can spin a nested message loop, during which the WebContents may be destroyed, leading to virtual function calls on a dangling this pointer and bypassing MiraclePtr.

Affected files:

  • content/browser/renderer_host/navigator.cc
  • content/browser/web_contents/web_contents_impl.cc

Estimated timestamp from git blame: 2026-03-06

Description

A potential Use-After-Free (UAF) vulnerability exists in the browser process, specifically in how cross-document navigations handle exiting fullscreen mode.

When a navigation commits, Navigator::DidNavigate invokes delegate_->DidNavigateAnyFramePreCommit(...) (content/browser/renderer_host/navigator.cc). The delegate is typically WebContentsImpl.

If the page is currently in fullscreen, WebContentsImpl::DidNavigateAnyFramePreCommit will attempt to exit fullscreen by calling ExitFullscreen(false), which eventually calls delegate_->ExitFullscreenModeForTab(this). As noted by inline comments in WebContentsImpl::ExitFullscreenMode (crbug.com/1506535), this delegate call may spin a nested message loop (e.g., on macOS for window transitions).

If the WebContentsImpl object is destroyed while this nested message loop is spinning (for example, if the fullscreen window is a popup and its opener closes it), the this pointers currently on the stack in Navigator::DidNavigate and WebContentsImpl::DidNavigateAnyFramePreCommit become dangling.

When the nested loop finishes, ExitFullscreenMode safely detects the destruction via a WeakPtr and returns early. However, the stack unwinds to DidNavigateAnyFramePreCommit, which immediately calls CancelKeyboardLock(keyboard_lock_widget_). This accesses the freed this pointer to read the keyboard_lock_widget_ member and perform a virtual method dispatch, leading to a UAF and potential vtable hijack.

Furthermore, the stack will unwind to Navigator::DidNavigate, which subsequently checks if the transition is a main frame and calls delegate_->DidNavigateMainFramePreCommit(...), reading the delegate_ from freed memory and performing another virtual call.

MiraclePtr (BRP) Bypass

This vulnerability naturally bypasses MiraclePtr (BackupRefPtr). WebContentsImpl embeds FrameTree directly, which in turn embeds Navigator directly. Therefore, all three reside in the exact same heap allocation. Navigator holds a raw_ptr<NavigatorDelegate> delegate_ which points to the WebContentsImpl.

When the WebContentsImpl is destroyed, the Navigator and its raw_ptr member are destroyed along with it. The raw_ptr destructor drops the BRP reference count before the memory is released to the allocator. Since the stack pointers waiting on the call stack are raw C++ pointers, the reference count drops to zero, and the memory is freed without being quarantined. An attacker can then use heap spraying to reclaim the memory with controlled data before the execution resumes.

Suggested Steps to Trigger

Note: These are potential steps as we have not verified them with a working Proof of Concept.

  1. An attacker creates a malicious webpage that opens a popup window.
  2. The user interacts with the popup, satisfying the user gesture requirement. The popup requests and enters fullscreen mode.
  3. The popup initiates a cross-document navigation to a different site.
  4. The navigation reaches the commit phase in the browser process, eventually calling WebContentsImpl::DidNavigateAnyFramePreCommit, which spins a nested message loop to exit fullscreen.
  5. While the browser is spinning the message loop, the attacker’s opener window calls popup.close(), queuing a task that synchronously destroys the popup’s WebContentsImpl.
  6. The opener window performs a heap spray via IPCs, allocating objects of the same size as WebContentsImpl to reclaim the freed memory with attacker-controlled bytes.
  7. The nested message loop completes, and the stack unwinds back to DidNavigateAnyFramePreCommit and Navigator::DidNavigate.
  8. The code dereferences the dangling this pointer (now pointing to sprayed memory) to call a virtual function, leading to arbitrary code execution in the browser process.

Proposed Fix

In WebContentsImpl::DidNavigateAnyFramePreCommit, acquire a base::WeakPtr to this before calling ExitFullscreen(false). If the WeakPtr is invalidated after the call returns, return early.

Similarly, in Navigator::DidNavigate, acquire a base::WeakPtr to the relevant object (e.g., via the RenderFrameHost or FrameTree) before the call to delegate_->DidNavigateAnyFramePreCommit. Check its validity before proceeding to call delegate_->DidNavigateMainFramePreCommit. Alternatively, delay the fullscreen exit logic and keyboard lock cancellation until after the synchronous commit sequence is complete.

Evaluated with Chrome root at commit: a9cbf6e8b275fe4147435aa905f3b7f5a656f5f0


Results from 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