CVE-2026-14006
Overview
Files Changed
content/browser/renderer_host/navigation_request.cc
Patch
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;
}
Original Bug Report
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.cccontent/browser/renderer_host/navigation_request.hcontent/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
- Initiate a navigation that triggers a throttle utilizing
GetResponseBody(e.g.,ProfileManagementNavigationThrottle). - The throttle’s
WillProcessResponsecallsnavigation_handle()->GetResponseBody(callback)and returnsDEFER. - Trigger a
Resume()call for the throttle while the callback is pending. - Ensure the callback execution synchronously destroys the
NavigationRequest(for instance, by starting a new navigation in the same frame). - Observe a crash or unexpected behavior when the logging code attempts to access the invalidated
resuming_throttlepointer.
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.