Medium chrome Logic Error 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactMissing authorization in Viz
DescriptionMissing authorization in Viz
ComponentViz
Bug ClassLogic Error
Tracker502232151
Fix commita8d1167fe626 (chromium/src) +64/-5
CISA KEVNot listed
CreditedGoogle
Disclosed2026-08-25

Changed Functions

FunctionChangeNotes
BoundsTrackingDelegate
content/browser/back_forward_cache_internal_browsertest.cc
modified

Files Changed

  • content/browser/back_forward_cache_internal_browsertest.cc
  • content/browser/renderer_host/render_frame_host_impl.cc
From a8d1167fe626a0a8b6876f1de94bf56d08e928bc Mon Sep 17 00:00:00 2001
From: Tzarial <[email protected]>
Date: Fri, 17 Jul 2026 04:28:20 -0700
Subject: [PATCH] [agy][content] Ignore SetWindowRect in BFCache

LocalMainFrameHost::SetWindowRect arriving for a document that has
entered the back/forward cache must not move or resize the window,
which is now displaying a different primary main frame.

Fixed: 502232151
Test: BackForwardCacheBrowserTest.SetWindowRectIgnoredForCachedFrame
Change-Id: Id4e872b4c4de6cc7b43cb3ae4b4cab1a89a26b44
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8087302
Commit-Queue: Tzarial <[email protected]>
Reviewed-by: Rakina Zata Amni <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1663804}
---

diff --git a/content/browser/back_forward_cache_internal_browsertest.cc b/content/browser/back_forward_cache_internal_browsertest.cc
index 926210f..96975c30 100644
--- a/content/browser/back_forward_cache_internal_browsertest.cc
+++ b/content/browser/back_forward_cache_internal_browsertest.cc
@@ -26,6 +26,7 @@
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/disallow_activation_reason.h"
 #include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/web_contents_delegate.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
@@ -2022,6 +2023,57 @@
                     {reason}, FROM_HERE);
 }
 
+// LocalMainFrameHost::SetWindowRect arriving for a document that has entered
+// the back/forward cache must not move or resize the window, which is now
+// displaying a different primary main frame. See https://crbug.com/502232151.
+IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
+                       SetWindowRectIgnoredForCachedFrame) {
+  // A WebContentsDelegate that records SetContentsBounds() calls.
+  class BoundsTrackingDelegate : public WebContentsDelegate {
+   public:
+    void SetContentsBounds(WebContents* source,
+                           const gfx::Rect& bounds) override {
+      ++set_contents_bounds_call_count_;
+    }
+    int set_contents_bounds_call_count() const {
+      return set_contents_bounds_call_count_;
+    }
+
+   private:
+    int set_contents_bounds_call_count_ = 0;
+  };
+
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
+  GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
+
+  // 1) Navigate to A.
+  EXPECT_TRUE(NavigateToURL(shell(), url_a));
+  RenderFrameHostImplWrapper rfh_a(current_frame_host());
+
+  // 2) Navigate to B. A is stored in the back/forward cache.
+  EXPECT_TRUE(NavigateToURL(shell(), url_b));
+  ASSERT_TRUE(rfh_a->IsInBackForwardCache());
+
+  BoundsTrackingDelegate tracking_delegate;
+  WebContentsDelegate* old_delegate = web_contents()->GetDelegate();
+  web_contents()->SetDelegate(&tracking_delegate);
+
+  // 3) Simulate the cached renderer sending SetWindowRect. The request must be
+  // dropped without forwarding new bounds to the WebContentsDelegate.
+  bool callback_ran = false;
+  rfh_a->SetWindowRect(
+      gfx::Rect(10, 10, 300, 200),
+      base::BindLambdaForTesting([&] { callback_ran = true; }));
+  EXPECT_TRUE(callback_ran);
+  EXPECT_EQ(0, tracking_delegate.set_contents_bounds_call_count());
+
+  // The page must be evicted from the back/forward cache.
+  ASSERT_TRUE(rfh_a.WaitUntilRenderFrameDeleted());
+
+  web_contents()->SetDelegate(old_delegate);
+}
+
 // Test scenarios where the "BackForwardCache" content flag is enabled but
 // the command line flag "DisableBackForwardCache" is turned on, resulting in
 // the feature being disabled.
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 08e2dcb..27e1428 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -7841,15 +7841,22 @@
         "SetWindowRect called during prerendering.");
     return;
   }
