Medium CVSS 6.5 webkit UAF 🔧 Commit mapped

Overview

Medium
Severity
6.5
CVSS
No
Exploited ITW
Fixed
Fix Status
DescriptionProcessing maliciously crafted web content may lead to an unexpected Safari crash
ComponentWebCore HTML
Bug ClassUAF
Tracker313175
Fix commit040ef6e21ffa (WebKit/WebKit) +122/-10
CWECWE-416 (Use-after-free)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H
CISA KEVNot listed
CreditedJosef Korbel, Gia Bui (@yabeow) from Calif.io
Disclosed2026-06-29

Background

Canvas rendering context (m_context)
A canvas lazily creates and caches one rendering context (2d/webgl/bitmap/webgpu) in m_context on the first getContext call.
WebIDL dictionary conversion
Converting a JS argument to a WebIDL dictionary reads its properties, which can invoke author-defined getters and thus run script mid-call.
Re-entrancy
Script run during argument conversion can call back into getContext on the same canvas before the outer call finishes.
ASSERT_WITH_SECURITY_IMPLICATION
A stronger assertion marking an invariant whose violation is security-relevant, enabled in security test/debug builds.

Root Cause Analysis

HTMLCanvasElement::getContext (and OffscreenCanvas::getContext) convert the JS arguments into a WebIDL settings/attributes dictionary before creating the rendering context, and that dictionary conversion can run author script through getters/valueOf on the supplied object. The pre-patch code checked m_context once at entry, performed the conversion, then unconditionally created a new context and assigned m_context = createContext...(). If script executed during the conversion re-entrantly called getContext() and set m_context, the outer call would overwrite m_context with a second, freshly-created context — dropping the context the inner call had already created (and possibly already returned to script), leaving dangling references to a now-freed context object: a use-after-free / memory corruption.

The fix factors the existing-context path into a getExistingContext lambda and, after every dictionary conversion that can run script, re-checks if (m_context) return getExistingContext(); and guards creation with if (!m_context), so a context established by re-entrancy is honored rather than clobbered. For OffscreenCanvas, the conversion can additionally detach the canvas, so a shouldThrowForDetachedCanvas() re-check is added after each conversion and creation is likewise guarded by if (!m_context). The ASSERT(!m_context) invariants in createContext2d/createContextWebGL/createContextBitmapRenderer/createContextWebGPU were strengthened to ASSERT_WITH_SECURITY_IMPLICATION(!m_context) to catch a double-create.

The restored invariant is that m_context is re-validated after any step that may run script, and a canvas creates exactly one rendering context exactly once.

Key insight
getContext cached m_context’s nullness across a WebIDL dictionary conversion that can run script and re-entrantly set it, then created a second context and clobbered the first — re-checking m_context (and detach state) after every script-running step closes it.

Attack Path

  1. Call getContext with a script hook Call canvas.getContext(type, settings) where settings is an object whose getter (or a coercible member) runs attacker script during WebIDL dictionary conversion.
  2. Re-enter getContext Inside the getter, call canvas.getContext(type, …) again; the inner call creates and assigns m_context and returns a context object to script.
  3. Clobber the context The outer call resumes after conversion and, pre-patch, creates a second context and overwrites m_context, dropping the inner context.
  4. Free and dangle The replaced context is destroyed (helped by gc()) while script still references it — use-after-free / memory corruption in WebContent.

Impact Assessment

A script-controlled re-entrancy UAF / double-create of a canvas rendering context. The attacker fully controls the re-entrant call and its timing via the settings-object getter, but the object and window are constrained, so the realistic outcome is a controlled crash with potential for memory corruption after heap grooming. Confined to the WebContent process (GPU-backed contexts are still brokered from WebContent). Rated medium (CVSS 6.5).

Changed Functions

FunctionChangeNotes
HTMLCanvasElement::getContext
Source/WebCore/html/HTMLCanvasElement.cpp
modified Extracts a getExistingContext() lambda and, after each 2d/bitmap/webgl dictionary conversion, re-checks `if (m_context) return getExistingContext();` so a re-entrantly-created context is not overwritten.
HTMLCanvasElement::createContext2d / createContextWebGL / createContextBitmapRenderer / createContextWebGPU
Source/WebCore/html/HTMLCanvasElement.cpp
modified ASSERT(!m_context) upgraded to ASSERT_WITH_SECURITY_IMPLICATION(!m_context) to catch a double-create as a security-relevant invariant.
OffscreenCanvas::getContext
Source/WebCore/html/OffscreenCanvas.cpp
modified Adds shouldThrowForDetachedCanvas() re-checks after conversions (script may detach the canvas) and guards each context creation with if (!m_context) against re-entrant creation.

Files Changed

  • LayoutTests/fast/canvas/canvas-getContext-reentrant-expected.txt
  • LayoutTests/fast/canvas/canvas-getContext-reentrant.html
  • Source/WebCore/html/HTMLCanvasElement.cpp
  • Source/WebCore/html/OffscreenCanvas.cpp

Audit Directions

  • Other getContext-style factories
    grep for a member read, then releaseReturnValue()/dictionary conversion, then an unconditional member assignment without re-checking the member — the exact clobber shape fixed here.
  • Assumptions across argument coercion
    Audit DOM methods that assume a member stays null/unchanged across convert<IDLDictionary>/argument coercion, since coercion can run script and re-enter.
  • OffscreenCanvas detach/transfer
    Check transferToImageBitmap and other OffscreenCanvas paths for state (m_detached/m_context) that can change via script during conversion.

Original Bug Report

The reporter's bug is still restricted on the tracker.