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
ComponentJSC DFG
Bug ClassUAF
Tracker298606
Fix commitf014a3289076 (WebKit/WebKit) +1083/-450
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
CreditedGary Kwong, Hossein Lotfi (@hosselot) of Trend Micro Zero Day Initiative
Disclosed2025-11-03

Background

DFG allocation sinking / elimination
A DFG optimization that defers or removes object allocations, rematerializing them only on paths that need them (e.g. at OSR exit).
Butterfly
The out-of-line storage for a JSObject/JSArray’s indexed and named properties; a separate heap allocation from the cell.
Phantom / Materialize nodes
DFG nodes representing a sunk allocation (Phantom) and its later rebuild (Materialize) during OSR exit.
DFGDoesGC / DFGMayExit / Clobberize
Analyses that model whether a node can GC, can exit, and what state it reads/writes; they must know every allocation to be correct.

Root Cause Analysis

In the DFG JIT, a JS array allocation of known constant length was represented by a single fused node type (NewArrayWithConstantSize, with Phantom/Materialize variants) that allocated both the JSArray cell and its backing butterfly. That fusion hid the butterfly allocation from the DFG’s allocation-sinking and supporting analyses: the butterfly was not a first-class allocation the way the object was, so allocation elimination / object sinking, GC modeling (DFGDoesGC), may-exit analysis (DFGMayExit), and effect modeling (DFGClobberize) reasoned about the array without correctly accounting for a separately-lived butterfly. Under array-allocation-elimination (the added tests cover closure-capture, conditional-usage and cross-function cases), that mismatch let the optimizer sink/eliminate or materialize the allocation in a way where the array could reference a butterfly that had not been (re)materialized or had been treated as not needing GC — a use-after-free / stale-butterfly access when the array was materialized at OSR exit or used after sinking.

The fix decomposes the operation: a new NewButterflyWithSize node explicitly allocates the butterfly, and NewArrayWithButterfly (with PhantomNewArrayWithButterfly / PhantomNewButterflyWithSize / MaterializeNewButterflyWithSize variants) builds the array from that butterfly. ConstantFoldingPhase now inserts a NewButterflyWithSize and converts the array node to NewArrayWithButterfly (running CFA carefully so the default folder doesn’t re-handle it), and every analysis (DoesGC, MayExit, FixupPhase, LoopUnrollingPhase, BackwardsPropagation, Clobberize, AbstractInterpreter, CloneHelper, Node conversion) is updated to know both node kinds.

The restored invariant is that the butterfly is a first-class, separately-tracked allocation, so allocation sinking, materialization and GC modeling handle its lifetime correctly and never leave the array pointing at an unmaterialized/freed butterfly.

Key insight
Representing a constant-size array and its butterfly as one fused DFG node hid the butterfly from allocation-sinking, GC and materialization analyses, so elimination could leave the array referencing an unmaterialized/freed butterfly; splitting it into NewButterflyWithSize + NewArrayWithButterfly makes the butterfly a first-class, correctly-tracked allocation.

Attack Path

  1. Write array-allocating hot code Run JS that allocates constant-size arrays in patterns amenable to allocation elimination (captured in closures, conditionally used, or passed across inlined functions), so the DFG creates NewArrayWithConstantSize nodes.
  2. Trigger allocation sinking Get the DFG to sink/eliminate the fused array allocation, which mis-models the butterfly as not separately allocated or not GC-relevant.
  3. Force materialization / GC Cause an OSR exit that materializes the sunk array, or a GC, at a point where the butterfly is stale or unmaterialized.
  4. Use-after-free The array is used with a freed/unmaterialized butterfly, corrupting memory in the WebContent process.

Impact Assessment

A DFG allocation-elimination correctness bug where a fused array-plus-butterfly node hid the butterfly from GC/sinking/materialization analyses, producing a use-after-free / stale-butterfly access in the WebContent process. It is reachable from ordinary JS array code that the JIT optimizes, and while the observed effect is a crash, an array pointing at a controlled freed butterfly is a strong path toward type confusion and read/write. Confined to WebContent. Rated medium.

