Low chrome Logic Error 🔧 Commit mapped

Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUI misrepresentation in Core
DescriptionUI misrepresentation in Core
ComponentCore
Bug ClassLogic Error
Tracker519210950
Fix commit0eb9d43c1318 (chromium/src) +86/-3
CISA KEVNot listed
CreditedGoogle
Disclosed2026-08-25

Changed Functions

FunctionChangeNotes
FakeTextInputClientMacDelegate
content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
modified
TEST_F
content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
modified

Files Changed

  • content/browser/renderer_host/render_widget_host_view_mac.mm
  • content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
From 0eb9d43c13183f55606f1d763ffda3603660e844 Mon Sep 17 00:00:00 2001
From: Maggie Chen <[email protected]>
Date: Wed, 15 Jul 2026 10:08:37 -0700
Subject: [PATCH] [agy][content] Clamp macOS IME candidate rect

Clamp the first rect returned by SyncGetFirstRectForRange on macOS
to the visible viewport size. This prevents a compromised renderer
from placing the IME candidate window outside the webpage viewport.

Fixed: 519210950
Test: content_unittests --gtest_filter=RenderWidgetHostViewMacTest.SyncGetFirstRectForRange_Clamped
Change-Id: I419e7014ede241d393047378d2ba12349f71c0c5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8092181
Reviewed-by: Avi Drissman <[email protected]>
Commit-Queue: Maggie Chen <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1662699}
---

diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm
index 1b82d56c..90880472 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac.mm
@@ -2369,6 +2369,11 @@
     // which means we have to scale the rect by the device scale factor.
     *rect = gfx::ScaleToEnclosingRect(blink_rect, 1.f / device_scale_factor);
   }
+
+  // Ensure the returned rect is clamped to the viewport to prevent a
+  // compromised renderer from placing IME windows outside the page.
+  // See https://crbug.com/519210950.
+  rect->AdjustToFit(gfx::Rect(GetVisibleViewportSize()));
   return true;
 }
 
diff --git a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
index dcc0c0a7..d8f3530d 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
@@ -33,7 +33,9 @@
 #include "content/browser/compositor/image_transport_factory.h"
 #include "content/browser/gpu/compositor_util.h"
 #include "content/browser/renderer_host/frame_token_message_queue.h"
+#include "content/browser/renderer_host/frame_tree.h"
 #include "content/browser/renderer_host/render_widget_host_delegate.h"
+#include "content/browser/renderer_host/text_input_client_mac.h"
 #include "content/browser/renderer_host/text_input_manager.h"
 #include "content/browser/site_instance_group.h"
 #include "content/common/features.h"
@@ -51,6 +53,7 @@
 #include "content/test/stub_render_widget_host_owner_delegate.h"
 #include "content/test/test_render_view_host.h"
 #include "content/test/test_render_widget_host.h"
+#include "content/test/test_web_contents.h"
 #include "gpu/ipc/service/image_transport_surface.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -424,9 +427,10 @@
   MockRenderWidgetHostImpl(RenderWidgetHostDelegate* delegate,
                            base::SafeRef<SiteInstanceGroup> site_instance_group,
                            int32_t routing_id,
-                           bool for_frame_widget)
+                           bool for_frame_widget,
+                           FrameTree* frame_tree = nullptr)
       : RenderWidgetHostImpl(
-            /*frame_tree=*/nullptr,
+            frame_tree,
             /*self_owned=*/false,
             DefaultFrameSinkId(*site_instance_group, routing_id),
             delegate,
@@ -530,7 +534,7 @@
     host_ = std::make_unique<MockRenderWidgetHostImpl>(
         &delegate_, site_instance_group_->GetSafeRef(),
         process_host_->GetNextRoutingID(),
-        /*for_frame_widget=*/true);
+        /*for_frame_widget=*/true, &contents()->GetPrimaryFrameTree());
     host_->set_owner_delegate(&mock_owner_delegate_);
     delegate_.set_focused_widget(host_.get());
     rwhv_mac_ = new RenderWidgetHostViewMac(host_.get());
@@ -2518,4 +2522,78 @@
   EXPECT_NSEQ([view accessibilityParent], parent_view);
 }
 
