CVE-2025-43457
Overview
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.
Attack Path
- 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.
- 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.
- 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.
- Use-after-free The array is used with a freed/unmaterialized butterfly, corrupting memory in the WebContent process.
Impact Assessment
Changed Functions
| Function | Change | Notes |
|---|---|---|
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 / DFGAbstractInterpreterInlinesSource/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.jsJSTests/stress/array-allocation-elimination-conditional-usage.jsJSTests/stress/array-allocation-elimination-cross-function.jsSource/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.hSource/JavaScriptCore/dfg/DFGBackwardsPropagationPhase.cppSource/JavaScriptCore/dfg/DFGClobberize.hSource/JavaScriptCore/dfg/DFGCloneHelper.hSource/JavaScriptCore/dfg/DFGConstantFoldingPhase.cppSource/JavaScriptCore/dfg/DFGDoesGC.cppSource/JavaScriptCore/dfg/DFGFixupPhase.cppSource/JavaScriptCore/dfg/DFGLoopUnrollingPhase.cppSource/JavaScriptCore/dfg/DFGMayExit.cppSource/JavaScriptCore/dfg/DFGNode.cppSource/JavaScriptCore/dfg/DFGNode.hSource/JavaScriptCore/dfg/DFGNodeType.hSource/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cppSource/JavaScriptCore/dfg/DFGObjectMaterializationData.hSource/JavaScriptCore/dfg/DFGOperations.cppSource/JavaScriptCore/dfg/DFGOperations.hSource/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cppSource/JavaScriptCore/dfg/DFGPromotedHeapLocation.hSource/JavaScriptCore/dfg/DFGSafeToExecute.hSource/JavaScriptCore/dfg/DFGSpeculativeJIT.cppSource/JavaScriptCore/dfg/DFGSpeculativeJIT.hSource/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cppSource/JavaScriptCore/dfg/DFGSpeculativeJIT64.cppSource/JavaScriptCore/dfg/DFGStoreBarrierInsertionPhase.cppSource/JavaScriptCore/dfg/DFGUseKind.hSource/JavaScriptCore/dfg/DFGValidate.cppSource/JavaScriptCore/ftl/FTLCapabilities.cppSource/JavaScriptCore/ftl/FTLExitTimeObjectMaterialization.cppSource/JavaScriptCore/ftl/FTLLowerDFGToB3.cppSource/JavaScriptCore/ftl/FTLOperations.cppSource/JavaScriptCore/runtime/IndexingType.cppSource/JavaScriptCore/runtime/IndexingType.h
Audit Directions
- Other fused allocationsLook 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 coverageGrep 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 orderAudit MaterializeNewButterflyWithSize/PhantomNewArrayWithButterfly handling at OSR exit to confirm the butterfly is materialized before the array that references it.