Low chrome Memory Corruption 🔧 Commit mapped

Overview

Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactMemory corruption in Navigation
DescriptionMemory corruption in Navigation
ComponentNavigation
Sub-componentiOS
Bug ClassMemory Corruption
Tracker553661952
Fix commit1fea4f847605 (chromium/src) +43/-7
CISA KEVNot listed
CreditedGoogle
Disclosed2026-10-20

Changed Functions

FunctionChangeNotes
TEST_P
ios/web/navigation/navigation_manager_impl_unittest.mm
modified

Files Changed

  • ios/web/navigation/navigation_item_impl.h
  • ios/web/navigation/navigation_manager_impl.mm
  • ios/web/navigation/navigation_item_impl.mm
  • ios/web/navigation/navigation_manager_impl_unittest.mm
  • ios/web/navigation/navigation_item_impl_unittest.mm
From 1fea4f84760555ac0766eb7dbaca357f0c370ae1 Mon Sep 17 00:00:00 2001
From: Gauthier Ambard <gambard@chromium.org>
Date: Tue, 22 Sep 2026 06:36:42 -0700
Subject: [PATCH] [ios] Prevent Referrer-Policy bypass in synthesized NavigationItem

NavigationManagerImpl::WKWebViewCache lazily synthesizes missing
NavigationItem entries using the previous item's URL and
ReferrerPolicyAlways. When a request is replayed (e.g., via "Request
Desktop Site" or web view recreation), the unscrubbed full URL is sent
in the Referer header, bypassing the source site's declared
Referrer-Policy (e.g., no-referrer, same-origin) as well as
HTTPS-to-HTTP downgrade protections.

Additionally, NavigationItemImpl::RestoreStateFromItem() previously
omitted the persisted referrer_, dropping the saved policy during tab
restoration.

This change fixes the issue by:
1. Restoring referrer_ in NavigationItemImpl::RestoreStateFromItem()
   when the URLs match, preserving the original Referrer-Policy
   persisted in session storage across tab restorations.
2. Changing the synthesized referrer policy in
   WKWebViewCache::GetNavigationItemImplAtIndex() from
   ReferrerPolicyAlways to ReferrerPolicyStrictOriginWhenCrossOrigin,
   aligning with W3C/WebKit defaults and stripping paths and query
   parameters on cross-origin requests.
3. Adding unit test coverage in NavigationItemTest.RestoreState and
   NavigationManagerTest to verify referrer restoration, clearing on
   empty referrers, and strict-origin synthesized policy.

Bug: 553661952
Change-Id: I12d8531dae669a8fd7fd7ab591c6716a6d3ef8af
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8397807
Reviewed-by: Mike Dougherty <michaeldo@chromium.org>
Commit-Queue: Gauthier Ambard <gambard@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1702549}
---
diff --git a/ios/web/navigation/navigation_item_impl.h b/ios/web/navigation/navigation_item_impl.h
index 9d3d948d..817221c6 100644
--- a/ios/web/navigation/navigation_item_impl.h
+++ b/ios/web/navigation/navigation_item_impl.h
@@ -133,7 +133,9 @@
   void SetUntrusted();
   bool IsUntrusted();
 
-  // Restores the state of the `other` navigation item in this item.
+  // Restores the state (virtual URL, security scoped file resource, and
+  // referrer) of the `other` navigation item in this item if their URLs match.
+  // Also restores the user agent type regardless of URL match.
   void RestoreStateFromItem(NavigationItem* other);
 
 #ifndef NDEBUG
diff --git a/ios/web/navigation/navigation_item_impl.mm b/ios/web/navigation/navigation_item_impl.mm
index 5259de7..172e5a4a 100644
--- a/ios/web/navigation/navigation_item_impl.mm
+++ b/ios/web/navigation/navigation_item_impl.mm
@@ -382,15 +382,17 @@
 
 void NavigationItemImpl::RestoreStateFromItem(NavigationItem* other) {
   // Restore the UserAgent type in any case, as if the URLs are different it
-  // might mean that `this` is a next navigation. The page display state and the
-  // virtual URL only make sense if it is the same item. The other headers might
-  // not make sense after creating a new navigation to the page.
+  // might mean that `this` is a next navigation. The page display state, the
+  // virtual URL, the security scoped file resource, and the referrer only make
+  // sense if it is the same item. Other headers might not make sense after
+  // creating a new navigation to the page.
   if (other->GetUserAgentType() != UserAgentType::NONE) {
     SetUserAgentType(other->GetUserAgentType());
   }
   if (url_ == other->GetURL()) {
     SetVirtualURL(other->GetVirtualURL());
     SetSecurityScopedFileResource(other->GetSecurityScopedFileResource());
+    SetReferrer(other->GetReferrer());
   }
 }
 