+class FakeTextInputClientMacDelegate
+    : public TextInputClientMac::AsyncRequestDelegate {
+ public:
+  FakeTextInputClientMacDelegate() = default;
+  ~FakeTextInputClientMacDelegate() override = default;
+
+  void SetResponseRect(const gfx::Rect& rect) { response_rect_ = rect; }
+
+  void GetCharacterIndexAtPoint(
+      RenderFrameHost* rfh,
+      const TextInputClientMac::RequestToken& request_token,
+      const gfx::Point& point) override {
+    FAIL() << "Unexpected call to GetCharacterIndexAtPoint";
+  }
+
+  void GetFirstRectForRange(
+      RenderFrameHost* rfh,
+      const TextInputClientMac::RequestToken& request_token,
+      const gfx::Range& range) override {
+    TextInputClientMac::GetInstance()->SetFirstRectWhileLockedForTesting(
+        request_token, response_rect_);
+  }
+
+ private:
+  gfx::Rect response_rect_;
+};
+
+TEST_F(RenderWidgetHostViewMacTest, SyncGetFirstRectForRange_Clamped) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures(
+      /*enabled_features=*/{},
+      /*disabled_features=*/{
+          features::kCachedFirstRectAllowRangeOutsideSelection,
+          features::kCachedFirstRectAllowInvalidSelection});
+
+  // Focus the root frame tree node so GetFocusedRenderFrameHostImpl succeeds.
+  contents()->GetPrimaryFrameTree().SetFocusedFrame(
+      contents()->GetPrimaryFrameTree().root(), nullptr);
+
+  // Set the view bounds to a known size.
+  rwhv_mac_->SetBounds(gfx::Rect(0, 0, 800, 600));
+
+  // Create a fake delegate that returns an out-of-bounds rect.
+  // Forged rect: x=-100, y=-200, w=10, h=20 (in physical pixels).
+  float dsf = rwhv_mac_->GetDeviceScaleFactor();
+  gfx::Rect forged_rect_in_pixels(-100, -200, 10, 20);
+
+  auto fake_delegate = std::make_unique<FakeTextInputClientMacDelegate>();
+  fake_delegate->SetResponseRect(forged_rect_in_pixels);
+
+  TextInputClientMac::GetInstance()->SetAsyncRequestDelegateForTesting(
+      std::move(fake_delegate));
+
+  gfx::Rect rect;
+  gfx::Range actual_range;
+  bool success = false;
+
+  // Call the method under test.
+  rwhv_mac_->SyncGetFirstRectForRange(gfx::Range(1, 2), &rect, &actual_range,
+                                      &success);
+
+  EXPECT_TRUE(success);
+
+  // Expected clamped rect (in DIPs).
+  // Clamped to viewport (0, 0, 800, 600):
+  // X should be clamped to 0.
+  // Y should be clamped to 0.
+  gfx::Rect expected_rect(0, 0, 10 / dsf, 20 / dsf);
+  EXPECT_EQ(rect, expected_rect);
+
+  // Restore default delegate.
+  TextInputClientMac::GetInstance()->SetAsyncRequestDelegateForTesting(nullptr);
+}
+
 }  // namespace content
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
index dcc0c0a7..d8f3530d 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
@@ -33,7 +33,9 @@
 #include "content/browser/compositor/image_transport_factory.h"
 #include "content/browser/gpu/compositor_util.h"
 #include "content/browser/renderer_host/frame_token_message_queue.h"
+#include "content/browser/renderer_host/frame_tree.h"
 #include "content/browser/renderer_host/render_widget_host_delegate.h"
+#include "content/browser/renderer_host/text_input_client_mac.h"
 #include "content/browser/renderer_host/text_input_manager.h"
 #include "content/browser/site_instance_group.h"
 #include "content/common/features.h"
@@ -51,6 +53,7 @@
 #include "content/test/stub_render_widget_host_owner_delegate.h"
 #include "content/test/test_render_view_host.h"
 #include "content/test/test_render_widget_host.h"
