Low watch CVSS 4.3 chrome Logic Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Low
Severity
4.3
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInappropriate implementation in Compositing
DescriptionInappropriate implementation in Compositing
ComponentCompositing
Sub-componentInternals>Compositing>Scroll
Bug ClassLogic Error
Action tierwatch — logic/policy bug — security-relevant, no memory-unsafety
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker377948403
Fix commit4d86cee91c43 (chromium/src) +137/-58
CWECWE-79 (Cross-site scripting)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N
CISA KEVNot listed
CreditedDahyeon Park
Reported2024-11-08 · bounty $1000 (per vendor advisory)
Fix landed2024-11-13 (public git)
Disclosed2025-01-14
Patch gap62 days — fix public in source before the advisory

Background

`PaintedScrollbarLayerImpl`
The compositor-side (cc) layer implementation that emits draw quads for a scrollbar’s track, buttons, and thumb during frame generation.
`SharedQuadState`
A viz structure holding transform, quad_layer_rect, visible_quad_layer_rect, clip, and opacity that multiple draw quads share so the display compositor can clip and composite them correctly.
`internal_contents_scale_`
The device/content scale factor applied to layer-space rects to convert them into the scaled content space in which quads are actually drawn.
`visible_layer_rect()`
The layer-space rectangle the compositor believes is visible, used to clip content before it is scaled up and rasterized.

Root Cause Analysis

The vulnerable path was the solid-color thumb branch of PaintedScrollbarLayerImpl::AppendQuads, guarded by thumb_color_.has_value(). That branch computed thumb_rect and visible_thumb_rect in unscaled layer space, intersected the visible rect with visible_layer_rect(), and then called SetAll/SetNew with those unscaled rects while the transform in the SharedQuadState still applied internal_contents_scale_. When a layer has a large scale, a single layer-space “pixel” maps to a very large region, so clipping in layer space before scaling is unreliable: scale-after-clip produces a quad that exceeds the intended clip bounds. Because this branch built and configured its own SharedQuadState via SetAll instead of going through PopulateScaledSharedQuadState, the clip rect that the shared, non-solid-color path relied on was never applied.

The fix unifies both paths so PopulateScaledSharedQuadState runs first and the thumb quad is emitted with gfx::ScaleToEnclosingRect-scaled rects (scaled_thumb_quad_rect, scaled_visible_thumb_quad_rect), ensuring the quad geometry and clip are computed in the same scaled space and can no longer overflow.

Key insight
The core mistake was clipping and emitting the solid-color thumb quad in unscaled layer space while relying on a hand-built SharedQuadState that skipped the shared clip logic; the fix routes the branch through PopulateScaledSharedQuadState and scales the quad rects with gfx::ScaleToEnclosingRect so clipping happens consistently in scaled content space.

Attack Path

  1. Force a large compositor scale A page applies a very large CSS transform scale (or device scale) to an element that owns a painted scrollbar, so internal_contents_scale_ becomes large.
  2. Trigger the solid-color thumb path The scrollbar is configured so thumb_color_.has_value() is true, selecting the vulnerable branch of AppendQuads.
  3. Defeat layer-space clipping Because clipping uses unscaled thumb_rect and visible_layer_rect() before the scale is applied, the resulting SolidColorDrawQuad covers a region far larger than the intended visible/clip area after scaling.
  4. Produce out-of-bounds compositing geometry The oversized, unclipped quad reaches viz, causing the thumb to be composited over regions it should not touch, mis-occluding or over-drawing other content.

Impact Assessment

An attacker gains an incorrect-compositing primitive in the GPU/viz display pipeline of the browser, letting a scrollbar thumb quad draw or occlude outside its intended clip when a large scale is applied. This is reachable from ordinary untrusted web content (no special privileges) that merely styles a scrollable element with a large transform, and manifests as inappropriate implementation in Compositing rather than direct memory corruption.

Changed Functions

FunctionChangeNotes
if
cc/layers/painted_scrollbar_layer_impl.cc
modified

Files Changed

  • cc/layers/painted_scrollbar_layer_impl.cc
  • cc/layers/painted_scrollbar_layer_impl_unittest.cc

