[5] DFG ValueRepReduction type confusion via MultiGetByOffset
Severity: High | Component: JSC DFG JIT — ValueRepReductionPhase | 460528e
Rated High because the observable effect is a type confusion where a non-numeric JSValue's bit pattern is reinterpreted as an unboxed double in JIT-compiled code, reachable via crafted polymorphic property accesses — the missing per-case validation for MultiGetByOffset constant cases is directly visible in the diff with confidence 0.92.
ValueRepReduction for doubles needs to eagerly convert constants in MultiGetByOffset cases to doubles. This patch escapes MultiGetByOffset constants that cannot be converted to doubles purely.
Source/JavaScriptCore/dfg/DFGValueRepReductionPhase.cpp
case MultiGetByOffset:
case GetByOffset: {
- if (node->child1().useKind() == RealNumberUse || node->child1().useKind() == NumberUse) {
- if (node->child1()->origin.exitOK)
- candidates.add(node->child1().node());
+ if (node->child1().useKind() != RealNumberUse && node->child1().useKind() != NumberUse)
break;
- }
+ if (!node->child1()->origin.exitOK)
+ break;
+ if (node->child1()->op() == MultiGetByOffset) {
+ bool isCandidate = true;
+ MultiGetByOffsetData& data = node->child1()->multiGetByOffsetData();
+ for (unsigned i = 0; i < data.cases.size(); ++i) {
+ GetByOffsetMethod& method = data.cases[i].method();
+ if (method.kind() == GetByOffsetMethod::Constant && !method.constant()->value().toNumberFromPrimitive()) {
+ isCandidate = false;
+ break;
+ }
+ }
+ if (!isCandidate)
+ break;
+ }
+ candidates.add(node->child1().node());
break;
}
Patch Details
The fix adds a validation loop for MultiGetByOffset nodes before marking them as double-conversion candidates. The new code iterates through all MultiGetByOffsetData cases and checks whether each Constant-kind case's value can be purely converted to double via toNumberFromPrimitive(). If any constant case fails this conversion, the node is rejected. Previously, MultiGetByOffset was treated identically to GetByOffset — added as a candidate with no per-case constant validation.
Missing per-case constant-convertibility validation before committing a polymorphic property load to unboxed double representation in DFG.
Background
The DFG ValueRepReductionPhase runs on SSA-form DFG IR and identifies nodes whose output can be converted from boxed JSValue to an unboxed machine representation (double, int52, int32). This avoids boxing/unboxing overhead on hot paths. MultiGetByOffset is a DFG node that represents a polymorphic inline cache — a property access profiled to hit multiple object structures. Each case in a MultiGetByOffset specifies how to load the property: either from an object slot at a fixed offset or as an inline constant (GetByOffsetMethod::Constant). toNumberFromPrimitive() attempts pure numeric conversion of a JSValue — it returns a valid double for numbers and numeric strings but fails for values like Symbols, BigInts, or non-coercible objects.
JS property access DFG IR ValueRepReduction
───────────────── ────────────────── ──────────────────────────
obj.x MultiGetByOffset Is every case safely
(profiled: 3 ├─ case S1: offset 16 convertible to double?
structures) ├─ case S2: Constant(42) 42 → 42.0 ✓
└─ case S3: Constant(sym) Symbol → ✗ REJECT
Analysis
Before the fix, convertValueRepsToUnboxed<DoubleRepUse>() treated MultiGetByOffset identically to GetByOffset when deciding whether a node could be converted from boxed JSValue to unboxed double. MultiGetByOffset dispatches across multiple structure cases, some of which may return inline constants. When the phase marks such a node as a double candidate, the JIT later eagerly converts the node's output to an unboxed double. If one of the constant cases holds a value that cannot be purely converted to double (e.g., a Symbol or BigInt), the eager double conversion would produce incorrect bit-pattern reinterpretation — the raw JSValue encoding would be treated as a double.
The bug arose because MultiGetByOffset was grouped with GetByOffset in a shared case statement despite having fundamentally different semantics — MultiGetByOffset carries inline constant cases that GetByOffset does not.
Aa Aaaaaaaa Aaaaa Aaaaa Aaaaaaaaaaa Aaaaaaaa Aaaaaaaa Aaaaa Aaa Aaaa Aaaaaaa a Aaaaaaa Aaaaa Aaa Aaa Aaaaaa Aa Aaaaaaaa Aa Aaaaaaaaaaa Aa Aaaaaaaaaaaaaaaa Aaa Aaaaaaa Aaaa Aaaaaaa a Aaaaaaaaaaa Aaaaaaaaa Aaaaa Aaa Aaaaaaaaaaa Aaaa Aaaaaaaaaaaaaaaaaa Aaaaaaaaaa Aaa Aaaaaaaaaaa Aaaa Aaaaa Aaaaa Aaa Aaa Aaaa Aa Aaaaaaaaa Aaa Aaaaaaaaaaa Aaaaaaaaaa Aaa Aaa Aaaaaaa Aa Aa Aaaaaaa Aaaaaaa Aaaa Aaaaaaaaaaaaa Aaaaaa Aaaaa Aaaa Aa Aaaa Aa Aaaaaaaaaa Aa Aaaa Aaaaaaa Aaaaaa Aa Aaaaaaaaa Aaaa Aaaaaaaa a Aaaaaaaaa Aa Aaaaaaaaa Aaaaaaaaa Aaaaaaaa Aaaaaaa Aaaa Aa a Aaaaaaa Aaa Aa Aaa Aaaaaaaa Aaaaa Aaaaa Aaaa Aaaa Aaaaaa Aaaaaaaaaaa a Aaaaaaaaaa Aaaaaaa Aaaaaaaaaa
Aaaa Aaaaaaaaaaaaa Aaaaaaa Aaaa Aaaaaa Aaaaaa Aaaaaaaaaaaa Aaaa Aa Aaa Aaaaaaaa Aaaaaaaa Aaa Aaa Aaaaaaaaaa Aaaa Aaaaaa Aaaaaaa Aaaa Aaa Aaaa Aaaaaa Aa a Aaaaaa Aaaaaaaaa Aaa Aa Aaaaaa Aaaaaaaaaaaaa Aa Aa Aaaaaaa Aaaaaaa Aaaaaa Aaa Aaaa a Aaaaaaaaaaaaaaaaaa Aaaa a Aaaaaaaaaaa Aaaaaaaa Aaaa Aaaaa Aaaaaaa Aaaa Aaaaaaaaaaa Aaaaaaaa Aaaaaaaaaaaa Aaaa Aa Aaaaa Aa Aaaaaaaaa Aaaaaaaaa Aaa Aaaaaaa Aa a Aaaaaaa Aa Aaaaaaaa Aaa Aaaaaa Aaaaaaa Aaaa Aaa Aaaaa Aaaaaaaaa Aaaaaaaaaaaa Aaaaa Aaaaaaaa Aaaa Aaaa Aaaaaaaaa Aa Aaaaa Aaaaaaaaaa Aaaaaaaaaa Aa Aaa Aaaaaaaaaa Aaaaaaaa
Aaaa Aaaaaaa Aa Aaaaaaaaaaa Aaaaa Aaaaa Aaaaaaa Aa Aaaaaa Aaaaa Aa Aaaaaaaaaaaa Aaaaaa Aa a Aaaaaaaaa Aaaaaa Aa Aaaa Aa Aaa Aaaaaaaaaa
Aaaaaaaaa Aaaa Aaaaaaa a Aaaaaaaaaaaa Aaa Aaaaa Aaaaaaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaaaaa Aaaaa Aaa Aaaaaaa Aaa Aaa Aaaaaaaa Aaaaaaaaaaa Aaaaaaaaaaaaaaaa Aaaaaa a Aaaaa a Aaa Aaaaaaaa Aaaa Aaa Aaa Aaaaaaaa Aaa Aaaa Aaaaaaa Aaaaaaaaaa Aa Aaaaaaaa Aaaaaaa Aa Aaa Aaaaa
🔒Explores how polymorphic property access interacts with value representation narrowing and what type confusion primitive this could yield
Subscribe to read more
Audit directions
a Aaaaa Aaaaaaaaaaaa Aaaaaa Aaaa Aaaaaa Aaaaa Aaaaaaaaaaaaaaa Aaaa Aaaaaaaa Aaa Aaaaaaaa Aaaaaaa Aa Aaaaaaaaaaa Aaaaaa Aaa Aaaa Aaa Aaaaaaaaaaaaaaa Aaaaaaa Aaaaa Aaaaa Aaa Aaa Aaa Aaaaaa Aaaa Aaaaaa Aaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaa Aa Aaaaaaaaaaaaaaa a Aaaaaaaaaaaa Aaaaa Aaaa Aaaa Aaaaaaaaaaaaaa Aaaaaaaaaa Aaaaa Aaaaaaa Aaaa Aaaaa Aaaaaaaaa Aaaaaaaa Aaaaa Aaaaaaaaaaaa Aa Aaaaaaa Aaaaaaa Aaaaa Aaaaa Aa Aaaaaaaa Aaa Aaaaaaaaaaaaaaaaaa Aaaaa Aa Aaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
a Aaaaaaaa Aaaaaaaaaaaaaa Aaaaaaaa Aa Aaaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaa Aaaaaaaa Aaaa Aaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaaaaaa Aaaaa a Aaaa Aaaaaa Aaaaaaaaaaaaa Aaaaa Aaa Aaa Aaaaaaaaaaa Aaaa Aaa Aa Aaaaaaa Aaa Aaa Aaaaaaaaaa Aaaaa Aaaaa Aaa Aaaaaaaa Aaaaaaaaaa Aa Aaa Aaaaaa Aaaaa Aaaaa Aaa Aaaaaaa Aaaaa a Aaaa Aaaaa a Aaaa Aa a Aaaaaaaaa Aaaa Aaaaa Aaaaaaaa Aaaaaaaaaa Aa Aaaaaaaa Aaaa Aaa Aaaaa Aaaaaaaaaaaaa Aaaaaaaa Aa Aaaaaaaa Aa Aaaaa Aaaaaaaaaaaaaaaaaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaa
a Aaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaa Aaaaaaaaaaaaa Aaa Aaaaaaaaaa Aaaaaaaa Aaaaaaaaaaaaaaaaa Aaaaa Aaaaa Aaaaaa Aaaaaaaaaaaaaaa Aaa Aaaaa Aaaaa Aaa Aaaaaaaaa Aaaaaaaaa Aaaaaa Aaaa Aaa Aaaaaaaaa Aaaaaaaaaaaaa Aaaaaaaaaa Aa Aaaaa a Aaaaaaaaaaaa Aaaaaaa Aaaaaaaaaaaaaaa Aaaaa Aaaa Aaaaaaaaaaaa Aa Aaaaaaaaaaa Aaaaaaaaa Aaa Aaaaaaaaa Aaaaaaaa Aaaaaa Aaaaa Aa Aaaaa Aaaaaaaaaa
🔒Multiple audit patterns identified across DFG optimization phases, with concrete search targets for variant discovery
Subscribe to read more