-  // Throw out SetWindowRects that are not from the outermost document.
-  if (GetParentOrOuterDocument()) {
-    local_main_frame_host_receiver_.ReportBadMessage(
-        "SetWindowRect called from child frame.");
+
+  // Always ack the renderer so it can clear its pending window rect, even on
+  // the early-return paths below.
+  base::ScopedClosureRunner ack(std::move(callback));
+
+  // An inactive document (e.g. one that has entered the back/forward cache)
+  // must not move or resize the window, which now hosts a different primary
+  // main frame. We use ValidateOutermostMainFrameWindowChange to evict the
+  // page from BFCache if this happens, to prevent the renderer from getting
+  // out of sync with the browser's window bounds if the page is later
+  // restored. See https://crbug.com/502232151.
+  if (!ValidateOutermostMainFrameWindowChange("SetWindowRect")) {
     return;
   }
 
   delegate_->SetWindowRect(bounds);
-  std::move(callback).Run();
 }
 
 void RenderFrameHostImpl::MoveWindowTo(const gfx::Point& origin,
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/content/browser/back_forward_cache_internal_browsertest.cc b/content/browser/back_forward_cache_internal_browsertest.cc
index 926210f..96975c30 100644
--- a/content/browser/back_forward_cache_internal_browsertest.cc
+++ b/content/browser/back_forward_cache_internal_browsertest.cc
@@ -26,6 +26,7 @@
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/disallow_activation_reason.h"
 #include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/web_contents_delegate.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
@@ -2022,6 +2023,57 @@
                     {reason}, FROM_HERE);
 }
 
+// LocalMainFrameHost::SetWindowRect arriving for a document that has entered
+// the back/forward cache must not move or resize the window, which is now
+// displaying a different primary main frame. See https://crbug.com/502232151.
+IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
+                       SetWindowRectIgnoredForCachedFrame) {
+  // A WebContentsDelegate that records SetContentsBounds() calls.
+  class BoundsTrackingDelegate : public WebContentsDelegate {
+   public:
+    void SetContentsBounds(WebContents* source,
+                           const gfx::Rect& bounds) override {
+      ++set_contents_bounds_call_count_;
+    }
+    int set_contents_bounds_call_count() const {
+      return set_contents_bounds_call_count_;
+    }
+
+   private:
+    int set_contents_bounds_call_count_ = 0;
+  };
+
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
+  GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
+
+  // 1) Navigate to A.
+  EXPECT_TRUE(NavigateToURL(shell(), url_a));
+  RenderFrameHostImplWrapper rfh_a(current_frame_host());
+
+  // 2) Navigate to B. A is stored in the back/forward cache.
+  EXPECT_TRUE(NavigateToURL(shell(), url_b));
+  ASSERT_TRUE(rfh_a->IsInBackForwardCache());
+
+  BoundsTrackingDelegate tracking_delegate;
+  WebContentsDelegate* old_delegate = web_contents()->GetDelegate();
+  web_contents()->SetDelegate(&tracking_delegate);
+
+  // 3) Simulate the cached renderer sending SetWindowRect. The request must be
+  // dropped without forwarding new bounds to the WebContentsDelegate.
+  bool callback_ran = false;
+  rfh_a->SetWindowRect(
+      gfx::Rect(10, 10, 300, 200),
+      base::BindLambdaForTesting([&] { callback_ran = true; }));
+  EXPECT_TRUE(callback_ran);
+  EXPECT_EQ(0, tracking_delegate.set_contents_bounds_call_count());
+
+  // The page must be evicted from the back/forward cache.
+  ASSERT_TRUE(rfh_a.WaitUntilRenderFrameDeleted());
+
+  web_contents()->SetDelegate(old_delegate);
+}
+
 // Test scenarios where the "BackForwardCache" content flag is enabled but
 // the command line flag "DisableBackForwardCache" is turned on, resulting in
 // the feature being disabled.
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.