Audit Directions

  • Clip-before-scale ordering
    Review other AppendQuads implementations that intersect against visible_layer_rect() or occlusion in unscaled layer space before applying internal_contents_scale_, since scale-after-clip can inflate quads past their clip.
  • Hand-built SharedQuadState
    Flag code paths that call SharedQuadState::SetAll directly instead of PopulateScaledSharedQuadState, as they may bypass the shared clip-rect application and drift from the scaled path.
  • Large-scale layer geometry
    Fuzz and test compositor layers under extreme transform/device scales where a single layer-space pixel maps to a large area, exercising thumb, track, and other solid-color quad emission for out-of-clip overflow.
From 4d86cee91c4307c8bb4fe2a8b6874b661482b179 Mon Sep 17 00:00:00 2001
From: Xianzhu Wang <wangxianzhu@chromium.org>
Date: Wed, 13 Nov 2024 22:17:29 +0000
Subject: [PATCH] Fix solid color thumb quad under large scale

If a layer has a large scale, visible_layer_rect() can't reliably
clip a quad before scaling because a "pixel" in the layer is very
large and scale-after-clip will create a quad exceeding the clip.

clip
  large-scale
    layer

Now share more code with the non-solid-color-thumb code path.

For a AppendQuads method, to ensure the clip rect is applied, it's
better to call PopulateScaledSharedQuadState() instead of creating a
shared quad state by itself.

Bug: 377948403
Change-Id: I8d7dd08fe9fc7907685bfbaa1a5fd5682688aed0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6014683
Reviewed-by: Philip Rogers <pdr@chromium.org>
Commit-Queue: Xianzhu Wang <wangxianzhu@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1382613}
---

