Low chrome UAF 🔧 Commit mapped

Overview

Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Input
DescriptionUse after free in Input
ComponentInput
Bug ClassUAF
Tracker533113044
Fix commit1732e833369c (chromium/src) +47/-0
CISA KEVNot listed
CreditedGoogle
Disclosed2026-10-20

Changed Functions

FunctionChangeNotes
if
content/browser/web_contents/web_contents_impl.cc
modified
TEST_F
content/browser/web_contents/web_contents_impl_unittest.cc
modified
WebContentsImplTestKeyboardEvents
content/browser/web_contents/web_contents_impl_unittest.cc
modified

Files Changed

  • content/browser/web_contents/web_contents_impl_unittest.cc
  • content/browser/web_contents/web_contents_impl.cc
From 1732e833369ccac6e659d8ede8c60b1bd1143875 Mon Sep 17 00:00:00 2001
From: Jonathan Ross <jonross@chromium.org>
Date: Wed, 16 Sep 2026 12:01:32 -0700
Subject: [PATCH] Clamp selection bounding box to the viewport

TextInputManager::SelectionBoundsChanged clamps `anchor_rect` and
`focus_rect` to the root viewport via TransformAndClampBounds, but the
sibling `bounding_box` field was transformed into root coordinates and
then stored without being clamped.

Consumers such as WebContentsImpl::GetTextSelectionBounds and
RenderWidgetHostViewAura::GetSelectionBoundingBox translate this rect
into screen coordinates to position UI, so a bounding box that falls
outside the viewport can place that UI outside the web contents area.

Clamp the bounding box in ` WebContentsImpl::GetTextSelectionBounds`. We
do not clamp it in `TextInputManager` as the clipboard API needs to know
the full range, even if it is outside the viewport.

TAG=agy
CONV=bfb8e921-0c0e-40cb-a8ce-192a5ff95a3a

Bug: 533113044
Change-Id: If756f435fb8d2ec8c4ad7de08054c2e7c3b3dcc8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8408493
Reviewed-by: Dave Tapuska <dtapuska@chromium.org>
Commit-Queue: Jonathan Ross <jonross@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1699142}
---
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index c01bcd9..683d1490 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -6727,6 +6727,12 @@
       const auto* region = text_input_manager_->GetSelectionRegion(view);
       if (region) {
         gfx::Rect bounds = region->bounding_box;
+        // `bounding_box` is renderer-supplied and is deliberately stored
+        // unclamped, because consumers such as ClipboardHistory rely on it to
+        // describe the full extent of the selection even when that extent is
+        // larger than the viewport. Callers of this method instead use the
+        // result to position UI, so clamp it to the outermost view here.
+        bounds.AdjustToFit(gfx::Rect(root_view->GetVisibleViewportSize()));
         if (!bounds.IsEmpty()) {
           gfx::Point origin = bounds.origin();
           origin += root_view->GetViewBounds().OffsetFromOrigin();
diff --git a/content/browser/web_contents/web_contents_impl_unittest.cc b/content/browser/web_contents/web_contents_impl_unittest.cc
index ee64777..4c7a6042 100644
--- a/content/browser/web_contents/web_contents_impl_unittest.cc
+++ b/content/browser/web_contents/web_contents_impl_unittest.cc
@@ -4086,6 +4086,47 @@
   EXPECT_FALSE(text_input_manager->HasObserver(contents()));
 }
 
+// The renderer-supplied selection bounding box is stored unclamped, but
+// GetTextSelectionBounds() is used to position UI, so the rect it returns must
+// lie inside the view even when the reported selection does not.
+TEST_F(WebContentsImplTest, GetTextSelectionBoundsIsClampedToView) {
+  TestRenderFrameHost* rfh = main_test_rfh();
+  auto* view = static_cast<RenderWidgetHostViewBase*>(rfh->GetView());
+  ASSERT_TRUE(view);
+  view->SetBounds(gfx::Rect(0, 0, 800, 600));
+
+  // Fetching the manager through the view is what registers the view with it.
+  TextInputManager* text_input_manager = view->GetTextInputManager();
+  ASSERT_TRUE(text_input_manager);
+  ASSERT_EQ(text_input_manager, contents()->GetTextInputManager());
+
+  ui::mojom::TextInputState state;
+  state.type = ui::TEXT_INPUT_TYPE_TEXT;
+  text_input_manager->UpdateTextInputState(view, state);
+
+  // A bounding box whose large negative y places it above the top of the view.
+  const gfx::Rect anchor_rect(8, 16, 0, 19);
+  const gfx::Rect out_of_view_bounding_box(200, -120, 40, 20);
+  text_input_manager->SelectionBoundsChanged(
+      view, anchor_rect, base::i18n::LEFT_TO_RIGHT, anchor_rect,
+      base::i18n::LEFT_TO_RIGHT, out_of_view_bounding_box,
+      /*is_anchor_first=*/true);
+
+  // The stored region keeps the unclamped extent.
+  const TextInputManager::SelectionRegion* region =
+      text_input_manager->GetSelectionRegion(view);
+  ASSERT_TRUE(region);
+  EXPECT_EQ(region->bounding_box, out_of_view_bounding_box);
+
+  // The bounds handed to callers are clamped into the view, then offset into
+  // screen coordinates.
+  const std::optional<gfx::Rect> bounds =
+      contents()->GetTextSelectionBounds(rfh);
+  ASSERT_TRUE(bounds.has_value());
+  const gfx::Vector2d offset = view->GetViewBounds().OffsetFromOrigin();
+  EXPECT_EQ(*bounds, gfx::Rect(gfx::Point(200, 0) + offset, gfx::Size(40, 20)));
+}
+
 class WebContentsImplTestKeyboardEvents
     : public WebContentsImplTest,
       public testing::WithParamInterface<blink::WebInputEvent::Type> {};
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/content/browser/web_contents/web_contents_impl_unittest.cc b/content/browser/web_contents/web_contents_impl_unittest.cc
index ee64777..4c7a6042 100644
--- a/content/browser/web_contents/web_contents_impl_unittest.cc
+++ b/content/browser/web_contents/web_contents_impl_unittest.cc
@@ -4086,6 +4086,47 @@
   EXPECT_FALSE(text_input_manager->HasObserver(contents()));
 }
 
