Chrome · Viz
CVE-2026-79116
Logic Error in Viz
Overview
Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
BoundsTrackingDelegatecontent/browser/back_forward_cache_internal_browsertest.cc |
modified |
Files Changed
content/browser/back_forward_cache_internal_browsertest.cccontent/browser/renderer_host/render_frame_host_impl.cc
Patch
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.
References
On This Page