Low chrome Logic Error 🔧 Commit mapped

Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient validation of untrusted input in Navigation
DescriptionInsufficient validation of untrusted input in Navigation
ComponentNavigation
Bug ClassLogic Error
Tracker520005624
Fix commit02caf58c9b9d (chromium/src) +50/-5
CISA KEVNot listed
CreditedGoogle
Disclosed2026-07-29

Changed Functions

FunctionChangeNotes
if
content/browser/renderer_host/render_frame_host_impl.cc
modified

Files Changed

  • content/browser/back_forward_cache_browsertest.cc
  • content/browser/renderer_host/render_frame_host_impl.cc
  • content/browser/renderer_host/render_frame_host_impl.h
From 02caf58c9b9d3e71e45df3c8a3db9642ea7ced62 Mon Sep 17 00:00:00 2001
From: Tzarial <zork@google.com>
Date: Fri, 26 Jun 2026 10:16:34 -0700
Subject: [PATCH] [agy][content] Ignore history user activation from inactive RFHs.

DidConsumeHistoryUserActivation IPC can be sent by a compromised
renderer on behalf of a subframe RenderFrameHost while its page is in
the BackForwardCache. Since its FrameTreeNode's frame_tree_ still
references the primary FrameTree, walking that tree would consume
the history user activation state on an unrelated active page.

This CL adds a check to ensure we only process this IPC when the
RenderFrameHost is in kActive state, matching the security pattern of
UpdateUserActivationState.

Fixed: 520005624
Test: RenderFrameHostImplTest.DidConsumeHistoryUserActivationNonActive
Change-Id: Ide3168f3e6bb0c285e6f1a0fc90b8076f605a2af
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7989439
Commit-Queue: Tzarial <zork@chromium.org>
Reviewed-by: Arthur Sonzogni <arthursonzogni@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1653252}
---

diff --git a/content/browser/back_forward_cache_browsertest.cc b/content/browser/back_forward_cache_browsertest.cc
index 21dd284..8b84b1f2 100644
--- a/content/browser/back_forward_cache_browsertest.cc
+++ b/content/browser/back_forward_cache_browsertest.cc
@@ -3691,6 +3691,46 @@
   ExpectRestored(FROM_HERE);
 }
 