diff --git a/ios/web/navigation/navigation_manager_impl.mm b/ios/web/navigation/navigation_manager_impl.mm
index e761446..a942a4c 100644
--- a/ios/web/navigation/navigation_manager_impl.mm
+++ b/ios/web/navigation/navigation_manager_impl.mm
@@ -1579,9 +1579,10 @@
   std::unique_ptr<web::NavigationItemImpl> new_item =
       navigation_manager_->CreateNavigationItemWithRewriters(
           net::GURLWithNSURL(wk_item.URL),
-          (prev_wk_item ? web::Referrer(net::GURLWithNSURL(prev_wk_item.URL),
-                                        web::ReferrerPolicyAlways)
-                        : web::Referrer()),
+          (prev_wk_item
+               ? web::Referrer(net::GURLWithNSURL(prev_wk_item.URL),
+                               web::ReferrerPolicyStrictOriginWhenCrossOrigin)
+               : web::Referrer()),
           ui::PageTransition::PAGE_TRANSITION_LINK,
           NavigationInitiationType::RENDERER_INITIATED, HttpsUpgradeType::kNone,
           // Not using GetLastCommittedItem()->GetURL() in case the last
diff --git a/ios/web/navigation/navigation_item_impl_unittest.mm b/ios/web/navigation/navigation_item_impl_unittest.mm
index f1dce29..bdd7a97 100644
--- a/ios/web/navigation/navigation_item_impl_unittest.mm
+++ b/ios/web/navigation/navigation_item_impl_unittest.mm
@@ -19,6 +19,7 @@
 namespace {
 
 const char kItemURLString[] = "http://init.test";
+constexpr std::string_view kReferrerURLString = "http://referrer.url";
 static NSString* const kHTTPHeaderKey1 = @"key1";
 static NSString* const kHTTPHeaderKey2 = @"key2";
 static NSString* const kHTTPHeaderValue1 = @"value1";
@@ -213,6 +214,8 @@
   other_item.SetVirtualURL(GURL("www.virtual.com"));
   NSData* data = [@"data" dataUsingEncoding:NSUTF8StringEncoding];
   other_item.SetSecurityScopedFileResource(data);
+  other_item.SetReferrer(
+      Referrer(GURL(kReferrerURLString), ReferrerPolicyDefault));
 
   ASSERT_NE(other_item.GetURL(), item_->GetURL());
 
@@ -222,12 +225,15 @@
   EXPECT_NE(other_item.GetVirtualURL(), item_->GetVirtualURL());
   EXPECT_NE(other_item.GetSecurityScopedFileResource(),
             item_->GetSecurityScopedFileResource());
+  EXPECT_NE(other_item.GetReferrer(), item_->GetReferrer());
 
   NavigationItemImpl other_item2;
   other_item2.SetUserAgentType(UserAgentType::DESKTOP);
   other_item2.SetURL(item_->GetURL());
   other_item2.SetVirtualURL(GURL("www.virtual.com"));
   other_item2.SetSecurityScopedFileResource(data);
+  other_item2.SetReferrer(
+      Referrer(GURL(kReferrerURLString), ReferrerPolicyDefault));
 
   // Same URL, everything is restored.
   item_->RestoreStateFromItem(&other_item2);
@@ -235,6 +241,15 @@
   EXPECT_EQ(other_item2.GetVirtualURL(), item_->GetVirtualURL());
   EXPECT_EQ(other_item2.GetSecurityScopedFileResource(),
             item_->GetSecurityScopedFileResource());
+  EXPECT_EQ(other_item2.GetReferrer(), item_->GetReferrer());
+
+  // Restoring an item with an empty referrer correctly updates the referrer.
+  NavigationItemImpl other_item3;
+  other_item3.SetURL(item_->GetURL());
+  ASSERT_TRUE(other_item3.GetReferrer().url.is_empty());
+  item_->RestoreStateFromItem(&other_item3);
+  EXPECT_EQ(other_item3.GetReferrer(), item_->GetReferrer());
+  EXPECT_TRUE(item_->GetReferrer().url.is_empty());
 }
 
 // Tests that NavigationItemImpl round trip correctly when serialized to proto.
diff --git a/ios/web/navigation/navigation_manager_impl_unittest.mm b/ios/web/navigation/navigation_manager_impl_unittest.mm
index 09a39514..2853f3a 100644
--- a/ios/web/navigation/navigation_manager_impl_unittest.mm
+++ b/ios/web/navigation/navigation_manager_impl_unittest.mm
@@ -2136,6 +2136,22 @@
   EXPECT_EQ(GURL("http://www.url.com/0"), item->GetURL());
 }
 
