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 <zork@google.com>
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 <zork@chromium.org>
Reviewed-by: Rakina Zata Amni <rakina@chromium.org>
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