CVE-2025-0448
Overview
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
vizstructure 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.
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
- 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. - Trigger the solid-color thumb path
The scrollbar is configured so
thumb_color_.has_value()is true, selecting the vulnerable branch ofAppendQuads. - Defeat layer-space clipping
Because clipping uses unscaled
thumb_rectandvisible_layer_rect()before the scale is applied, the resultingSolidColorDrawQuadcovers a region far larger than the intended visible/clip area after scaling. - 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
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
| Function | Change | Notes |
|---|---|---|
ifcc/layers/painted_scrollbar_layer_impl.cc |
modified |
Files Changed
cc/layers/painted_scrollbar_layer_impl.cccc/layers/painted_scrollbar_layer_impl_unittest.cc
Audit Directions
- Clip-before-scale orderingReview other
AppendQuadsimplementations that intersect againstvisible_layer_rect()or occlusion in unscaled layer space before applyinginternal_contents_scale_, since scale-after-clip can inflate quads past their clip. - Hand-built SharedQuadStateFlag code paths that call
SharedQuadState::SetAlldirectly instead ofPopulateScaledSharedQuadState, as they may bypass the shared clip-rect application and drift from the scaled path. - Large-scale layer geometryFuzz 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.
Patch
From 4d86cee91c4307c8bb4fe2a8b6874b661482b179 Mon Sep 17 00:00:00 2001 From: Xianzhu Wang <[email protected]> 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 <[email protected]> Commit-Queue: Xianzhu Wang <[email protected]> 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;
Regression Test / PoC
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
Original Bug Report
blank <select> and <optgroup> inside iframe can be drawn outside of iframe
Steps to reproduce the problem
- Locate frame_1920x1080.html and src.html to same directory
- 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