Changed Functions

FunctionChangeNotes
DFGConstantFoldingPhase (array-with-constant-size folding)
Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
modified Inserts an explicit NewButterflyWithSize node and converts the array to NewArrayWithButterfly (gated by !hasAnyArrayStorage, executing CFA and marking alreadyHandled) instead of the fused convertToNewArrayWithConstantSize.
Node::convertToNewArrayWithButterfly (was convertToNewArrayWithConstantSize)
Source/JavaScriptCore/dfg/DFGNode.cpp
modified New conversion that builds a NewArrayWithButterfly from a separate butterfly node, replacing the fused constant-size conversion.
DFGDoesGC / DFGMayExit / DFGFixupPhase / DFGLoopUnrollingPhase / DFGBackwardsPropagationPhase / DFGClobberize / DFGAbstractInterpreterInlines
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
modified All updated to handle NewButterflyWithSize / NewArrayWithButterfly (and their Phantom/Materialize forms) so GC, exit, effect and type analyses track the butterfly as a first-class allocation.

Files Changed

  • JSTests/stress/array-allocation-elimination-closure-capture.js
  • JSTests/stress/array-allocation-elimination-conditional-usage.js
  • JSTests/stress/array-allocation-elimination-cross-function.js
  • Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
  • Source/JavaScriptCore/dfg/DFGBackwardsPropagationPhase.cpp
  • Source/JavaScriptCore/dfg/DFGClobberize.h
  • Source/JavaScriptCore/dfg/DFGCloneHelper.h
  • Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
  • Source/JavaScriptCore/dfg/DFGDoesGC.cpp
  • Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
  • Source/JavaScriptCore/dfg/DFGLoopUnrollingPhase.cpp
  • Source/JavaScriptCore/dfg/DFGMayExit.cpp
  • Source/JavaScriptCore/dfg/DFGNode.cpp
  • Source/JavaScriptCore/dfg/DFGNode.h
  • Source/JavaScriptCore/dfg/DFGNodeType.h
  • Source/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cpp
  • Source/JavaScriptCore/dfg/DFGObjectMaterializationData.h
  • Source/JavaScriptCore/dfg/DFGOperations.cpp
  • Source/JavaScriptCore/dfg/DFGOperations.h
  • Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
  • Source/JavaScriptCore/dfg/DFGPromotedHeapLocation.h
  • Source/JavaScriptCore/dfg/DFGSafeToExecute.h
  • Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
  • Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
  • Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
  • Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
  • Source/JavaScriptCore/dfg/DFGStoreBarrierInsertionPhase.cpp
  • Source/JavaScriptCore/dfg/DFGUseKind.h
  • Source/JavaScriptCore/dfg/DFGValidate.cpp
  • Source/JavaScriptCore/ftl/FTLCapabilities.cpp
  • Source/JavaScriptCore/ftl/FTLExitTimeObjectMaterialization.cpp
  • Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
  • Source/JavaScriptCore/ftl/FTLOperations.cpp
  • Source/JavaScriptCore/runtime/IndexingType.cpp
  • Source/JavaScriptCore/runtime/IndexingType.h

Audit Directions

  • Other fused allocations
    Look for DFG nodes that allocate a cell plus out-of-line storage in one node (typed arrays, spreads, arguments) and verify sinking/materialization tracks each sub-allocation separately.
  • Node-kind coverage
    Grep DFG phase switch statements for the new NewButterflyWithSize/NewArrayWithButterfly cases to ensure every analysis (DoesGC, MayExit, Clobberize, AI, safe-to-execute) handles them, since a missed case reintroduces the mismatch.
  • Materialization order
    Audit MaterializeNewButterflyWithSize/PhantomNewArrayWithButterfly handling at OSR exit to confirm the butterfly is materialized before the array that references it.

Original Bug Report

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