CVE-2024-54505
Overview
Background
- Backwards propagation / usage flags
- A DFG phase that records how values are used (NodeBytecodeUsesAsNumber/AsOther/…) to guide later representation and conversion choices.
- SpecOther
- The speculated type for ‘other’ primitives (null/undefined); UsesAsOther is only meaningful when the operand can be SpecOther.
- ToString / CallStringConstructor
- DFG nodes for String() coercion whose operand-usage flags feed downstream optimization.
Root Cause Analysis
This fixes an incorrect backward-propagation of usage flags for ToString/CallStringConstructor in the DFG that could mislead value-representation decisions into a type confusion. DFGBackwardsPropagationPhase walks the graph backward to record how each value is used (NodeBytecodeUses* flags), which downstream phases consult to pick number/other representations and conversions. For ToString and CallStringConstructor, the pre-patch code unconditionally merged NodeBytecodeUsesAsNumber | NodeBytecodeUsesAsOther | NodeBytecodeNeedsNaNOrInfinity onto the operand (child1). Marking the operand as UsesAsOther even when its use kind cannot actually be an ‘other’ (null/undefined, SpecOther) type feeds an inconsistent usage signal into later optimization: the representation/conversion chosen for the operand based on these flags becomes inconsistent with how the value is really produced/consumed, and that mismatch is exploitable as a type confusion (a value handled as the wrong representation), which the advisory rates as memory corruption.
The fix gates the UsesAsOther flag on the operand actually being able to be SpecOther: if (typeFilterFor(node->child1().useKind()) & SpecOther) node->child1()->mergeFlags(NodeBytecodeUsesAsOther); and always merges UsesAsNumber | NeedsNaNOrInfinity.
The restored invariant is that the UsesAsOther usage flag is propagated only when the operand’s use kind admits an ‘other’ type, so downstream representation choices are consistent. INFERENCE: the precise downstream miscompilation that turns the wrong flag into a corrupting type confusion is not contained in this one-hunk diff; the patch establishes the incorrect unconditional UsesAsOther propagation as the root cause.
Attack Path
- Reach the DFG Run JS with a ToString/String() over a value whose DFG use kind cannot be an ‘other’ type.
- Mis-propagate UsesAsOther Backward propagation unconditionally marks the operand UsesAsOther, an inconsistent usage signal.
- Choose a wrong representation A later phase picks a value representation/conversion based on the incorrect flags.
- Type confusion The value is handled as the wrong representation, a memory-corruption primitive in the WebContent process (inferred escalation).
Impact Assessment
Changed Functions
| Function | Change | Notes |
|---|---|---|
BackwardsPropagationPhase (ToString / CallStringConstructor)Source/JavaScriptCore/dfg/DFGBackwardsPropagationPhase.cpp |
modified | Merges NodeBytecodeUsesAsOther only when typeFilterFor(child1 useKind) & SpecOther, instead of unconditionally, so the usage flag matches the operand's possible types. |
Files Changed
JSTests/stress/string-add-conversion-unused.jsSource/JavaScriptCore/dfg/DFGBackwardsPropagationPhase.cpp
Audit Directions
- Unconditional usage-flag mergesAudit DFGBackwardsPropagationPhase for other nodes that merge NodeBytecodeUsesAsOther/AsNumber unconditionally regardless of the operand’s use kind / possible types.
- Flag-driven representationTrace consumers of NodeBytecodeUsesAsOther to confirm they tolerate (or the producers correctly gate) the flag on SpecOther.