Low chrome Logic Error 🔧 Commit mapped

Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactMissing authorization in Preload
DescriptionMissing authorization in Preload
ComponentPreload
Bug ClassLogic Error
Tracker497499482
Fix commitfcd4f10edbdc (chromium/src) +44/-14
CISA KEVNot listed
CreditedGoogle
Disclosed2026-08-25

Files Changed

  • chrome/browser/preloading/prefetch/no_state_prefetch/prerender_nostate_prefetch_browsertest.cc
  • components/no_state_prefetch/browser/no_state_prefetch_contents.cc
  • components/no_state_prefetch/browser/no_state_prefetch_contents.h
From fcd4f10edbdcee80d2f510d95b32cb5bfcc3abbb Mon Sep 17 00:00:00 2001
From: Test User <[email protected]>
Date: Fri, 03 Jul 2026 03:16:28 -0700
Subject: [PATCH] Give the prefetch WebContents its own SessionStorageNamespace

NoStatePrefetchContents was passing the launching tab's
SessionStorageNamespace into WebContents::CreateWithSessionStorage. This
was a leftover from the old swap-in prerenderer; NSP contents are never
swapped in, so the hidden WebContents has no need for the launcher's
session storage data and should not share its namespace with another
WebContents. Create the prefetch WebContents with a fresh namespace via
WebContents::Create instead, and keep recording the launcher's namespace
id for use in Matches() only.

Add a browser test that verifies the prefetch WebContents has a distinct
namespace id from the launching tab while still matching it.

TAG=agy
CONV=dbc0ad8c-2f13-498b-81e0-53ee084728bc

Fixed: 497499482
Change-Id: I129d9abcab0fa1661e3b935d681cad42f2266909
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8034880
Commit-Queue: Minoru Chikamune <[email protected]>
Reviewed-by: Hiroki Nakagawa <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1656467}
---

diff --git a/chrome/browser/preloading/prefetch/no_state_prefetch/prerender_nostate_prefetch_browsertest.cc b/chrome/browser/preloading/prefetch/no_state_prefetch/prerender_nostate_prefetch_browsertest.cc
index e850512d..a553c2d 100644
--- a/chrome/browser/preloading/prefetch/no_state_prefetch/prerender_nostate_prefetch_browsertest.cc
+++ b/chrome/browser/preloading/prefetch/no_state_prefetch/prerender_nostate_prefetch_browsertest.cc
@@ -62,8 +62,10 @@
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/browsing_data_remover.h"
+#include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
+#include "content/public/browser/session_storage_namespace.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -1770,6 +1772,37 @@
   }
 }
 
+// Checks that the hidden WebContents created for a prefetch is given its own
+// SessionStorageNamespace rather than the namespace of the launching tab. The
+// prefetch contents are never swapped in, so they have no need for the
+// launcher's session storage data, and the launcher's namespace should remain
+// available for matching only.
+IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest,
+                       PrefetchUsesIsolatedSessionStorageNamespace) {
+  GURL url = src_server()->GetURL(kHungPrerenderPage);
+  std::unique_ptr<TestPrerender> prerender =
+      PrefetchFromURL(url, FINAL_STATUS_CANCELLED, 0);
+
+  ASSERT_TRUE(prerender->contents());
+  content::WebContents* prefetch_web_contents =
+      prerender->contents()->no_state_prefetch_contents();
+  ASSERT_TRUE(prefetch_web_contents);
+
+  content::SessionStorageNamespace* launcher_namespace =
+      GetSessionStorageNamespace();
+  ASSERT_TRUE(launcher_namespace);
+  content::SessionStorageNamespace* prefetch_namespace =
+      prefetch_web_contents->GetController()
+          .GetDefaultSessionStorageNamespace();
+  ASSERT_TRUE(prefetch_namespace);
+
+  EXPECT_NE(launcher_namespace->id(), prefetch_namespace->id());
+  EXPECT_TRUE(prerender->contents()->Matches(url, launcher_namespace));
+
+  GetNoStatePrefetchManager()->CancelAllPrerenders();
+  prerender->WaitForStop();
+}
+
 // Cancels the prerender of a page with its own prerender.  The second prerender
 // should never be started.
 IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest,
diff --git a/components/no_state_prefetch/browser/no_state_prefetch_contents.cc b/components/no_state_prefetch/browser/no_state_prefetch_contents.cc
index a7317c8..42fa693 100644
--- a/components/no_state_prefetch/browser/no_state_prefetch_contents.cc
+++ b/components/no_state_prefetch/browser/no_state_prefetch_contents.cc
@@ -280,7 +280,7 @@
   SetPreloadingTriggeringOutcome(
       attempt_.get(), content::PreloadingTriggeringOutcome::kRunning);
 
-  no_state_prefetch_contents_ = CreateWebContents(session_storage_namespace);
+  no_state_prefetch_contents_ = CreateWebContents();
   no_state_prefetch_contents_->SetOwnerLocationForDebug(FROM_HERE);
   content::WebContentsObserver::Observe(no_state_prefetch_contents_.get());
   delegate_->OnNoStatePrefetchContentsCreated(
@@ -348,14 +348,11 @@
   observer_list_.RemoveObserver(observer);
 }
 
