Chrome · Chrome for iOS
CVE-2026-18013
Logic Error in Chrome for iOS
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
TEST_Fcomponents/history/core/browser/history_backend_unittest.cc |
modified |
Files Changed
components/history/core/browser/history_backend.cccomponents/history/core/browser/history_backend_unittest.cc
Patch
From 095293e1d94ae4916f6a64213e09a9f8d5fd767c Mon Sep 17 00:00:00 2001 From: Marc Treib <[email protected]> Date: Mon, 15 Jun 2026 09:20:17 -0700 Subject: [PATCH] HistoryBackend: Exclude redirects from segment computation This CL updates HistoryBackend::CalculateSegmentID() so that redirects are excluded, i.e. only the start of a redirect chain is considered as the start of a segment. See linked bug for justification. Note: This will make no difference for local visits, since AddPage() already only calls the segment-related methods for redirect chain starts. It only makes a difference for synced visits (which are only added to segments in some narrow circumstances and only on iOS). Fixed: 523245998 Change-Id: I84f770bacde7cf3b2bfc37c8856e33a16a6a6964 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7941351 Commit-Queue: Marc Treib <[email protected]> Reviewed-by: Roman Arora <[email protected]> Cr-Commit-Position: refs/heads/main@{#1646871} --- diff --git a/components/history/core/browser/history_backend.cc b/components/history/core/browser/history_backend.cc index 04ec63a8..dea05db 100644 --- a/components/history/core/browser/history_backend.cc +++ b/components/history/core/browser/history_backend.cc @@ -544,8 +544,10 @@ const GURL& url, VisitID from_visit, ui::PageTransition transition_type) { - // We only consider main frames. - if (!ui::PageTransitionIsMainFrame(transition_type)) { + // Only consider main frames, and ignore redirects (i.e. consider only the + // start of a redirect chain). + if (!ui::PageTransitionIsMainFrame(transition_type) || + ui::PageTransitionIsRedirect(transition_type)) { return 0; } diff --git a/components/history/core/browser/history_backend_unittest.cc b/components/history/core/browser/history_backend_unittest.cc index 45556089..a9972f0e 100644 --- a/components/history/core/browser/history_backend_unittest.cc +++ b/components/history/core/browser/history_backend_unittest.cc @@ -1240,6 +1240,123 @@ ASSERT_EQ(0, backend_->db()->GetRowForURL(url, &row)); } +TEST_F(HistoryBackendTest, SegmentsDoNotIncludeRedirects) { + ASSERT_TRUE(backend_.get()); + + base::Time visit_time = base::Time::Now() - base::Days(1); + + // 1. Typed visit (no redirect). A segment should be created. + GURL typed_url("http://typed-no-redirect.com"); + HistoryAddPageArgs typed_request( + typed_url, visit_time, 0, 0, std::nullopt, GURL(), RedirectList(), + ui::PAGE_TRANSITION_TYPED, false, SOURCE_BROWSED, + VisitResponseCodeCategory::kNot404, false, true); + backend_->AddPage(typed_request); + + std::string typed_segment_name = + VisitSegmentDatabase::ComputeSegmentName(typed_url); + SegmentID typed_segment_id = + backend_->db()->GetSegmentNamed(typed_segment_name); + EXPECT_NE(0, typed_segment_id); + + // 2. Client redirect: User visits referrer_url (typed), which then + // client-redirects to target_url, resulting in two separate AddPage() calls. + // A segment should be created for referrer_url, but NOT for target_url. + GURL referrer_url("http://client-redirect-referrer.com"); + HistoryAddPageArgs referrer_request( + referrer_url, visit_time - base::Seconds(10), 0, 0, std::nullopt, GURL(), + RedirectList(), ui::PAGE_TRANSITION_TYPED, false, SOURCE_BROWSED, + VisitResponseCodeCategory::kNot404, false, true); + backend_->AddPage(referrer_request); + + std::string referrer_segment_name = + VisitSegmentDatabase::ComputeSegmentName(referrer_url); + EXPECT_NE(0, backend_->db()->GetSegmentNamed(referrer_segment_name)); + + GURL client_redirect_target_url("http://client-redirect-target.com"); + HistoryAddPageArgs client_redirect_request( + client_redirect_target_url, visit_time, 0, 0, std::nullopt, referrer_url, + RedirectList{referrer_url, client_redirect_target_url}, + ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED | + ui::PAGE_TRANSITION_CLIENT_REDIRECT), + false, SOURCE_BROWSED, VisitResponseCodeCategory::kNot404, false, true); + backend_->AddPage(client_redirect_request); + + std::string client_redirect_target_segment_name = + VisitSegmentDatabase::ComputeSegmentName(client_redirect_target_url); + EXPECT_EQ( + 0, backend_->db()->GetSegmentNamed(client_redirect_target_segment_name)); + + // 3. Single visit with ui::PAGE_TRANSITION_TYPED | + // ui::PAGE_TRANSITION_SERVER_REDIRECT (this shouldn't usually happen, but + // could represent a synced/restored visit). No segment should be created. + GURL single_server_redirect_url("http://single-server-redirect.com"); + HistoryAddPageArgs single_server_redirect_request( + single_server_redirect_url, visit_time, 0, 0, std::nullopt, GURL(), + RedirectList(), + ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED | + ui::PAGE_TRANSITION_SERVER_REDIRECT), + false, SOURCE_BROWSED, VisitResponseCodeCategory::kNot404, false, true); + backend_->AddPage(single_server_redirect_request); + + std::string single_server_redirect_segment_name = + VisitSegmentDatabase::ComputeSegmentName(single_server_redirect_url); + EXPECT_EQ( + 0, backend_->db()->GetSegmentNamed(single_server_redirect_segment_name)); + + // 4. Realistic server redirect chain: User types server_redirect_source_url, + // which server-redirects to server_redirect_target_url. A segment should be + // created for the source_url, but NOT for the target_url. + GURL server_redirect_source_url("http://server-redirect-source.com"); + GURL server_redirect_target_url("http://server-redirect-target.com"); + HistoryAddPageArgs server_redirect_request( + server_redirect_target_url, visit_time, 0, 0, std::nullopt, GURL(), + RedirectList{server_redirect_source_url, server_redirect_target_url}, + ui::PAGE_TRANSITION_TYPED, false, SOURCE_BROWSED, + VisitResponseCodeCategory::kNot404, false, true); + backend_->AddPage(server_redirect_request); + + std::string server_redirect_source_segment_name = + VisitSegmentDatabase::ComputeSegmentName(server_redirect_source_url); + EXPECT_NE( + 0, backend_->db()->GetSegmentNamed(server_redirect_source_segment_name)); + + std::string server_redirect_target_segment_name = + VisitSegmentDatabase::ComputeSegmentName(server_redirect_target_url); + EXPECT_EQ( + 0, backend_->db()->GetSegmentNamed(server_redirect_target_segment_name)); + + // 5. Bookmark non-redirect control. + // A segment should be created. + GURL bookmark_url("http://bookmark-no-redirect.com"); + HistoryAddPageArgs bookmark_request( + bookmark_url, visit_time, 0, 0, std::nullopt, GURL(), RedirectList(), + ui::PAGE_TRANSITION_AUTO_BOOKMARK, false, SOURCE_BROWSED, + VisitResponseCodeCategory::kNot404, false, true); + backend_->AddPage(bookmark_request); + + std::string bookmark_segment_name = + VisitSegmentDatabase::ComputeSegmentName(bookmark_url); + SegmentID bookmark_segment_id = + backend_->db()->GetSegmentNamed(bookmark_segment_name); + EXPECT_NE(0, bookmark_segment_id); + + // 6. Bookmark with server redirect (e.g. synced/restored or + // individually-added redirect visit). No segment should be created. + GURL bookmark_redirect_url("http://bookmark-server-redirect.com"); + HistoryAddPageArgs bookmark_redirect_request( + bookmark_redirect_url, visit_time, 0, 0, std::nullopt, GURL(), + RedirectList(), + ui::PageTransitionFromInt(ui::PAGE_TRANSITION_AUTO_BOOKMARK | + ui::PAGE_TRANSITION_SERVER_REDIRECT), + false, SOURCE_BROWSED, VisitResponseCodeCategory::kNot404, false, true); + backend_->AddPage(bookmark_redirect_request); + + std::string bookmark_redirect_segment_name = + VisitSegmentDatabase::ComputeSegmentName(bookmark_redirect_url); + EXPECT_EQ(0, backend_->db()->GetSegmentNamed(bookmark_redirect_segment_name)); +} + TEST_F(HistoryBackendTest, AddPage404) { // Enable `history::kVisitedLinksOn404` to make 404s eligible for History. base::test::ScopedFeatureList feature_list;
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/components/history/core/browser/history_backend_unittest.cc b/components/history/core/browser/history_backend_unittest.cc
index 45556089..a9972f0e 100644
--- a/components/history/core/browser/history_backend_unittest.cc
+++ b/components/history/core/browser/history_backend_unittest.cc
@@ -1240,6 +1240,123 @@
ASSERT_EQ(0, backend_->db()->GetRowForURL(url, &row));
}
+TEST_F(HistoryBackendTest, SegmentsDoNotIncludeRedirects) {
+ ASSERT_TRUE(backend_.get());
+
+ base::Time visit_time = base::Time::Now() - base::Days(1);
+
+ // 1. Typed visit (no redirect). A segment should be created.
+ GURL typed_url("http://typed-no-redirect.com");
+ HistoryAddPageArgs typed_request(
+ typed_url, visit_time, 0, 0, std::nullopt, GURL(), RedirectList(),
+ ui::PAGE_TRANSITION_TYPED, false, SOURCE_BROWSED,
+ VisitResponseCodeCategory::kNot404, false, true);
+ backend_->AddPage(typed_request);
+
+ std::string typed_segment_name =
+ VisitSegmentDatabase::ComputeSegmentName(typed_url);
+ SegmentID typed_segment_id =
+ backend_->db()->GetSegmentNamed(typed_segment_name);
+ EXPECT_NE(0, typed_segment_id);
+
+ // 2. Client redirect: User visits referrer_url (typed), which then
+ // client-redirects to target_url, resulting in two separate AddPage() calls.
+ // A segment should be created for referrer_url, but NOT for target_url.
+ GURL referrer_url("http://client-redirect-referrer.com");
+ HistoryAddPageArgs referrer_request(
+ referrer_url, visit_time - base::Seconds(10), 0, 0, std::nullopt, GURL(),
+ RedirectList(), ui::PAGE_TRANSITION_TYPED, false, SOURCE_BROWSED,
+ VisitResponseCodeCategory::kNot404, false, true);
+ backend_->AddPage(referrer_request);
+
+ std::string referrer_segment_name =
+ VisitSegmentDatabase::ComputeSegmentName(referrer_url);
+ EXPECT_NE(0, backend_->db()->GetSegmentNamed(referrer_segment_name));
+
+ GURL client_redirect_target_url("http://client-redirect-target.com");
+ HistoryAddPageArgs client_redirect_request(
+ client_redirect_target_url, visit_time, 0, 0, std::nullopt, referrer_url,
+ RedirectList{referrer_url, client_redirect_target_url},
+ ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED |
+ ui::PAGE_TRANSITION_CLIENT_REDIRECT),
+ false, SOURCE_BROWSED, VisitResponseCodeCategory::kNot404, false, true);
+ backend_->AddPage(client_redirect_request);
+
+ std::string client_redirect_target_segment_name =
+ VisitSegmentDatabase::ComputeSegmentName(client_redirect_target_url);
+ EXPECT_EQ(
+ 0, backend_->db()->GetSegmentNamed(client_redirect_target_segment_name));
+
+ // 3. Single visit with ui::PAGE_TRANSITION_TYPED |
+ // ui::PAGE_TRANSITION_SERVER_REDIRECT (this shouldn't usually happen, but
+ // could represent a synced/restored visit). No segment should be created.
+ GURL single_server_redirect_url("http://single-server-redirect.com");
+ HistoryAddPageArgs single_server_redirect_request(
+ single_server_redirect_url, visit_time, 0, 0, std::nullopt, GURL(),
+ RedirectList(),
+ ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED |
+ ui::PAGE_TRANSITION_SERVER_REDIRECT),
+ false, SOURCE_BROWSED, VisitResponseCodeCategory::kNot404, false, true);
+ backend_->AddPage(single_server_redirect_request);
+
+ std::string single_server_redirect_segment_name =
+ VisitSegmentDatabase::ComputeSegmentName(single_server_redirect_url);
+ EXPECT_EQ(
+ 0, backend_->db()->GetSegmentNamed(single_server_redirect_segment_name));
+
+ // 4. Realistic server redirect chain: User types server_redirect_source_url,
+ // which server-redirects to server_redirect_target_url. A segment should be
+ // created for the source_url, but NOT for the target_url.
+ GURL server_redirect_source_url("http://server-redirect-source.com");
+ GURL server_redirect_target_url("http://server-redirect-target.com");
+ HistoryAddPageArgs server_redirect_request(
+ server_redirect_target_url, visit_time, 0, 0, std::nullopt, GURL(),
+ RedirectList{server_redirect_source_url, server_redirect_target_url},
+ ui::PAGE_TRANSITION_TYPED, false, SOURCE_BROWSED,
+ VisitResponseCodeCategory::kNot404, false, true);
+ backend_->AddPage(server_redirect_request);
+
+ std::string server_redirect_source_segment_name =
+ VisitSegmentDatabase::ComputeSegmentName(server_redirect_source_url);
+ EXPECT_NE(
+ 0, backend_->db()->GetSegmentNamed(server_redirect_source_segment_name));
+
+ std::string server_redirect_target_segment_name =
+ VisitSegmentDatabase::ComputeSegmentName(server_redirect_target_url);
+ EXPECT_EQ(
+ 0, backend_->db()->GetSegmentNamed(server_redirect_target_segment_name));
+
+ // 5. Bookmark non-redirect control.
+ // A segment should be created.
+ GURL bookmark_url("http://bookmark-no-redirect.com");
+ HistoryAddPageArgs bookmark_request(
+ bookmark_url, visit_time, 0, 0, std::nullopt, GURL(), RedirectList(),
+ ui::PAGE_TRANSITION_AUTO_BOOKMARK, false, SOURCE_BROWSED,
+ VisitResponseCodeCategory::kNot404, false, true);
+ backend_->AddPage(bookmark_request);
+
+ std::string bookmark_segment_name =
+ VisitSegmentDatabase::ComputeSegmentName(bookmark_url);
+ SegmentID bookmark_segment_id =
+ backend_->db()->GetSegmentNamed(bookmark_segment_name);
+ EXPECT_NE(0, bookmark_segment_id);
+
+ // 6. Bookmark with server redirect (e.g. synced/restored or
+ // individually-added redirect visit). No segment should be created.
+ GURL bookmark_redirect_url("http://bookmark-server-redirect.com");
+ HistoryAddPageArgs bookmark_redirect_request(
+ bookmark_redirect_url, visit_time, 0, 0, std::nullopt, GURL(),
+ RedirectList(),
+ ui::PageTransitionFromInt(ui::PAGE_TRANSITION_AUTO_BOOKMARK |
+ ui::PAGE_TRANSITION_SERVER_REDIRECT),
+ false, SOURCE_BROWSED, VisitResponseCodeCategory::kNot404, false, true);
+ backend_->AddPage(bookmark_redirect_request);
+
+ std::string bookmark_redirect_segment_name =
+ VisitSegmentDatabase::ComputeSegmentName(bookmark_redirect_url);
+ EXPECT_EQ(0, backend_->db()->GetSegmentNamed(bookmark_redirect_segment_name));
+}
+
TEST_F(HistoryBackendTest, AddPage404) {
// Enable `history::kVisitedLinksOn404` to make 404s eligible for History.
base::test::ScopedFeatureList feature_list;
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