+// Regression test for https://crbug.com/520005624.
+// Ensures that receiving a DidConsumeHistoryUserActivation IPC from a subframe
+// that is in the BackForwardCache is ignored and does not clear history user
+// activation of the active page.
+IN_PROC_BROWSER_TEST_F(
+    BackForwardCacheBrowserTest,
+    DidConsumeHistoryUserActivationFromCachedSubframeIsIgnored) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  GURL url_ab(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b)"));
+  GURL url_c(embedded_test_server()->GetURL("c.com", "/title1.html"));
+
+  // 1) Navigate to A(B).
+  ASSERT_TRUE(NavigateToURL(shell(), url_ab));
+  RenderFrameHostImplWrapper rfh_a(current_frame_host());
+  RenderFrameHostImplWrapper rfh_b(rfh_a->child_at(0)->current_frame_host());
+
+  // 2) Navigate to C.
+  ASSERT_TRUE(NavigateToURL(shell(), url_c));
+  RenderFrameHostImplWrapper rfh_c(current_frame_host());
+
+  // Verify A(B) is in the BackForwardCache.
+  ASSERT_TRUE(rfh_a->IsInBackForwardCache());
+  ASSERT_TRUE(rfh_b->IsInBackForwardCache());
+
+  // 3) Set history user activation active on the active page C.
+  rfh_c->UpdateUserActivationState(
+      blink::mojom::UserActivationUpdateType::kNotifyActivation,
+      blink::mojom::UserActivationNotificationType::kInteraction);
+  EXPECT_TRUE(rfh_c->IsHistoryUserActivationActive());
+
+  // 4) Simulate receiving DidConsumeHistoryUserActivation from B (which is in
+  // BFCache).
+  rfh_b->DidConsumeHistoryUserActivation();
+
+  // 5) The history user activation on active page C must NOT be consumed.
+  EXPECT_TRUE(rfh_c->IsHistoryUserActivationActive());
+}
+
 // BEFORE ADDING A NEW TEST HERE
 // Read the note at the top about the other files you could add it to.
 }  // namespace content
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 860abec..6e0b31c 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -9632,12 +9632,15 @@
 }
 
 void RenderFrameHostImpl::DidConsumeHistoryUserActivation() {
-  // owner_ may be null for IsPendingDeletion() or IsInBackForwardCache(), in
-  // which case the history user activation is managed by a different active
-  // RenderFrameHost.
-  if (owner_) {
-    owner_->DidConsumeHistoryUserActivation();
+  // This IPC is only sent from an active document, but might be received after
+  // the transition to the backforward cache or after entering pending
+  // deletion. It must be ignored as it would affect the state of the primary
+  // page instead of the old one.
+  if (lifecycle_state() != LifecycleStateImpl::kActive) {
+    return;
   }
+  CHECK(owner_);  // See `owner_` invariants about `lifecycle_state_`.
+  owner_->DidConsumeHistoryUserActivation();
 }
 
 void RenderFrameHostImpl::HadStickyUserActivationBeforeNavigationChanged(
diff --git a/content/browser/renderer_host/render_frame_host_impl.h b/content/browser/renderer_host/render_frame_host_impl.h
index bd8c4967..2d526d8 100644
--- a/content/browser/renderer_host/render_frame_host_impl.h
+++ b/content/browser/renderer_host/render_frame_host_impl.h
@@ -3544,6 +3544,8 @@
   FRIEND_TEST_ALL_PREFIXES(
       RenderFrameHostImplTest,
       BrowserInitiatedCloseIsNotCancelledIfPageIsntPrimary);
+  FRIEND_TEST_ALL_PREFIXES(RenderFrameHostImplTest,
+                           DidConsumeHistoryUserActivationNonActive);
   FRIEND_TEST_ALL_PREFIXES(DocumentUserDataTest, CheckInPendingDeletionState);
   FRIEND_TEST_ALL_PREFIXES(WebContentsImplBrowserTest, FrozenAndUnfrozenIPC);
   FRIEND_TEST_ALL_PREFIXES(RenderFrameHostImplBrowserTest,
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/content/browser/back_forward_cache_browsertest.cc b/content/browser/back_forward_cache_browsertest.cc
index 21dd284..8b84b1f2 100644
--- a/content/browser/back_forward_cache_browsertest.cc
+++ b/content/browser/back_forward_cache_browsertest.cc
@@ -3691,6 +3691,46 @@
   ExpectRestored(FROM_HERE);
 }
 
+// Regression test for https://crbug.com/520005624.
+// Ensures that receiving a DidConsumeHistoryUserActivation IPC from a subframe
+// that is in the BackForwardCache is ignored and does not clear history user
+// activation of the active page.
+IN_PROC_BROWSER_TEST_F(
+    BackForwardCacheBrowserTest,
+    DidConsumeHistoryUserActivationFromCachedSubframeIsIgnored) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  GURL url_ab(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b)"));
+  GURL url_c(embedded_test_server()->GetURL("c.com", "/title1.html"));
+
+  // 1) Navigate to A(B).
+  ASSERT_TRUE(NavigateToURL(shell(), url_ab));
+  RenderFrameHostImplWrapper rfh_a(current_frame_host());
+  RenderFrameHostImplWrapper rfh_b(rfh_a->child_at(0)->current_frame_host());
+
+  // 2) Navigate to C.
+  ASSERT_TRUE(NavigateToURL(shell(), url_c));
+  RenderFrameHostImplWrapper rfh_c(current_frame_host());
+
+  // Verify A(B) is in the BackForwardCache.
+  ASSERT_TRUE(rfh_a->IsInBackForwardCache());
+  ASSERT_TRUE(rfh_b->IsInBackForwardCache());
+
+  // 3) Set history user activation active on the active page C.
+  rfh_c->UpdateUserActivationState(
+      blink::mojom::UserActivationUpdateType::kNotifyActivation,
+      blink::mojom::UserActivationNotificationType::kInteraction);
+  EXPECT_TRUE(rfh_c->IsHistoryUserActivationActive());
+
+  // 4) Simulate receiving DidConsumeHistoryUserActivation from B (which is in
+  // BFCache).
+  rfh_b->DidConsumeHistoryUserActivation();
+
+  // 5) The history user activation on active page C must NOT be consumed.
+  EXPECT_TRUE(rfh_c->IsHistoryUserActivationActive());
+}
+
 // BEFORE ADDING A NEW TEST HERE
 // Read the note at the top about the other files you could add it to.
 }  // namespace content
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.