+// The renderer-supplied selection bounding box is stored unclamped, but
+// GetTextSelectionBounds() is used to position UI, so the rect it returns must
+// lie inside the view even when the reported selection does not.
+TEST_F(WebContentsImplTest, GetTextSelectionBoundsIsClampedToView) {
+  TestRenderFrameHost* rfh = main_test_rfh();
+  auto* view = static_cast<RenderWidgetHostViewBase*>(rfh->GetView());
+  ASSERT_TRUE(view);
+  view->SetBounds(gfx::Rect(0, 0, 800, 600));
+
+  // Fetching the manager through the view is what registers the view with it.
+  TextInputManager* text_input_manager = view->GetTextInputManager();
+  ASSERT_TRUE(text_input_manager);
+  ASSERT_EQ(text_input_manager, contents()->GetTextInputManager());
+
+  ui::mojom::TextInputState state;
+  state.type = ui::TEXT_INPUT_TYPE_TEXT;
+  text_input_manager->UpdateTextInputState(view, state);
+
+  // A bounding box whose large negative y places it above the top of the view.
+  const gfx::Rect anchor_rect(8, 16, 0, 19);
+  const gfx::Rect out_of_view_bounding_box(200, -120, 40, 20);
+  text_input_manager->SelectionBoundsChanged(
+      view, anchor_rect, base::i18n::LEFT_TO_RIGHT, anchor_rect,
+      base::i18n::LEFT_TO_RIGHT, out_of_view_bounding_box,
+      /*is_anchor_first=*/true);
+
+  // The stored region keeps the unclamped extent.
+  const TextInputManager::SelectionRegion* region =
+      text_input_manager->GetSelectionRegion(view);
+  ASSERT_TRUE(region);
+  EXPECT_EQ(region->bounding_box, out_of_view_bounding_box);
+
+  // The bounds handed to callers are clamped into the view, then offset into
+  // screen coordinates.
+  const std::optional<gfx::Rect> bounds =
+      contents()->GetTextSelectionBounds(rfh);
+  ASSERT_TRUE(bounds.has_value());
+  const gfx::Vector2d offset = view->GetViewBounds().OffsetFromOrigin();
+  EXPECT_EQ(*bounds, gfx::Rect(gfx::Point(200, 0) + offset, gfx::Size(40, 20)));
+}
+
 class WebContentsImplTestKeyboardEvents
     : public WebContentsImplTest,
       public testing::WithParamInterface<blink::WebInputEvent::Type> {};
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.