CVE-2026-87619
Overview
Background
- Prefetch
- Chromium’s speculative loading mechanism that fetches a resource before navigation, implemented in
content::PrefetchService. - StoragePartition
- the isolated storage container (cookies, cache, service workers) that a
BrowserContextassigns to a given origin or embedder context. - Default storage partition
- the
StoragePartitionreturned byBrowserContext::GetDefaultStoragePartition(), used for ordinary top-level browsing. - Renderer initiator info
- the
GetRendererInitiatorInfo()record on a prefetch request that ties the request back to theRenderFrameHostthat initiated it.
Root Cause Analysis
The eligibility check in content/browser/preloading/prefetch/prefetch_service.cc only compared default_storage_partition against GetStoragePartitionForUrl(params.url), verifying that the target URL mapped to the default partition. It never verified that the initiating frame itself lived in the default partition, so a prefetch begun from a frame hosted in a non-default StoragePartition was still treated as eligible and serviced through the default partition. This violated the invariant that prefetch’s cookie and service-worker checks must run against the same partition as the initiator, producing an observable cross-partition discrepancy.
The fix computes initiator_storage_partition from renderer_info->GetRenderFrameHost()->GetStoragePartition() and adds it as a rejection condition, so any prefetch whose initiator is not in the default partition is finished as PreloadingEligibility::kNonDefaultStoragePartition.
initiator_storage_partition before allowing prefetch to proceed.Attack Path
- Host a frame in a non-default partition
An embedder scenario places a frame (with an initiating
RenderFrameHost) inside a non-defaultStoragePartition. - Trigger a prefetch That frame initiates a prefetch whose target URL resolves to the default storage partition.
- Bypass the partition guard The old check passes because only the target URL’s partition matched the default, so eligibility is granted.
- Observe the discrepancy Cookie and service-worker state from the default partition influence the prefetch behavior, exposing an observable difference across the partition boundary.
Impact Assessment
StoragePartition. Consistent with the metadata, the impact is a low-severity information/observability leak rather than memory corruption.Files Changed
content/browser/preloading/prefetch/prefetch_service.cc
Audit Directions
- Initiator-vs-target partition parityWherever prefetch, prerender, or speculative loading makes an eligibility decision, confirm the initiator’s
StoragePartitionis validated, not just the target URL’s. - Optional initiator infoAudit uses of
GetRendererInitiatorInfo()andGetRenderFrameHost()for null cases, ensuring a missing initiator defaults safely to the default partition as the fix does. - Cross-partition observabilityReview other browser-process checks that gate on cookies or service workers to ensure they run against a single consistent partition, avoiding observable discrepancies across partition boundaries.
Patch
From 9a5cd91885172d12f35c0f826c5f9c729973231b Mon Sep 17 00:00:00 2001 From: Test User <[email protected]> Date: Thu, 25 Jun 2026 22:27:43 -0700 Subject: [PATCH] Bugfix: This CL fixes a bug reported in crbug.com/497491557 Analysis Document: https://docs.google.com/document/d/16iYyG4o-2tAOFEJ1mIkzk6_EQmxMbjM9sX40sLNBUiE/edit?resourcekey=0-JEFuIGKb8N7MgyRLpV9TtA&tab=t.0#heading=h.25s5vbi8dmhl Bug: 497491557 Change-Id: I788105707bec3c840395a00fc1817e84f219fe5f Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7901928 Commit-Queue: Minoru Chikamune <[email protected]> Reviewed-by: Kouhei Ueno <[email protected]> Cr-Commit-Position: refs/heads/main@{#1652905} --- diff --git a/content/browser/preloading/prefetch/prefetch_service.cc b/content/browser/preloading/prefetch/prefetch_service.cc index 4f9956a..0aaf4203 100644 --- a/content/browser/preloading/prefetch/prefetch_service.cc +++ b/content/browser/preloading/prefetch/prefetch_service.cc @@ -947,9 +947,17 @@ // check for service workers and existing cookies. StoragePartition* default_storage_partition = browser_context_->GetDefaultStoragePartition(); - if (default_storage_partition != - browser_context_->GetStoragePartitionForUrl(params.url, - /*can_create=*/false)) { + StoragePartition* initiator_storage_partition = default_storage_partition; + if (auto* renderer_info = params.request().GetRendererInitiatorInfo()) { + if (auto* rfh = renderer_info->GetRenderFrameHost()) { + initiator_storage_partition = rfh->GetStoragePartition(); + } + } + + if (initiator_storage_partition != default_storage_partition || + default_storage_partition != + browser_context_->GetStoragePartitionForUrl(params.url, + /*can_create=*/false)) { std::move(params).Finish( PreloadingEligibility::kNonDefaultStoragePartition); return;