-std::unique_ptr<WebContents> NoStatePrefetchContents::CreateWebContents(
-    SessionStorageNamespace* session_storage_namespace) {
-  // TODO(ajwong): Remove the temporary map once prerendering is aware of
-  // multiple session storage namespaces per tab.
-  return WebContents::CreateWithSessionStorage(
-      WebContents::CreateParams(browser_context_),
-      CreateMapWithDefaultSessionStorageNamespace(browser_context_,
-                                                  session_storage_namespace));
+std::unique_ptr<WebContents> NoStatePrefetchContents::CreateWebContents() {
+  // The hidden WebContents is never swapped in, so it gets its own session
+  // storage namespace rather than sharing the launcher tab's namespace. The
+  // launcher's namespace id is recorded separately for matching.
+  return WebContents::Create(WebContents::CreateParams(browser_context_));
 }
 
 void NoStatePrefetchContents::NotifyPrefetchStart() {
diff --git a/components/no_state_prefetch/browser/no_state_prefetch_contents.h b/components/no_state_prefetch/browser/no_state_prefetch_contents.h
index 985a12e..0fe5909 100644
--- a/components/no_state_prefetch/browser/no_state_prefetch_contents.h
+++ b/components/no_state_prefetch/browser/no_state_prefetch_contents.h
@@ -112,9 +112,10 @@
 
   // Starts rendering the contents in the prerendered state.
   // |bounds| indicates the rectangle that the prerendered page should be in.
-  // |session_storage_namespace| indicates the namespace that the prerendered
-  // page should be part of. |preloading_attempt| allows to log metrics for this
-  // NoStatePrefetch attempt.
+  // |session_storage_namespace| indicates the namespace of the launching tab
+  // and is recorded for use in Matches(); the hidden WebContents is given its
+  // own independent namespace. |preloading_attempt| allows to log metrics for
+  // this NoStatePrefetch attempt.
   virtual void StartPrerendering(
       const gfx::Rect& bounds,
       content::SessionStorageNamespace* session_storage_namespace,
@@ -215,8 +216,7 @@
   void NotifyPrefetchStopLoading();
   void NotifyPrefetchStop();
 
-  std::unique_ptr<content::WebContents> CreateWebContents(
-      content::SessionStorageNamespace* session_storage_namespace);
+  std::unique_ptr<content::WebContents> CreateWebContents();
 
   bool prefetching_has_started_ = false;
 
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/chrome/browser/preloading/prefetch/no_state_prefetch/prerender_nostate_prefetch_browsertest.cc b/chrome/browser/preloading/prefetch/no_state_prefetch/prerender_nostate_prefetch_browsertest.cc
index e850512d..a553c2d 100644
--- a/chrome/browser/preloading/prefetch/no_state_prefetch/prerender_nostate_prefetch_browsertest.cc
+++ b/chrome/browser/preloading/prefetch/no_state_prefetch/prerender_nostate_prefetch_browsertest.cc
@@ -62,8 +62,10 @@
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/browsing_data_remover.h"
+#include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
+#include "content/public/browser/session_storage_namespace.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -1770,6 +1772,37 @@
   }
 }
 
+// Checks that the hidden WebContents created for a prefetch is given its own
+// SessionStorageNamespace rather than the namespace of the launching tab. The
+// prefetch contents are never swapped in, so they have no need for the
+// launcher's session storage data, and the launcher's namespace should remain
+// available for matching only.
+IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest,
+                       PrefetchUsesIsolatedSessionStorageNamespace) {
+  GURL url = src_server()->GetURL(kHungPrerenderPage);
+  std::unique_ptr<TestPrerender> prerender =
+      PrefetchFromURL(url, FINAL_STATUS_CANCELLED, 0);
+
+  ASSERT_TRUE(prerender->contents());
+  content::WebContents* prefetch_web_contents =
+      prerender->contents()->no_state_prefetch_contents();
+  ASSERT_TRUE(prefetch_web_contents);
+
+  content::SessionStorageNamespace* launcher_namespace =
+      GetSessionStorageNamespace();
+  ASSERT_TRUE(launcher_namespace);
+  content::SessionStorageNamespace* prefetch_namespace =
+      prefetch_web_contents->GetController()
+          .GetDefaultSessionStorageNamespace();
+  ASSERT_TRUE(prefetch_namespace);
+
+  EXPECT_NE(launcher_namespace->id(), prefetch_namespace->id());
+  EXPECT_TRUE(prerender->contents()->Matches(url, launcher_namespace));
+
+  GetNoStatePrefetchManager()->CancelAllPrerenders();
+  prerender->WaitForStop();
+}
+
 // Cancels the prerender of a page with its own prerender.  The second prerender
 // should never be started.
 IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest,
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.