High chrome Type Confusion 🔧 Commit mapped

Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactType confusion in Compositing
DescriptionType confusion in Compositing
ComponentCompositing
Bug ClassType Confusion
Tracker553449113
Fix commitdeb41ddc5770 (chromium/src) +67/-12
CISA KEVNot listed
CreditedGoogle
Disclosed2026-09-03

Changed Functions

FunctionChangeNotes
ToScrollOffsetAnimationCurve
cc/animation/scroll_offset_animation_curve.cc
modified
TEST_F
cc/animation/scroll_timeline_unittest.cc
modified

Files Changed

  • cc/animation/animation_host.cc
  • cc/animation/filter_animation_curve.cc
  • cc/animation/scroll_offset_animation_curve.cc
  • cc/animation/scroll_timeline.h
  • cc/animation/scroll_timeline_unittest.cc
  • cc/animation/timeline_trigger.cc
  • cc/animation/timeline_trigger.h
  • cc/animation/worklet_animation.h
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.