diff --git a/cc/layers/painted_scrollbar_layer_impl.cc b/cc/layers/painted_scrollbar_layer_impl.cc
index ed9b32b..12fb191 100644
--- a/cc/layers/painted_scrollbar_layer_impl.cc
+++ b/cc/layers/painted_scrollbar_layer_impl.cc
@@ -109,19 +109,28 @@
     AppendQuadsData* append_quads_data) const {
   viz::SharedQuadState* shared_quad_state =
       render_pass->CreateAndAppendSharedQuadState();
-  if (thumb_color_.has_value()) {
-    const gfx::Rect thumb_rect = ComputeThumbQuadRect();
-    if (thumb_rect.IsEmpty()) {
-      return;
-    }
-    gfx::Rect visible_thumb_rect =
-        draw_properties().occlusion_in_content_space.GetUnoccludedContentRect(
-            thumb_rect);
-    visible_thumb_rect.Intersect(visible_layer_rect());
-    if (visible_thumb_rect.IsEmpty()) {
-      return;
-    }
+  // The thumb sqs must be non-opaque so that the track and buttons will not be
+  // occluded in viz by the thumb's 'quad_layer_rect'.
+  constexpr bool kContentsOpaque = false;
+  PopulateScaledSharedQuadState(shared_quad_state, internal_contents_scale_,
+                                kContentsOpaque);
 
+  AppendDebugBorderQuad(render_pass, gfx::Rect(internal_content_bounds_),
+                        shared_quad_state, append_quads_data);
+
+  const gfx::Rect thumb_quad_rect = ComputeThumbQuadRect();
+  const gfx::Rect scaled_thumb_quad_rect =
+      gfx::ScaleToEnclosingRect(thumb_quad_rect, internal_contents_scale_);
+  const gfx::Rect visible_thumb_quad_rect =
+      draw_properties().occlusion_in_content_space.GetUnoccludedContentRect(
+          thumb_quad_rect);
+  if (visible_thumb_quad_rect.IsEmpty()) {
+    return;
+  }
+  const gfx::Rect scaled_visible_thumb_quad_rect = gfx::ScaleToEnclosingRect(
+      visible_thumb_quad_rect, internal_contents_scale_);
+
+  if (thumb_color_.has_value()) {
     gfx::MaskFilterInfo rounded_corners_mask =
         draw_properties().mask_filter_info;
     // Web tests draw the thumb as a square to avoid issues that come with the
@@ -130,24 +139,18 @@
     if (!is_web_test() && IsFluentScrollbarEnabled()) {
       const int rounded_corner_radius =
           orientation() == ScrollbarOrientation::kHorizontal
-              ? thumb_rect.height()
-              : thumb_rect.width();
-      rounded_corners_mask = gfx::MaskFilterInfo(
-          gfx::RRectF(gfx::RectF(thumb_rect), rounded_corner_radius));
-      rounded_corners_mask.ApplyTransform(
+              ? thumb_quad_rect.height()
+              : thumb_quad_rect.width();
+      shared_quad_state->mask_filter_info = gfx::MaskFilterInfo(
+          gfx::RRectF(gfx::RectF(thumb_quad_rect), rounded_corner_radius));
+      shared_quad_state->mask_filter_info.ApplyTransform(
           draw_properties().target_space_transform);
     }
-    shared_quad_state->SetAll(
-        draw_properties().target_space_transform, thumb_rect,
-        visible_thumb_rect, rounded_corners_mask, /*clip=*/std::nullopt,
-        /*contents_opaque=*/false, draw_properties().opacity,
-        /*blend=*/SkBlendMode::kSrcOver, GetSortingContextId(),
-        static_cast<uint32_t>(id()),
-        /*fast_rounded_corner=*/true);
     auto* thumb_quad =
         render_pass->CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>();
-    thumb_quad->SetNew(shared_quad_state, thumb_rect, visible_thumb_rect,
-                       thumb_color_.value(), /*anti_aliasing_off=*/false);
+    thumb_quad->SetNew(shared_quad_state, scaled_thumb_quad_rect,
+                       scaled_visible_thumb_quad_rect, thumb_color_.value(),
+                       /*anti_aliasing_off=*/false);
     ValidateQuadResources(thumb_quad);
     return;
   }
@@ -159,26 +162,9 @@
     return;
   }
 
-  // The thumb sqs must be non-opaque so that the track and buttons will not be
-  // occluded in viz by the thumb's 'quad_layer_rect'.
-  constexpr bool kContentsOpaque = false;
-  PopulateScaledSharedQuadState(shared_quad_state, internal_contents_scale_,
-                                kContentsOpaque);
-
-  AppendDebugBorderQuad(render_pass, gfx::Rect(internal_content_bounds_),
-                        shared_quad_state, append_quads_data);
-  gfx::Rect thumb_quad_rect = ComputeThumbQuadRect();
-  gfx::Rect scaled_thumb_quad_rect =
-      gfx::ScaleToEnclosingRect(thumb_quad_rect, internal_contents_scale_);
-  gfx::Rect visible_thumb_quad_rect =
-      draw_properties().occlusion_in_content_space.GetUnoccludedContentRect(
-          thumb_quad_rect);
-  gfx::Rect scaled_visible_thumb_quad_rect = gfx::ScaleToEnclosingRect(
-      visible_thumb_quad_rect, internal_contents_scale_);
   viz::ResourceId thumb_resource_id =
       layer_tree_impl()->ResourceIdForUIResource(thumb_ui_resource_id_);
-
-  if (!thumb_resource_id || visible_thumb_quad_rect.IsEmpty()) {
+  if (!thumb_resource_id) {
     return;
   }
 
diff --git a/cc/layers/painted_scrollbar_layer_impl_unittest.cc b/cc/layers/painted_scrollbar_layer_impl_unittest.cc
index 26ff7c4..6ea79f9d 100644
--- a/cc/layers/painted_scrollbar_layer_impl_unittest.cc
+++ b/cc/layers/painted_scrollbar_layer_impl_unittest.cc
@@ -65,8 +65,7 @@
   impl.CalcDrawProps(viewport_size);
 
   gfx::Rect thumb_rect = scrollbar_layer_impl->ComputeThumbQuadRect();
-  EXPECT_EQ(gfx::Rect(0, 500 / 4, 10, layer_size.height() / 2).ToString(),
-            thumb_rect.ToString());
+  EXPECT_EQ(gfx::Rect(0, 500 / 4, 10, layer_size.height() / 2), thumb_rect);
 
   {
     SCOPED_TRACE("No occlusion");
@@ -97,16 +96,14 @@
         viz::TextureDrawQuad::MaterialCast(track_and_buttons_draw_quad);
 
     gfx::Rect scaled_thumb_rect = gfx::ScaleToEnclosingRect(thumb_rect, scale);
-    EXPECT_EQ(track_and_buttons_quad->rect.ToString(),
-              gfx::Rect(scaled_layer_size).ToString());
+    EXPECT_EQ(track_and_buttons_quad->rect, gfx::Rect(scaled_layer_size));
     EXPECT_EQ(scrollbar_layer_impl->contents_opaque(),
               track_and_buttons_quad->shared_quad_state->are_contents_opaque);
-    EXPECT_EQ(track_and_buttons_quad->visible_rect.ToString(),
-              gfx::Rect(scaled_layer_size).ToString());
+    EXPECT_EQ(track_and_buttons_quad->visible_rect,
+              gfx::Rect(scaled_layer_size));
     EXPECT_FALSE(track_and_buttons_quad->needs_blending);
-    EXPECT_EQ(thumb_quad->rect.ToString(), scaled_thumb_rect.ToString());
-    EXPECT_EQ(thumb_quad->visible_rect.ToString(),
-              scaled_thumb_rect.ToString());
+    EXPECT_EQ(thumb_quad->rect, scaled_thumb_rect);
+    EXPECT_EQ(thumb_quad->visible_rect, scaled_thumb_rect);
     EXPECT_TRUE(thumb_quad->needs_blending);
     EXPECT_FALSE(thumb_quad->shared_quad_state->are_contents_opaque);
 
@@ -184,8 +181,8 @@
   impl.CalcDrawProps(viewport_size);
 
   gfx::Rect thumb_rect = scrollbar_layer_impl->ComputeThumbQuadRect();
-  EXPECT_EQ(gfx::Rect(2, 500 / 4, 11, layer_size.height() / 2).ToString(),
-            thumb_rect.ToString());
+  EXPECT_EQ(gfx::Rect(2, 500 / 4, 11, layer_size.height() / 2), thumb_rect);
+  gfx::Rect scaled_thumb_rect = gfx::ScaleToEnclosingRect(thumb_rect, scale);
 
   {
     SCOPED_TRACE("No occlusion");
@@ -220,8 +217,8 @@
     EXPECT_EQ(track_and_buttons_quad->visible_rect,
               gfx::Rect(scaled_layer_size));
     EXPECT_FALSE(track_and_buttons_quad->needs_blending);
-    EXPECT_EQ(thumb_quad->rect, thumb_rect);
-    EXPECT_EQ(thumb_quad->visible_rect, thumb_rect);
+    EXPECT_EQ(thumb_quad->rect, scaled_thumb_rect);
+    EXPECT_EQ(thumb_quad->visible_rect, scaled_thumb_rect);
     EXPECT_FALSE(thumb_quad->needs_blending);
     EXPECT_FALSE(thumb_quad->shared_quad_state->are_contents_opaque);
 
@@ -252,6 +249,84 @@
   }
 }
 
+TEST(PaintedScrollbarLayerImplTest,
+     SolidColorThumbNinePatchTrackUnderClipAndScale) {
+  gfx::Size layer_size(15, 1000);
+  gfx::Rect clip_rect(0, 0, 1000, 1000);
+  float scale = 33.f;
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/cc/layers/painted_scrollbar_layer_impl_unittest.cc b/cc/layers/painted_scrollbar_layer_impl_unittest.cc
index 26ff7c4..6ea79f9d 100644
--- a/cc/layers/painted_scrollbar_layer_impl_unittest.cc
+++ b/cc/layers/painted_scrollbar_layer_impl_unittest.cc
@@ -65,8 +65,7 @@
   impl.CalcDrawProps(viewport_size);
 
   gfx::Rect thumb_rect = scrollbar_layer_impl->ComputeThumbQuadRect();
-  EXPECT_EQ(gfx::Rect(0, 500 / 4, 10, layer_size.height() / 2).ToString(),
-            thumb_rect.ToString());
+  EXPECT_EQ(gfx::Rect(0, 500 / 4, 10, layer_size.height() / 2), thumb_rect);
 
   {
     SCOPED_TRACE("No occlusion");
@@ -97,16 +96,14 @@
         viz::TextureDrawQuad::MaterialCast(track_and_buttons_draw_quad);
 
     gfx::Rect scaled_thumb_rect = gfx::ScaleToEnclosingRect(thumb_rect, scale);
-    EXPECT_EQ(track_and_buttons_quad->rect.ToString(),
-              gfx::Rect(scaled_layer_size).ToString());
+    EXPECT_EQ(track_and_buttons_quad->rect, gfx::Rect(scaled_layer_size));
     EXPECT_EQ(scrollbar_layer_impl->contents_opaque(),
               track_and_buttons_quad->shared_quad_state->are_contents_opaque);
-    EXPECT_EQ(track_and_buttons_quad->visible_rect.ToString(),
-              gfx::Rect(scaled_layer_size).ToString());
+    EXPECT_EQ(track_and_buttons_quad->visible_rect,
+              gfx::Rect(scaled_layer_size));
     EXPECT_FALSE(track_and_buttons_quad->needs_blending);
-    EXPECT_EQ(thumb_quad->rect.ToString(), scaled_thumb_rect.ToString());
-    EXPECT_EQ(thumb_quad->visible_rect.ToString(),
-              scaled_thumb_rect.ToString());
+    EXPECT_EQ(thumb_quad->rect, scaled_thumb_rect);
+    EXPECT_EQ(thumb_quad->visible_rect, scaled_thumb_rect);
     EXPECT_TRUE(thumb_quad->needs_blending);
     EXPECT_FALSE(thumb_quad->shared_quad_state->are_contents_opaque);
 
@@ -184,8 +181,8 @@
   impl.CalcDrawProps(viewport_size);
 
   gfx::Rect thumb_rect = scrollbar_layer_impl->ComputeThumbQuadRect();
-  EXPECT_EQ(gfx::Rect(2, 500 / 4, 11, layer_size.height() / 2).ToString(),
-            thumb_rect.ToString());
+  EXPECT_EQ(gfx::Rect(2, 500 / 4, 11, layer_size.height() / 2), thumb_rect);
+  gfx::Rect scaled_thumb_rect = gfx::ScaleToEnclosingRect(thumb_rect, scale);
 
   {
     SCOPED_TRACE("No occlusion");
@@ -220,8 +217,8 @@
     EXPECT_EQ(track_and_buttons_quad->visible_rect,
               gfx::Rect(scaled_layer_size));
     EXPECT_FALSE(track_and_buttons_quad->needs_blending);
-    EXPECT_EQ(thumb_quad->rect, thumb_rect);
-    EXPECT_EQ(thumb_quad->visible_rect, thumb_rect);
+    EXPECT_EQ(thumb_quad->rect, scaled_thumb_rect);
+    EXPECT_EQ(thumb_quad->visible_rect, scaled_thumb_rect);
     EXPECT_FALSE(thumb_quad->needs_blending);
     EXPECT_FALSE(thumb_quad->shared_quad_state->are_contents_opaque);
 
@@ -252,6 +249,84 @@
   }
 }
 
+TEST(PaintedScrollbarLayerImplTest,
+     SolidColorThumbNinePatchTrackUnderClipAndScale) {
+  gfx::Size layer_size(15, 1000);
+  gfx::Rect clip_rect(0, 0, 1000, 1000);
+  float scale = 33.f;
+  gfx::Size scaled_layer_size = gfx::ScaleToCeiledSize(layer_size, scale);
+  gfx::Size viewport_size(1000, 1000);
+
+  LayerTreeImplTestBase impl;
+
+  SkBitmap thumb_sk_bitmap;
+  thumb_sk_bitmap.allocN32Pixels(10, 10);
+  thumb_sk_bitmap.setImmutable();
+  UIResourceId thumb_uid = 5;
+  UIResourceBitmap thumb_bitmap(thumb_sk_bitmap);
+  impl.host_impl()->CreateUIResource(thumb_uid, thumb_bitmap);
+
+  SkBitmap track_and_buttons_sk_bitmap;
+  track_and_buttons_sk_bitmap.allocN32Pixels(10, 10);
+  track_and_buttons_sk_bitmap.setImmutable();
+  UIResourceId track_and_buttons_uid = 6;
+  UIResourceBitmap track_and_buttons_bitmap(track_and_buttons_sk_bitmap);
+  impl.host_impl()->CreateUIResource(track_and_buttons_uid,
+                                     track_and_buttons_bitmap);
+
+  ScrollbarOrientation orientation = ScrollbarOrientation::kVertical;
+
+  PaintedScrollbarLayerImpl* scrollbar_layer_impl =
+      impl.AddLayerInActiveTree<PaintedScrollbarLayerImpl>(orientation, false,
+                                                           false);
+  scrollbar_layer_impl->SetBounds(layer_size);
+  scrollbar_layer_impl->SetContentsOpaque(true);
+  scrollbar_layer_impl->set_internal_contents_scale_and_bounds(
+      scale, scaled_layer_size);
+  scrollbar_layer_impl->SetDrawsContent(true);
+  scrollbar_layer_impl->SetThumbThickness(11);
+  scrollbar_layer_impl->SetThumbLength(500);
+  scrollbar_layer_impl->SetTrackRect(gfx::Rect(0, 0, 15, layer_size.height()));
+  scrollbar_layer_impl->SetCurrentPos(100.f / 4);
+  scrollbar_layer_impl->SetClipLayerLength(100.f);
+  scrollbar_layer_impl->SetScrollLayerLength(200.f);
+  scrollbar_layer_impl->set_track_and_buttons_ui_resource_id(
+      track_and_buttons_uid);
+  scrollbar_layer_impl->SetThumbColor(SkColors::kRed);
+
+  CopyProperties(impl.root_layer(), scrollbar_layer_impl);
+  CreateClipNode(scrollbar_layer_impl).clip = gfx::RectF(clip_rect);
+  CreateTransformNode(scrollbar_layer_impl).local.Scale(scale);
+
+  impl.CalcDrawProps(viewport_size);
+
+  gfx::Rect thumb_rect = scrollbar_layer_impl->ComputeThumbQuadRect();
+  EXPECT_EQ(gfx::Rect(2, 500 / 4, 11, layer_size.height() / 2), thumb_rect);
+  gfx::Rect scaled_thumb_rect = gfx::ScaleToEnclosingRect(thumb_rect, scale);
+
+  impl.AppendQuads(scrollbar_layer_impl);
+  EXPECT_EQ(2u, impl.quad_list().size());
+
+  const viz::SolidColorDrawQuad* thumb_quad =
+      viz::SolidColorDrawQuad::MaterialCast(impl.quad_list().ElementAt(0));
+  EXPECT_EQ(thumb_quad->rect, scaled_thumb_rect);
+  EXPECT_EQ(thumb_quad->visible_rect, scaled_thumb_rect);
+  EXPECT_FALSE(thumb_quad->needs_blending);
+  EXPECT_FALSE(thumb_quad->shared_quad_state->are_contents_opaque);
+  EXPECT_EQ(1.f, thumb_quad->shared_quad_state->opacity);
+
+  const viz::TextureDrawQuad* track_and_buttons_quad =
+      viz::TextureDrawQuad::MaterialCast(impl.quad_list().ElementAt(1));
+  EXPECT_EQ(track_and_buttons_quad->shared_quad_state->clip_rect, clip_rect);
+  EXPECT_EQ(track_and_buttons_quad->rect, gfx::Rect(scaled_layer_size));
+  EXPECT_EQ(scrollbar_layer_impl->contents_opaque(),
+            track_and_buttons_quad->shared_quad_state->are_contents_opaque);
+  EXPECT_EQ(track_and_buttons_quad->visible_rect, gfx::Rect(scaled_layer_size));
+  EXPECT_FALSE(track_and_buttons_quad->needs_blending);
+  EXPECT_EQ(track_and_buttons_quad->shared_quad_state->clip_rect, clip_rect);
+  EXPECT_EQ(1.f, track_and_buttons_quad->shared_quad_state->opacity);
+}
+
 TEST(PaintedScrollbarLayerImplTest, PaintedOpacityChangesInvalidate) {
   LayerTreeImplTestBase impl;
   ScrollbarOrientation orientation = ScrollbarOrientation::kVertical;
@@ -339,7 +414,7 @@
     : public PaintedScrollbarLayerImplSolidColorThumbTest {
  protected:
   void SetUp() override {
-    LayerTreeSettings settings;
+    LayerTreeSettings settings = CommitToPendingTreeLayerListSettings();
     settings.enable_fluent_overlay_scrollbar = true;
     settings.enable_fluent_scrollbar = true;
     impl_ = std::make_unique<LayerTreeImplTestBase>(settings);
@@ -394,6 +469,9 @@
   scrollbar_layer_impl->SetThumbThickness(
       scrollbar_layer_impl->GetIdleThicknessScale());
 
+  CopyProperties(impl_->root_layer(), scrollbar_layer_impl);
+  impl_->CalcDrawProps(gfx::Size(500, 500));
+
   // AppendThumbQuads should return early after checking that there is no
   // `thumb_color_` and Fluent scrollbars are enabled.
   // AppendTrackQuads should return early when the Fluent overlay code decides
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollbar-large-scale-in-iframe-ref.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollbar-large-scale-in-iframe-ref.html
new file mode 100644
index 0000000..38c8d0c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollbar-large-scale-in-iframe-ref.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<div style="position: absolute; top: 100px; left: 100px;
+            width: 100px; height: 100px; background: green"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollbar-large-scale-in-iframe.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollbar-large-scale-in-iframe.html
new file mode 100644
index 0000000..1808d1d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollbar-large-scale-in-iframe.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<link rel="help" href="https://crbug.com/377948403">
+<link rel="match" href="scrollbar-large-scale-in-iframe-ref.html">
+<iframe style="position: absolute; top: 100px; left: 100px;
+               border: none; width: 100px; height: 100px"
+        srcdoc="
+  <select multiple style='transform: scale(500, 10)'>
+    <optgroup style='height: 1000px'></optgroup>
+  </select>">
+</iframe>
+<div style="position: absolute; top: 100px; left: 100px;
+            width: 100px; height: 100px; background: green"></div>
diff --git a/third_party/blink/web_tests/platform/linux/fast/sub-pixel/transformed-iframe-copy-on-scroll-expected.png b/third_party/blink/web_tests/platform/linux/fast/sub-pixel/transformed-iframe-copy-on-scroll-expected.png
index 21e6518a..47ab936 100644
--- a/third_party/blink/web_tests/platform/linux/fast/sub-pixel/transformed-iframe-copy-on-scroll-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/sub-pixel/transformed-iframe-copy-on-scroll-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/frames/iframe-scaling-with-scroll-expected.png b/third_party/blink/web_tests/platform/win/fast/frames/iframe-scaling-with-scroll-expected.png
index b3caa2d..fdfac45 100644
--- a/third_party/blink/web_tests/platform/win/fast/frames/iframe-scaling-with-scroll-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/frames/iframe-scaling-with-scroll-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/sub-pixel/transformed-iframe-copy-on-scroll-expected.png b/third_party/blink/web_tests/platform/win/fast/sub-pixel/transformed-iframe-copy-on-scroll-expected.png
index e9ba7122..d19d0f5 100644
--- a/third_party/blink/web_tests/platform/win/fast/sub-pixel/transformed-iframe-copy-on-scroll-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/sub-pixel/transformed-iframe-copy-on-scroll-expected.png
Binary files differ
Loading diff…

Original Bug Report

reported by tr...@gmail.com

blank <select> and <optgroup> inside iframe can be drawn outside of iframe

Steps to reproduce the problem

  1. Locate frame_1920x1080.html and src.html to same directory
  2. Open frame_1920x1080.html, the grey box overflows outside of iframe

Problem Description

Of the iframes placed in 3x3, only the iframe in the middle has src. The content that should only be drawn in this iframe is being drawn outside the iframe. I haven’t figured out how to turn a grey box into another shape since I don’t know much about CSS, but I think it could be used for a variety of security attacks if it could be.

Not reproducible on chrome 129.0.6668.70

Summary

blank <select> and <optgroup> inside iframe can be drawn outside of iframe

Custom Questions

Reporter credit:

Dahyeon Park

Additional Data

Category: Security
Chrome Channel: Stable
Regression: Yes

View on issue tracker