Chrome · Navigation
CVE-2026-84355
Logic Error in Navigation
Overview
Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Files Changed
content/browser/renderer_host/navigation_controller_impl_browsertest.cccontent/browser/renderer_host/navigation_request.cccontent/browser/renderer_host/navigation_request.h
Patch
From 35ec6fdc64cf53e460fcf54bb1aebe291889c53b Mon Sep 17 00:00:00 2001
From: Noam Rosenthal <nrosenthal@chromium.org>
Date: Thu, 03 Sep 2026 11:12:46 -0700
Subject: [PATCH] Reland "Use committed origin when deciding whether to dispatch navigate event"
This is a reland of commit cc3f09b3a5179e1232fa27f898fa165cb06fc98b.
Original change caused test failures in
All/NavigationControllerBrowserTest.NavigateEventNotFiredForTraversalToCSPSandboxedEntry/*
on builders where DeferSpeculativeRFHCreation is disabled or not applicable
(e.g., fuchsia-arm64-cast-receiver-rel).
Root cause:
In DidCreateNavigationRequest, when DeferSpeculativeRFHCreation is disabled,
RenderFrameHostManager::GetFrameHostForNavigation() runs synchronously at
kBeforeNetworkRequest to create a speculative RFH. At this stage, HTTP
response headers have not yet arrived. NavigationRequest::GetUrlInfo()
only estimated sandbox flags from commit_params_->frame_policy.sandbox_flags,
which is empty for main-frame CSP sandboxing. As a result, UrlInfo was
estimated as unsandboxed, causing CanUseDestinationInstance() to conclude
that the sandboxed destination SiteInstance was unsuitable.
RenderFrameHostManager::GetSiteInstanceForNavigationRequest() observed
this mismatch (request->dest_site_instance() != dest_site_instance) and
called ResetStateForSiteInstanceChange(), which reset origin_related_state_.
When MaybeDispatchNavigateEventForCrossDocumentTraversal() subsequently ran,
origin_related_state_->committed_origin was missing, causing it to fall
back to resolving the pre-redirect URL as same-origin and firing the navigate
event unexpectedly.
Fix:
When computing UrlInfo before policies are computed, consider
dest_site_instance_->GetSiteInfo().IsSandboxed() when estimating sandbox
flags for history navigations.
Original change description:
> Use committed origin when deciding whether to dispatch navigate event
>
> The committed origin can be opaque in sandboxed iframes, and is also
> pre-redirect.
>
> Bug: 511774376
> Change-Id: Ia5f4257a907274f24abb32186ae0e074eb488716
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8041464
> Reviewed-by: Charlie Reis <creis@chromium.org>
> Commit-Queue: Noam Rosenthal <nrosenthal@google.com>
> Reviewed-by: Nate Chapin <japhet@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1662779}
Bug: 511774376
Change-Id: Id05f189baabd0ef75e52dbf040ac5463b6d23cde
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8345238
Commit-Queue: Noam Rosenthal <nrosenthal@google.com>
Reviewed-by: Charlie Reis <creis@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1691774}
---
diff --git a/content/browser/renderer_host/navigation_controller_impl_browsertest.cc b/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
index 7e83a45..65e762e 100644
--- a/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
+++ b/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
@@ -22020,6 +22020,10 @@
EXPECT_EQ(initial_site_instance, contents()->GetSiteInstance());
}
+// TODO(crbug.com/511774376): Add a browser test to verify that the navigate
+// event for a cross-document history traversal is not dispatched when the
+// destination entry was committed at an opaque origin due to CSP sandbox.
+
IN_PROC_BROWSER_TEST_P(NavigationControllerBrowserTest,
NavigateToNavigationApiKey_NullCommittedOrigin) {
// Ensure there's a history entry before the error page.
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
index 63b4aeb..b7e23d3 100644
--- a/content/browser/renderer_host/navigation_request.cc
+++ b/content/browser/renderer_host/navigation_request.cc
@@ -1889,6 +1889,7 @@
origin_related_state_.emplace(OriginRelatedState{
.item_sequence_number = frame_entry->item_sequence_number(),
.document_sequence_number = frame_entry->document_sequence_number(),
+ .committed_origin = frame_entry->committed_origin(),
});
}
@@ -4759,10 +4760,12 @@
//
// If PolicyContainer::ComputePoliciesToCommit() has run
// `policy_container_builder_` will be valid, but even if it hasn't, we can
- // speculatively take `commit_params_->frame_policy.sandbox_flags` if we
- // haven't received the response yet and don't have the final
- // `policy_container_builder_`, and if the state of the kOrigin flag changes,
- // we'll detect the change and recompute the target SiteInstance elsewhere.
+ // speculatively take `commit_params_->frame_policy.sandbox_flags` (or
+ // `dest_site_instance_` for history navigations) if we haven't received the
+ // response yet and don't have the final `policy_container_builder_`, to
+ // reduce the chance that recomputing will be needed. If the state of the
+ // kOrigin flag changes, we'll detect the change and recompute the target
+ // SiteInstance elsewhere.
//
// In general, about:blank documents should stay in their initiator's process.
// If neither the initiator or about:blank is sandboxed, or if both are, then
@@ -4792,12 +4795,15 @@
// Note: We'll end up here if this function is called before
// ComputePoliciesToCommit(), such as when computing a speculative
// RenderFrameHost's SiteInstance before receiving a response. In that
- // event we use the sandbox flags in commit_params_ as a current "best
+ // event we use the sandbox flags in commit_params_ (or the destination
+ // SiteInstance, if this is a history navigation) as a current "best
// estimate".
has_origin_restricted_sandbox_flag =
- (commit_params_->frame_policy.sandbox_flags &
- network::mojom::WebSandboxFlags::kOrigin) ==
- network::mojom::WebSandboxFlags::kOrigin;
+ ((commit_params_->frame_policy.sandbox_flags &
+ network::mojom::WebSandboxFlags::kOrigin) ==
+ network::mojom::WebSandboxFlags::kOrigin) ||
+ (dest_site_instance_ &&
+ dest_site_instance_->GetSiteInfo().IsSandboxed());
}
// It's possible that a sandbox attribute can disappear from a frame that
@@ -12030,13 +12036,19 @@
blink::mojom::NavigationType::HISTORY_DIFFERENT_DOCUMENT) {
return;
}
- // Only fire the navigate event if the destination is same-origin. Because
- // this check is performed at navigation start time, `destination_origin` is
+ // Only fire the navigate event if the destination is same-origin. Prefer
+ // the origin recorded when the destination entry previously committed, since
+ // the URL alone does not reflect opaque origins resulting from CSP sandbox.
+ // See also PopulateSingleNavigationApiHistoryEntryVector(). Because this
+ // check is performed at navigation start time, the URL-derived fallback is
// based on the pre-redirect URL, which is consistent with the renderer
// process logic for firing the navigate event for non-history navigations.
- url::Origin destination_origin = url::Origin::Resolve(
- common_params_->url,
- common_params_->initiator_origin.value_or(url::Origin()));
+ url::Origin destination_origin =
+ origin_related_state_ && origin_related_state_->committed_origin
+ ? *origin_related_state_->committed_origin
+ : url::Origin::Resolve(
+ common_params_->url,
+ common_params_->initiator_origin.value_or(url::Origin()));
if (!frame_tree_node_->current_origin().IsSameOriginWith(
destination_origin)) {
return;
diff --git a/content/browser/renderer_host/navigation_request.h b/content/browser/renderer_host/navigation_request.h
index da65f8c..78a097fc 100644
--- a/content/browser/renderer_host/navigation_request.h
+++ b/content/browser/renderer_host/navigation_request.h
@@ -3209,6 +3209,9 @@
struct OriginRelatedState {
int64_t item_sequence_number;
int64_t document_sequence_number;
+ // The origin that was recorded when the FrameNavigationEntry was previously
+ // committed, if any. May be nullopt for entries that have never committed.
+ std::optional<url::Origin> committed_origin;
};
std::optional<OriginRelatedState> origin_related_state_;
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/content/browser/renderer_host/navigation_controller_impl_browsertest.cc b/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
index 7e83a45..65e762e 100644
--- a/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
+++ b/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
@@ -22020,6 +22020,10 @@
EXPECT_EQ(initial_site_instance, contents()->GetSiteInstance());
}
+// TODO(crbug.com/511774376): Add a browser test to verify that the navigate
+// event for a cross-document history traversal is not dispatched when the
+// destination entry was committed at an opaque origin due to CSP sandbox.
+
IN_PROC_BROWSER_TEST_P(NavigationControllerBrowserTest,
NavigateToNavigationApiKey_NullCommittedOrigin) {
// Ensure there's a history entry before the error page.
Loading diff…
Original Bug Report
The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.
References
On This Page