+// Tests that a lazily created NavigationItemImpl uses
+// ReferrerPolicyStrictOriginWhenCrossOrigin with the previous entry's URL.
+TEST_P(NavigationManagerTest,
+       GetNavigationItemImplAtIndex_SynthesizedReferrerPolicy) {
+  [mock_wk_list_ setCurrentURL:@"http://www.url.com/1"
+                  backListURLs:@[ @"http://www.url.com/0" ]
+               forwardListURLs:nil];
+
+  NavigationItem* item = navigation_manager()->GetNavigationItemImplAtIndex(1);
+  ASSERT_TRUE(item);
+  EXPECT_EQ(GURL("http://www.url.com/1"), item->GetURL());
+  EXPECT_EQ(GURL("http://www.url.com/0"), item->GetReferrer().url);
+  EXPECT_EQ(web::ReferrerPolicyStrictOriginWhenCrossOrigin,
+            item->GetReferrer().policy);
+}
+
 TEST_P(NavigationManagerTest, UpdateCurrentItemForReplaceState) {
   navigation_manager()->AddPendingItem(
       GURL("http://www.url.com/0"),
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/ios/web/navigation/navigation_item_impl_unittest.mm b/ios/web/navigation/navigation_item_impl_unittest.mm
index f1dce29..bdd7a97 100644
--- a/ios/web/navigation/navigation_item_impl_unittest.mm
+++ b/ios/web/navigation/navigation_item_impl_unittest.mm
@@ -19,6 +19,7 @@
 namespace {
 
 const char kItemURLString[] = "http://init.test";
+constexpr std::string_view kReferrerURLString = "http://referrer.url";
 static NSString* const kHTTPHeaderKey1 = @"key1";
 static NSString* const kHTTPHeaderKey2 = @"key2";
 static NSString* const kHTTPHeaderValue1 = @"value1";
@@ -213,6 +214,8 @@
   other_item.SetVirtualURL(GURL("www.virtual.com"));
   NSData* data = [@"data" dataUsingEncoding:NSUTF8StringEncoding];
   other_item.SetSecurityScopedFileResource(data);
+  other_item.SetReferrer(
+      Referrer(GURL(kReferrerURLString), ReferrerPolicyDefault));
 
   ASSERT_NE(other_item.GetURL(), item_->GetURL());
 
@@ -222,12 +225,15 @@
   EXPECT_NE(other_item.GetVirtualURL(), item_->GetVirtualURL());
   EXPECT_NE(other_item.GetSecurityScopedFileResource(),
             item_->GetSecurityScopedFileResource());
+  EXPECT_NE(other_item.GetReferrer(), item_->GetReferrer());
 
   NavigationItemImpl other_item2;
   other_item2.SetUserAgentType(UserAgentType::DESKTOP);
   other_item2.SetURL(item_->GetURL());
   other_item2.SetVirtualURL(GURL("www.virtual.com"));
   other_item2.SetSecurityScopedFileResource(data);
+  other_item2.SetReferrer(
+      Referrer(GURL(kReferrerURLString), ReferrerPolicyDefault));
 
   // Same URL, everything is restored.
   item_->RestoreStateFromItem(&other_item2);
@@ -235,6 +241,15 @@
   EXPECT_EQ(other_item2.GetVirtualURL(), item_->GetVirtualURL());
   EXPECT_EQ(other_item2.GetSecurityScopedFileResource(),
             item_->GetSecurityScopedFileResource());
+  EXPECT_EQ(other_item2.GetReferrer(), item_->GetReferrer());
+
+  // Restoring an item with an empty referrer correctly updates the referrer.
+  NavigationItemImpl other_item3;
+  other_item3.SetURL(item_->GetURL());
+  ASSERT_TRUE(other_item3.GetReferrer().url.is_empty());
+  item_->RestoreStateFromItem(&other_item3);
+  EXPECT_EQ(other_item3.GetReferrer(), item_->GetReferrer());
+  EXPECT_TRUE(item_->GetReferrer().url.is_empty());
 }
 
 // Tests that NavigationItemImpl round trip correctly when serialized to proto.
diff --git a/ios/web/navigation/navigation_manager_impl_unittest.mm b/ios/web/navigation/navigation_manager_impl_unittest.mm
index 09a39514..2853f3a 100644
--- a/ios/web/navigation/navigation_manager_impl_unittest.mm
+++ b/ios/web/navigation/navigation_manager_impl_unittest.mm
@@ -2136,6 +2136,22 @@
   EXPECT_EQ(GURL("http://www.url.com/0"), item->GetURL());
 }
 
+// Tests that a lazily created NavigationItemImpl uses
+// ReferrerPolicyStrictOriginWhenCrossOrigin with the previous entry's URL.
+TEST_P(NavigationManagerTest,
+       GetNavigationItemImplAtIndex_SynthesizedReferrerPolicy) {
+  [mock_wk_list_ setCurrentURL:@"http://www.url.com/1"
+                  backListURLs:@[ @"http://www.url.com/0" ]
+               forwardListURLs:nil];
+
+  NavigationItem* item = navigation_manager()->GetNavigationItemImplAtIndex(1);
+  ASSERT_TRUE(item);
+  EXPECT_EQ(GURL("http://www.url.com/1"), item->GetURL());
+  EXPECT_EQ(GURL("http://www.url.com/0"), item->GetReferrer().url);
+  EXPECT_EQ(web::ReferrerPolicyStrictOriginWhenCrossOrigin,
+            item->GetReferrer().policy);
+}
+
 TEST_P(NavigationManagerTest, UpdateCurrentItemForReplaceState) {
   navigation_manager()->AddPendingItem(
       GURL("http://www.url.com/0"),
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.