← All issues

[13] WebAnimation null-deref via stale microtask backpointer

Severity: Medium | Component: WebCore Web Animations / KeyframeEffect | c7e7785

Rated Medium because the observable effect is a deterministic null deref inside the renderer (matching the ASan SEGV stack), and projection to a UAF read primitive depends on whether the backpointer observed freed memory rather than only a null transition — the analyst flags this as the load-bearing unverified architectural question.

Source/WebCore/animation/KeyframeEffect.cpp

void KeyframeEffect::applyPendingAcceleratedActions()
{
if (m_pendingAcceleratedActions.isEmpty())
return;
 
+ if (!animation())
+ return;
+
CheckedPtr renderer = this->renderer();

An early-return guard in KeyframeEffect::applyPendingAcceleratedActions() bails when animation() returns null, immediately after the existing empty-pending-actions check. A regression test schedules a pending accelerated action via wasRemovedFromStack() (mutating styles and reassigning animation.effect = new KeyframeEffect(null, ...)) and then nulls/replaces the effect's animation linkage before the asynchronous microtask fires.

Failure to extend the owned animation's lifetime across an asynchronous microtask boundary, leaving a backpointer that can be nulled (or freed) before the deferred work runs.

KeyframeEffect represents a CSS/JS animation effect attached to a WebAnimation; each effect carries a backpointer to its owning WebAnimation (exposed via KeyframeEffect::animation()). applyPendingAcceleratedActions() is the deferred drain function that hands queued play/pause/seek operations to the compositor through the renderer. WebKit's event loop allows native code to enqueue lambdas; lambdas capturing only weak references do not extend the captured object's lifetime. Assigning animation.effect = new KeyframeEffect(null, ...) detaches the previous effect from its animation.

applyPendingAcceleratedActions() was reachable via an asynchronous microtask scheduled from KeyframeEffect::wasRemovedFromStack(). wasRemovedFromStack itself holds a Ref/RefPtr to the animation across its synchronous body, but the lambda enqueued for the microtask captures only the KeyframeEffect (or a weak-reference path) — it does not extend the animation's lifetime.

🔒

The lifetime story behind this microtask-deferred crash, and whether the immediate null dereference can plausibly escalate, is examined in detail.

Subscribe to read more

🔒

Several reusable audit patterns identified across Web Animations, with concrete entry points for variant discovery in the deferred-work and detach paths.

Subscribe to read more