Chrome · Paint
CVE-2026-106182
Spoofing in Paint
Overview
Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
TEST_Pthird_party/blink/renderer/core/paint/compositing/compositing_test.cc |
modified |
Files Changed
third_party/blink/renderer/core/paint/compositing/compositing_test.ccthird_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.cc
Patch
From a80f58655f77af72c9127fdc16fa7cf0594b5fc9 Mon Sep 17 00:00:00 2001
From: Philip Rogers <pdr@chromium.org>
Date: Fri, 11 Sep 2026 15:51:27 -0700
Subject: [PATCH] Prevent merging sticky layers across non-2d-translation transforms
Sticky layers cannot be merged across non-2D-translation transforms
because the compositor sticky offset applied to the merged layer would
not correctly account for intermediate transforms. This patch adds a
check to ensure both nodes share the same RootOf2dTranslation.
Fixed: 514078734
Change-Id: I1eac45e1539c975b74ff255c7a0ad1007e53ec8e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8393871
Reviewed-by: Xianzhu Wang <wangxianzhu@chromium.org>
Commit-Queue: Philip Rogers <pdr@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1696682}
---
diff --git a/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.cc b/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.cc
index 4914a45..f5439ac5 100644
--- a/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.cc
@@ -184,7 +184,8 @@
UnaliasedParent()->NearestDirectlyCompositedAncestor() !=
other.UnaliasedParent()->NearestDirectlyCompositedAncestor() ||
&NearestScrollTranslationNode() !=
- &other.NearestScrollTranslationNode()) {
+ &other.NearestScrollTranslationNode() ||
+ RootOf2dTranslation() != other.RootOf2dTranslation()) {
return cc::StickyPositionConstraint::CanMergeResult::kCannotMerge;
}
diff --git a/third_party/blink/renderer/core/paint/compositing/compositing_test.cc b/third_party/blink/renderer/core/paint/compositing/compositing_test.cc
index 5254842..95b44618 100644
--- a/third_party/blink/renderer/core/paint/compositing/compositing_test.cc
+++ b/third_party/blink/renderer/core/paint/compositing/compositing_test.cc
@@ -1257,6 +1257,53 @@
EXPECT_TRUE(CcLayerByDOMElementId("d5"));
}
+TEST_P(CompositingTest, DontMergeStickyLayersAcrossNon2dTranslationTransform) {
+ InitializeWithHTML(*WebView()->MainFrameImpl()->GetFrame(), R"HTML(
+ <style>
+ .scroller {
+ width: 200px;
+ height: 200px;
+ overflow: scroll;
+ position: relative;
+ }
+ .wrapper {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100px;
+ height: 400px;
+ visibility: hidden;
+ }
+ .sticky {
+ position: sticky;
+ top: 0;
+ width: 100px;
+ height: 100px;
+ visibility: visible;
+ }
+ </style>
+ <div class="scroller">
+ <div style="height: 400px"></div>
+ <div class="wrapper">
+ <div id="a" class="sticky"></div>
+ </div>
+ <div class="wrapper" style="transform: rotate(1deg)">
+ <div id="b" class="sticky"></div>
+ </div>
+ </div>
+ )HTML");
+
+ // The two sticky elements have matching sticky constraints, but `b` is under
+ // a non-2d-translation transform. They should not be merged because the
+ // compositor sticky offset applied to a merged layer would not correctly
+ // account for the transform above `b`.
+ cc::Layer* a = CcLayerByDOMElementId("a");
+ ASSERT_TRUE(a);
+ cc::Layer* b = CcLayerByDOMElementId("b");
+ ASSERT_TRUE(b);
+ EXPECT_NE(a->transform_tree_index(), b->transform_tree_index());
+}
+
TEST_P(CompositingTest, DontCompositeStickyAlongNonScrollableAxis) {
InitializeWithHTML(*WebView()->MainFrameImpl()->GetFrame(), R"HTML(
<style>
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/third_party/blink/renderer/core/paint/compositing/compositing_test.cc b/third_party/blink/renderer/core/paint/compositing/compositing_test.cc
index 5254842..95b44618 100644
--- a/third_party/blink/renderer/core/paint/compositing/compositing_test.cc
+++ b/third_party/blink/renderer/core/paint/compositing/compositing_test.cc
@@ -1257,6 +1257,53 @@
EXPECT_TRUE(CcLayerByDOMElementId("d5"));
}
+TEST_P(CompositingTest, DontMergeStickyLayersAcrossNon2dTranslationTransform) {
+ InitializeWithHTML(*WebView()->MainFrameImpl()->GetFrame(), R"HTML(
+ <style>
+ .scroller {
+ width: 200px;
+ height: 200px;
+ overflow: scroll;
+ position: relative;
+ }
+ .wrapper {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100px;
+ height: 400px;
+ visibility: hidden;
+ }
+ .sticky {
+ position: sticky;
+ top: 0;
+ width: 100px;
+ height: 100px;
+ visibility: visible;
+ }
+ </style>
+ <div class="scroller">
+ <div style="height: 400px"></div>
+ <div class="wrapper">
+ <div id="a" class="sticky"></div>
+ </div>
+ <div class="wrapper" style="transform: rotate(1deg)">
+ <div id="b" class="sticky"></div>
+ </div>
+ </div>
+ )HTML");
+
+ // The two sticky elements have matching sticky constraints, but `b` is under
+ // a non-2d-translation transform. They should not be merged because the
+ // compositor sticky offset applied to a merged layer would not correctly
+ // account for the transform above `b`.
+ cc::Layer* a = CcLayerByDOMElementId("a");
+ ASSERT_TRUE(a);
+ cc::Layer* b = CcLayerByDOMElementId("b");
+ ASSERT_TRUE(b);
+ EXPECT_NE(a->transform_tree_index(), b->transform_tree_index());
+}
+
TEST_P(CompositingTest, DontCompositeStickyAlongNonScrollableAxis) {
InitializeWithHTML(*WebView()->MainFrameImpl()->GetFrame(), R"HTML(
<style>
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