← WebKit — 2026-W38

e0558372aafca3e5ed5bcc50d0950b60e22e281a  Use-after-free in CanvasRenderingContext2DBase::beginLayer via realizeSavesLoop early exit

severity medium class LogicError confidence 0.55 signal score 9.5 Source/WebCore/html
Primitive: Canvas2D beginLayer() proceeds to build a filter/layer even when save states were not fully realized, desyncing the save/layer stack.
Triage note: In CanvasRenderingContext2DBase::beginLayer() the fix adds `if (m_unrealizedSaveCount) return;` right after save()/realizeSaves(), so layer setup is skipped when realizeSaves() left pending save states (e.g. a depth cap). Without it the layer stack and the save stack get out of balance, which endLayer()/restore() would later unwind incorrectly — a real state-corruption guard, but the diff alone does not expose a concrete memory-safety primitive.
Structural signals: null_guard (+1.2), msg_strong (+3.0), msg_medium (+2.0), area:Source/WebCore/html (+2.0), surgical (+1.3)

Commit message

Use-after-free in CanvasRenderingContext2DBase::beginLayer via realizeSavesLoop early exit
https://bugs.webkit.org/show_bug.cgi?id=318378
<rdar://175228541>

Reviewed by Said Abou-Hallawa.

beginLayer() relies on realizeSaves() pushing a fresh State onto
m_stateStack so that the new layer's CanvasLayerContextSwitcher is stored
in its own frame. If the state stack is already at MaxSaveCount,
realizeSavesLoop() returns without growing the stack and
m_unrealizedSaveCount remains nonzero. Creating a layer in that case
would overwrite the targetSwitcher of the current top State, dropping the
previous layer while the new layer still holds a raw pointer to the
previous layer's GraphicsContext, leading to a use-after-free.

Fix this by returning early from beginLayer() when realizeSaves() was
unable to push a new State (m_unrealizedSaveCount is still nonzero).

Test: fast/canvas/canvas-layer-after-max-save-count.html

* LayoutTests/fast/canvas/canvas-layer-after-max-save-count-expected.txt: Added.
* LayoutTests/fast/canvas/canvas-layer-after-max-save-count.html: Added.
* Source/WebCore/html/canvas/CanvasRenderingContext2DBase.cpp:
(WebCore::CanvasRenderingContext2DBase::beginLayer):

Originally-landed-as: [email protected] (c5a2b165a95e). rdar://187507031
Canonical link: https://commits.webkit.org/321428@main

Before / after

Loading diff…