+#include "content/test/test_web_contents.h"
 #include "gpu/ipc/service/image_transport_surface.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -424,9 +427,10 @@
   MockRenderWidgetHostImpl(RenderWidgetHostDelegate* delegate,
                            base::SafeRef<SiteInstanceGroup> site_instance_group,
                            int32_t routing_id,
-                           bool for_frame_widget)
+                           bool for_frame_widget,
+                           FrameTree* frame_tree = nullptr)
       : RenderWidgetHostImpl(
-            /*frame_tree=*/nullptr,
+            frame_tree,
             /*self_owned=*/false,
             DefaultFrameSinkId(*site_instance_group, routing_id),
             delegate,
@@ -530,7 +534,7 @@
     host_ = std::make_unique<MockRenderWidgetHostImpl>(
         &delegate_, site_instance_group_->GetSafeRef(),
         process_host_->GetNextRoutingID(),
-        /*for_frame_widget=*/true);
+        /*for_frame_widget=*/true, &contents()->GetPrimaryFrameTree());
     host_->set_owner_delegate(&mock_owner_delegate_);
     delegate_.set_focused_widget(host_.get());
     rwhv_mac_ = new RenderWidgetHostViewMac(host_.get());
@@ -2518,4 +2522,78 @@
   EXPECT_NSEQ([view accessibilityParent], parent_view);
 }
 
+class FakeTextInputClientMacDelegate
+    : public TextInputClientMac::AsyncRequestDelegate {
+ public:
+  FakeTextInputClientMacDelegate() = default;
+  ~FakeTextInputClientMacDelegate() override = default;
+
+  void SetResponseRect(const gfx::Rect& rect) { response_rect_ = rect; }
+
+  void GetCharacterIndexAtPoint(
+      RenderFrameHost* rfh,
+      const TextInputClientMac::RequestToken& request_token,
+      const gfx::Point& point) override {
+    FAIL() << "Unexpected call to GetCharacterIndexAtPoint";
+  }
+
+  void GetFirstRectForRange(
+      RenderFrameHost* rfh,
+      const TextInputClientMac::RequestToken& request_token,
+      const gfx::Range& range) override {
+    TextInputClientMac::GetInstance()->SetFirstRectWhileLockedForTesting(
+        request_token, response_rect_);
+  }
+
+ private:
+  gfx::Rect response_rect_;
+};
+
+TEST_F(RenderWidgetHostViewMacTest, SyncGetFirstRectForRange_Clamped) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures(
+      /*enabled_features=*/{},
+      /*disabled_features=*/{
+          features::kCachedFirstRectAllowRangeOutsideSelection,
+          features::kCachedFirstRectAllowInvalidSelection});
+
+  // Focus the root frame tree node so GetFocusedRenderFrameHostImpl succeeds.
+  contents()->GetPrimaryFrameTree().SetFocusedFrame(
+      contents()->GetPrimaryFrameTree().root(), nullptr);
+
+  // Set the view bounds to a known size.
+  rwhv_mac_->SetBounds(gfx::Rect(0, 0, 800, 600));
+
+  // Create a fake delegate that returns an out-of-bounds rect.
+  // Forged rect: x=-100, y=-200, w=10, h=20 (in physical pixels).
+  float dsf = rwhv_mac_->GetDeviceScaleFactor();
+  gfx::Rect forged_rect_in_pixels(-100, -200, 10, 20);
+
+  auto fake_delegate = std::make_unique<FakeTextInputClientMacDelegate>();
+  fake_delegate->SetResponseRect(forged_rect_in_pixels);
+
+  TextInputClientMac::GetInstance()->SetAsyncRequestDelegateForTesting(
+      std::move(fake_delegate));
+
+  gfx::Rect rect;
+  gfx::Range actual_range;
+  bool success = false;
+
+  // Call the method under test.
+  rwhv_mac_->SyncGetFirstRectForRange(gfx::Range(1, 2), &rect, &actual_range,
+                                      &success);
+
+  EXPECT_TRUE(success);
+
+  // Expected clamped rect (in DIPs).
+  // Clamped to viewport (0, 0, 800, 600):
+  // X should be clamped to 0.
+  // Y should be clamped to 0.
+  gfx::Rect expected_rect(0, 0, 10 / dsf, 20 / dsf);
+  EXPECT_EQ(rect, expected_rect);
+
+  // Restore default delegate.
+  TextInputClientMac::GetInstance()->SetAsyncRequestDelegateForTesting(nullptr);
+}
+
 }  // 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.