Chrome · Compositing
CVE-2026-85051
Type Confusion in Compositing
Overview
High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ToScrollOffsetAnimationCurvecc/animation/scroll_offset_animation_curve.cc |
modified | |
TEST_Fcc/animation/scroll_timeline_unittest.cc |
modified |
Files Changed
cc/animation/animation_host.cccc/animation/filter_animation_curve.cccc/animation/scroll_offset_animation_curve.cccc/animation/scroll_timeline.hcc/animation/scroll_timeline_unittest.cccc/animation/timeline_trigger.cccc/animation/timeline_trigger.hcc/animation/worklet_animation.h
Patch
From deb41ddc5770bd74cc7d0e4d6a7d7b12e2c5ecec Mon Sep 17 00:00:00 2001
From: Robert Flack <flackr@chromium.org>
Date: Fri, 28 Aug 2026 13:20:18 -0700
Subject: [PATCH] Promote DCHECK to CHECK in cc animation downcasts
Promotes `DCHECK` downcast assertions to `CHECK` across `cc/animation/`
downcasting helpers (`ToScrollTimeline`, `ToWorkletAnimation`,
`ToScrollOffsetAnimationCurve`, and `ToFilterAnimationCurve`).
Introduces `ToTimelineTrigger` helper functions with `CHECK` verification
and uses them in `AnimationHost::UpdateTriggers`.
This ensures downcasts between base and derived animation, timeline, trigger,
and curve instances are always validated.
Bug: 553449113
Change-Id: I06849cde7d144966b89ec2899eebef5f5a66fee0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8308916
Reviewed-by: David A <awogbemila@chromium.org>
Commit-Queue: Robert Flack <flackr@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1688263}
---
diff --git a/cc/animation/animation_host.cc b/cc/animation/animation_host.cc
index 0a5e0d6..8514063 100644
--- a/cc/animation/animation_host.cc
+++ b/cc/animation/animation_host.cc
@@ -1091,9 +1091,7 @@
for (const auto& kv : id_to_trigger_map_.Read(*this)) {
AnimationTrigger* trigger = kv.second.get();
// NOTE(crbug.com/451238244): Only timeline triggers are supported for now.
- DCHECK(trigger->IsTimelineTrigger());
- static_cast<TimelineTrigger*>(trigger)->Update(scroll_tree, events,
- monotonic_time);
+ ToTimelineTrigger(trigger)->Update(scroll_tree, events, monotonic_time);
}
}
diff --git a/cc/animation/filter_animation_curve.cc b/cc/animation/filter_animation_curve.cc
index 8a01901f..003a1f43a 100644
--- a/cc/animation/filter_animation_curve.cc
+++ b/cc/animation/filter_animation_curve.cc
@@ -4,6 +4,7 @@
#include "cc/animation/filter_animation_curve.h"
+#include "base/check_op.h"
#include "base/memory/ptr_util.h"
#include "ui/gfx/animation/keyframe/keyframed_animation_curve-inl.h"
@@ -30,13 +31,13 @@
const FilterAnimationCurve* FilterAnimationCurve::ToFilterAnimationCurve(
const gfx::AnimationCurve* c) {
- DCHECK_EQ(gfx::AnimationCurve::FILTER, c->Type());
+ CHECK_EQ(gfx::AnimationCurve::FILTER, c->Type());
return static_cast<const FilterAnimationCurve*>(c);
}
FilterAnimationCurve* FilterAnimationCurve::ToFilterAnimationCurve(
gfx::AnimationCurve* c) {
- DCHECK_EQ(AnimationCurve::FILTER, c->Type());
+ CHECK_EQ(AnimationCurve::FILTER, c->Type());
return static_cast<FilterAnimationCurve*>(c);
}
diff --git a/cc/animation/scroll_offset_animation_curve.cc b/cc/animation/scroll_offset_animation_curve.cc
index f2d5475..3e57ae3 100644
--- a/cc/animation/scroll_offset_animation_curve.cc
+++ b/cc/animation/scroll_offset_animation_curve.cc
@@ -412,13 +412,13 @@
const ScrollOffsetAnimationCurve*
ScrollOffsetAnimationCurve::ToScrollOffsetAnimationCurve(
const AnimationCurve* c) {
- DCHECK_EQ(ScrollOffsetAnimationCurve::SCROLL_OFFSET, c->Type());
+ CHECK_EQ(ScrollOffsetAnimationCurve::SCROLL_OFFSET, c->Type());
return static_cast<const ScrollOffsetAnimationCurve*>(c);
}
ScrollOffsetAnimationCurve*
ScrollOffsetAnimationCurve::ToScrollOffsetAnimationCurve(AnimationCurve* c) {
- DCHECK_EQ(ScrollOffsetAnimationCurve::SCROLL_OFFSET, c->Type());
+ CHECK_EQ(ScrollOffsetAnimationCurve::SCROLL_OFFSET, c->Type());
return static_cast<ScrollOffsetAnimationCurve*>(c);
}
diff --git a/cc/animation/scroll_timeline.h b/cc/animation/scroll_timeline.h
index 8d350fc..d6218ad 100644
--- a/cc/animation/scroll_timeline.h
+++ b/cc/animation/scroll_timeline.h
@@ -8,6 +8,7 @@
#include <optional>
#include <vector>
+#include "base/check.h"
#include "base/time/time.h"
#include "cc/animation/animation_export.h"
#include "cc/animation/animation_timeline.h"
@@ -199,13 +200,13 @@
};
inline ScrollTimeline* ToScrollTimeline(AnimationTimeline* timeline) {
- DCHECK(timeline->IsScrollTimeline());
+ CHECK(timeline->IsScrollTimeline());
return static_cast<ScrollTimeline*>(timeline);
}
inline const ScrollTimeline* ToScrollTimeline(
const AnimationTimeline* timeline) {
- DCHECK(timeline->IsScrollTimeline());
+ CHECK(timeline->IsScrollTimeline());
return static_cast<const ScrollTimeline*>(timeline);
}
diff --git a/cc/animation/scroll_timeline_unittest.cc b/cc/animation/scroll_timeline_unittest.cc
index 9824011f..d06aad59 100644
--- a/cc/animation/scroll_timeline_unittest.cc
+++ b/cc/animation/scroll_timeline_unittest.cc
@@ -437,4 +437,22 @@
active_timeline->IsActive(scroll_tree(), true /*is_active_tree*/));
}
+TEST_F(ScrollTimelineTest, ToScrollTimeline) {
+ ScrollTimeline::ScrollOffsets scroll_offsets(0, 100);
+ scoped_refptr<ScrollTimeline> scroll_timeline = ScrollTimeline::Create(
+ scroller_id(), ScrollTimeline::ScrollDown, scroll_offsets);
+ scoped_refptr<AnimationTimeline> plain_timeline =
+ AnimationTimeline::Create(1);
+
+ EXPECT_EQ(ToScrollTimeline(scroll_timeline.get()), scroll_timeline.get());
+ const AnimationTimeline* const_scroll_timeline = scroll_timeline.get();
+ EXPECT_EQ(ToScrollTimeline(const_scroll_timeline), scroll_timeline.get());
+
+#if GTEST_HAS_DEATH_TEST
+ EXPECT_DEATH_IF_SUPPORTED(ToScrollTimeline(plain_timeline.get()), "");
+ const AnimationTimeline* const_plain_timeline = plain_timeline.get();
+ EXPECT_DEATH_IF_SUPPORTED(ToScrollTimeline(const_plain_timeline), "");
+#endif
+}
+
} // namespace cc
diff --git a/cc/animation/timeline_trigger.cc b/cc/animation/timeline_trigger.cc
index c1c915ba..bc770f2 100644
--- a/cc/animation/timeline_trigger.cc
+++ b/cc/animation/timeline_trigger.cc
@@ -68,8 +68,7 @@
AnimationEvents* events,
base::TimeTicks monotonic_time) {
ScrollTimeline* scroll_timeline =
- reinterpret_cast<ScrollTimeline*>(timeline_.Read(*this).get());
- DCHECK(scroll_timeline);
+ ToScrollTimeline(timeline_.Read(*this).get());
// Triggers only function based on the active tree.
if (!scroll_timeline->IsActive(scroll_tree, /*is_active_tree=*/true)) {
diff --git a/cc/animation/timeline_trigger.h b/cc/animation/timeline_trigger.h
index 13a0203..5674704 100644
--- a/cc/animation/timeline_trigger.h
+++ b/cc/animation/timeline_trigger.h
@@ -5,6 +5,7 @@
#ifndef CC_ANIMATION_TIMELINE_TRIGGER_H_
#define CC_ANIMATION_TIMELINE_TRIGGER_H_
+#include "base/check.h"
#include "cc/animation/animation_trigger.h"
namespace cc {
@@ -71,6 +72,17 @@
State state_ = State::kIdle;
};
+inline TimelineTrigger* ToTimelineTrigger(AnimationTrigger* trigger) {
+ CHECK(trigger->IsTimelineTrigger());
+ return static_cast<TimelineTrigger*>(trigger);
+}
+
+inline const TimelineTrigger* ToTimelineTrigger(
+ const AnimationTrigger* trigger) {
+ CHECK(trigger->IsTimelineTrigger());
+ return static_cast<const TimelineTrigger*>(trigger);
+}
+
} // namespace cc
#endif // CC_ANIMATION_TIMELINE_TRIGGER_H_
diff --git a/cc/animation/worklet_animation.h b/cc/animation/worklet_animation.h
index e4323e7..1b4c5e4 100644
--- a/cc/animation/worklet_animation.h
+++ b/cc/animation/worklet_animation.h
@@ -9,6 +9,7 @@
#include <optional>
#include <string>
+#include "base/check.h"
#include "base/time/time.h"
#include "cc/animation/animation.h"
#include "cc/animation/animation_export.h"
@@ -154,10 +155,15 @@
};
inline WorkletAnimation* ToWorkletAnimation(Animation* animation) {
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/cc/animation/scroll_timeline_unittest.cc b/cc/animation/scroll_timeline_unittest.cc
index 9824011f..d06aad59 100644
--- a/cc/animation/scroll_timeline_unittest.cc
+++ b/cc/animation/scroll_timeline_unittest.cc
@@ -437,4 +437,22 @@
active_timeline->IsActive(scroll_tree(), true /*is_active_tree*/));
}
+TEST_F(ScrollTimelineTest, ToScrollTimeline) {
+ ScrollTimeline::ScrollOffsets scroll_offsets(0, 100);
+ scoped_refptr<ScrollTimeline> scroll_timeline = ScrollTimeline::Create(
+ scroller_id(), ScrollTimeline::ScrollDown, scroll_offsets);
+ scoped_refptr<AnimationTimeline> plain_timeline =
+ AnimationTimeline::Create(1);
+
+ EXPECT_EQ(ToScrollTimeline(scroll_timeline.get()), scroll_timeline.get());
+ const AnimationTimeline* const_scroll_timeline = scroll_timeline.get();
+ EXPECT_EQ(ToScrollTimeline(const_scroll_timeline), scroll_timeline.get());
+
+#if GTEST_HAS_DEATH_TEST
+ EXPECT_DEATH_IF_SUPPORTED(ToScrollTimeline(plain_timeline.get()), "");
+ const AnimationTimeline* const_plain_timeline = plain_timeline.get();
+ EXPECT_DEATH_IF_SUPPORTED(ToScrollTimeline(const_plain_timeline), "");
+#endif
+}
+
} // namespace cc
diff --git a/cc/animation/worklet_animation_unittest.cc b/cc/animation/worklet_animation_unittest.cc
index 61ec54e5..bdd8c16 100644
--- a/cc/animation/worklet_animation_unittest.cc
+++ b/cc/animation/worklet_animation_unittest.cc
@@ -548,6 +548,26 @@
EXPECT_EQ(input->updated_animations.size(), 1u);
}
+TEST_F(WorkletAnimationTest, ToWorkletAnimation) {
+ scoped_refptr<WorkletAnimation> worklet_animation =
+ WorkletAnimation::Create(worklet_animation_id_, "test_name", 1.0,
+ /* options */ nullptr,
+ /* effect_timings */ nullptr);
+ scoped_refptr<Animation> plain_animation = Animation::Create(1);
+
+ EXPECT_EQ(ToWorkletAnimation(worklet_animation.get()),
+ worklet_animation.get());
+ const Animation* const_worklet_animation = worklet_animation.get();
+ EXPECT_EQ(ToWorkletAnimation(const_worklet_animation),
+ worklet_animation.get());
+
+#if GTEST_HAS_DEATH_TEST
+ EXPECT_DEATH_IF_SUPPORTED(ToWorkletAnimation(plain_animation.get()), "");
+ const Animation* const_plain_animation = plain_animation.get();
+ EXPECT_DEATH_IF_SUPPORTED(ToWorkletAnimation(const_plain_animation), "");
+#endif
+}
+
} // namespace
} // namespace cc
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