Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Navigation
DescriptionUse after free in Navigation
ComponentNavigation
Bug ClassUAF
Tracker515423596
Fix commit1878bf456a25 (chromium/src) +2/-1
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-30

Files Changed

  • content/browser/renderer_host/navigation_request.cc
From 1878bf456a2521ad28ada3fd2f76d05d8153fe4a Mon Sep 17 00:00:00 2001
From: Takashi Toyoshima <toyoshim@chromium.org>
Date: Mon, 01 Jun 2026 15:32:07 -0700
Subject: [PATCH] Fix throttle name reporting in NavigationRequest::Resume.

This CL ensures that we can correctly obtain the resuming throttle's
name for the crash key when `DumpWithoutCrashing()` is triggered in
`NavigationRequest::Resume`.

Previously, if `DumpWithoutCrashing()` was triggered after the response
body callback, the resuming throttle might have already been cleaned up,
preventing us from getting its name.

This is resolved by capturing the throttle's name before executing the
callback.

BUG=515423596

Change-Id: I30a0831c7a41821baa902286dd0eab855eb0c2b5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7890808
Reviewed-by: Charlie Reis <creis@chromium.org>
Commit-Queue: Takashi Toyoshima <toyoshim@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1639730}
---

diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
index c79bb9e..7f11086 100644
--- a/content/browser/renderer_host/navigation_request.cc
+++ b/content/browser/renderer_host/navigation_request.cc
@@ -8599,12 +8599,13 @@
       CHECK(response_body_callback_);
       response_body_watcher_.reset();
       base::WeakPtr<NavigationRequest> this_ptr(weak_factory_.GetWeakPtr());
+      std::string throttle_name = resuming_throttle->GetNameForLogging();
       std::move(response_body_callback_).Run(std::string());
       if (this_ptr.WasInvalidated()) {
         // TODO(https://crbug.com/411238078): Replace the debug code with a
         // comment once we ensure that this is the root cause.
         SCOPED_CRASH_KEY_STRING32("Bug411238078", "throttle",
-                                  resuming_throttle->GetNameForLogging());
+                                  throttle_name.c_str());
         base::debug::DumpWithoutCrashing();
         return;
       }
Loading diff…

Original Bug Report

reported by vm...@google.com

Browser-Process Use-After-Free in NavigationRequest::Resume

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 potential Use-After-Free (UAF) exists in the browser process when a NavigationRequest is synchronously destroyed during a callback. Debug logging code subsequently attempts to access a NavigationThrottle object that has been freed along with its owner.

Affected files:

  • content/browser/renderer_host/navigation_request.cc
  • content/browser/renderer_host/navigation_request.h
  • content/browser/renderer_host/navigation_throttle_registry_impl.h

Estimated timestamp from git blame: 2025-05-30

Summary

A potential Use-After-Free (UAF) vulnerability has been identified in NavigationRequest::Resume within the browser process. When a NavigationRequest is synchronously destroyed during the execution of its response_body_callback_, the code enters an error-logging path that attempts to perform a virtual method call on a NavigationThrottle object that has already been deallocated.

Technical Details

The issue resides in content/browser/renderer_host/navigation_request.cc. The Resume method uses a base::WeakPtr to detect if the NavigationRequest is destroyed during a synchronous callback. The problematic code block is:

// content/browser/renderer_host/navigation_request.cc

base::WeakPtr<NavigationRequest> this_ptr(weak_factory_.GetWeakPtr());
std::move(response_body_callback_).Run(std::string());
if (this_ptr.WasInvalidated()) {
  // TODO(https://crbug.com/411238078): Replace the debug code with a
  // comment once we ensure that this is the root cause.
  SCOPED_CRASH_KEY_STRING32("Bug411238078", "throttle",
                            resuming_throttle->GetNameForLogging());
  base::debug::DumpWithoutCrashing();
  return;
}

When response_body_callback_.Run() is executed, it can trigger logic that synchronously destroys the NavigationRequest. Since the NavigationRequest owns the NavigationThrottleRegistryImpl, which in turn owns the NavigationThrottle instances, destruction of the request causes the resuming_throttle object to be freed.

Upon returning from the callback, this_ptr.WasInvalidated() evaluates to true. The code then attempts to call resuming_throttle->GetNameForLogging(). Because GetNameForLogging() is a virtual function and resuming_throttle is a raw pointer on the stack (which is not protected by MiraclePtr/BackupRefPtr), this results in a virtual method dispatch through the vtable of a freed object.

Impact

An attacker who can control navigation timing and trigger synchronous navigation destruction during this specific callback could potentially reclaim the freed memory. By hijacking the virtual function call, an attacker could achieve arbitrary code execution in the browser process, leading to a full sandbox escape.

Suggested/Potential Steps to Reproduce

  1. Initiate a navigation that triggers a throttle utilizing GetResponseBody (e.g., ProfileManagementNavigationThrottle).
  2. The throttle’s WillProcessResponse calls navigation_handle()->GetResponseBody(callback) and returns DEFER.
  3. Trigger a Resume() call for the throttle while the callback is pending.
  4. Ensure the callback execution synchronously destroys the NavigationRequest (for instance, by starting a new navigation in the same frame).
  5. Observe a crash or unexpected behavior when the logging code attempts to access the invalidated resuming_throttle pointer.

Suggested Fix

Avoid accessing the resuming_throttle pointer if this_ptr.WasInvalidated() is true. The logging should either be removed or modified to use a value (such as the throttle name) that was captured before the callback was executed.

if (this_ptr.WasInvalidated()) {
  // Return immediately without dereferencing the potentially freed resuming_throttle.
  return;
}

Evaluated with Chrome root at commit: 29093e11cf509e3593f6229e4b1b075cca356049


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
Links in the report