[7] Wasm OMG Tail Call F32 Spill Stack OOB Write
Severity: High | Component: JSC Wasm OMG JIT — tail call FPR spilling | a73d24d
Rated High because the observable effect is a deterministic 4-byte stack-adjacent write from JIT-generated code reachable via any Wasm module with F32 tail call arguments, and the adjacent data could include saved registers or control flow metadata — though the attacker's control over the written value (likely upper FPR bits, typically zeros on x86 SSE) limits the primitive's strength, projected with confidence 0.85.
F32 and F64 were both doing a double store when spilling in OMG tail calls, while F32 should be doing a float store to avoid writing out of bounds of the spill slot. No test as cannot reliably and observably test this temporary stack corruption.
Source/JavaScriptCore/wasm/WasmOMGIRGenerator.cpp
} else if (src.isFPR()) {
srcOffset = allocateSpill(dstType.width());
- if (dstType.width() <= Width::Width64)
- jit.storeDouble(src.fpr(), CCallHelpers::Address(MacroAssembler::stackPointerRegister, srcOffset));
- else
- jit.storeVector(src.fpr(), CCallHelpers::Address(MacroAssembler::stackPointerRegister, srcOffset));
+ auto dst = CCallHelpers::Address(MacroAssembler::stackPointerRegister, srcOffset);
+ if (dstType == Types::F32)
+ jit.storeFloat(src.fpr(), dst);
+ else if (dstType == Types::F64)
+ jit.storeDouble(src.fpr(), dst);
+ else {
+ ASSERT(dstType == Types::V128);
+ jit.storeVector(src.fpr(), dst);
+ }
Type-width mismatch between spill slot allocation and store instruction in JIT-generated tail call code.
Patch Details
In prepareForTailCallImpl, the FPR spilling path during OMG-tier Wasm tail calls previously used a single storeDouble (8-byte store) for all FPR types with width ≤ 64 bits, including F32. The fix splits this into three distinct cases: storeFloat (4 bytes) for Types::F32, storeDouble (8 bytes) for Types::F64, and storeVector (16 bytes) for Types::V128. The spill slot is allocated via allocateSpill(dstType.width()), which for F32 returns a 4-byte slot — so the old 8-byte storeDouble into a 4-byte slot wrote 4 bytes beyond the allocation.
Background
WebAssembly tail calls (return_call / return_call_indirect) reuse the current stack frame for the callee. During tail call preparation, the JIT must temporarily spill live argument values to scratch stack space before rearranging them into the callee's expected locations. allocateSpill(width) reserves a stack region sized to the given width, and the store instruction must match that width exactly — storeFloat writes 4 bytes, storeDouble writes 8. FPR registers on modern architectures are 64 or 128 bits wide and can hold values of different floating-point widths; the register width and the memory store width are not the same.
Analysis
The root cause is a width-based dispatch that conflated F32 and F64 because both fit in a 64-bit FPR. The spill slot allocation correctly used allocateSpill(dstType.width()), which allocates 4 bytes for F32. But the subsequent store used storeDouble (8 bytes) for any FPR value with width <= Width64, including F32. Every F32 tail-call argument spill therefore wrote 4 bytes past the end of its allocated slot. The extra 4 bytes overwrite whatever is adjacent — another spill slot, saved registers, or return metadata on the stack frame.
Notably, the constant-handling path just below in the same function already correctly distinguished F32 (Width32/storeFloat) from F64 (Width64/storeDouble). This asymmetry — where the register path was wrong but the constant path was right — strongly suggests the FPR path was written with a Width-based check that didn't account for the semantic difference between float and double FPR values.
Aaa Aaaaaaa Aaaaa a Aaa Aaaaa Aa Aaaa Aa Aaa Aaaaaa Aaa Aaaaaaaa a Aa Aaaaaaaaa Aaaaaa Aa Aaa Aaa Aaaa Aaaaaaa Aa Aaa Aaaaaa Aaaaa Aa Aaaaa Aaaaaaaaaaaaa Aaa Aaaaaaaa Aaa Aa Aaaaaaaaaa Aaa Aaaaaaa Aaaaaaa Aaaa Aaa Aaaaaaa Aaaaa Aaaaaaa Aaaaaa Aaaaaaa Aaa Aaaaaaaaaaa Aaaaaaaaa Aaa Aaaaaaa Aaaaaaaa Aaaa Aaa Aaaaa Aaaaaaa Aaaaaaa Aaaa Aa Aaaaa Aaaaa Aaaaaaaaa Aa Aaaaa Aaaaaaa
Aa Aaaaaaaaa Aaaaaaaaaaa Aaa Aaaa Aaaa Aaaaaaaa Aaaaa Aa Aaaaaaaa Aaaaa Aaaaaa Aaaaaaaa Aaaaaaaa Aaaaa Aaaa a Aaaaa Aaaaa Aaaaa Aaaaa Aaaaaa Aaa Aaaaaaaaaaaa Aaaaaa Aaa Aaaaa Aa Aaaaaaaaaaa Aaaaa Aaaaa Aaaaaaaa a Aaaaaa Aaaaaaaaa Aaaaaaaaa Aa Aaaaa Aaaaaaa a Aaaa Aaaaaa Aaaa Aaa Aaaa Aaaa Aaaaaaaaa Aa Aaa Aaaaaaaaa Aaaa Aa Aaa Aaaaaa Aa Aaaaaaa Aaa Aaaaaaaaaaa Aaaaa Aaaaaaaaaaaaaaaaa Aaa Aaaa Aaaaa
Aaaa Aaaaaaaaaaaaa Aaaaaaa Aaaaaa Aaaaaa Aaaaaa Aaa Aaaaaaaaaa Aaaaaaaa Aaaaaaaa Aaaaa Aaaaaaaaaa Aa Aaa Aaaa a Aaaa Aaaa Aaaaaaa Aaaaa Aaaaaaa a Aaaaa Aaaaa Aaaa Aaaaaaa Aaaaaaa Aaaa Aaaaaaaaaa Aaaaaa Aaa Aaaaaaaaa a Aaaaaaaa Aaaaaaa Aaaaaa Aaaaa Aa Aaaaaaaa Aaa Aaaa Aaaaaa Aaaaaaaaaaa
Aaa Aaaaaa Aaaaaaaaaa Aaaaa Aaa Aaa Aaaaaaa Aaaaaaaa Aaa Aaaaaaaaaa Aaaa Aaaa Aaaaaaaaa Aaaaa Aaaaaaaaaaaa Aaaaaaaaaa Aa Aaa Aaaaa Aa Aaaa Aaaaaaaaaa Aaaaaa Aaaa Aaaaa Aaaaaaaaaaaaa Aaa Aaaaaaaaa Aaaaaaa Aaa Aaaaaaa Aaaaaaaa Aaaa Aaa Aaaaaaaaa Aaaaaaaa Aaaa Aa Aaa Aaaa Aa Aaaaaaaaaaaaa Aaaa Aaaaaaaa Aaaaaa Aaaaaaa Aaaaaaa
Audit directions
a Aaaaaaaa Aaa Aaaaaaaaaaaa Aaaaa Aaaa Aaaaaaaa Aa Aaaaaaaa Aaaaa Aaaaaa Aaaa Aaaaaaaa Aaaa Aaaaaa Aaaa Aa Aaa Aaaaa Aaaa Aaaaaa Aaa Aaaaaa Aaaaaaaaaaaaaa Aaaaaaa Aaaaaaaaaaaaaa Aaaaaaaa Aaaaaa Aaaa Aaaaaaaaaaaa Aaa Aaaaaa Aaaaaaaaaa Aaaa Aaa Aaa Aaaaaaa Aaaaaaaaaa Aaaaa Aaaaa Aaaaa Aaaaa Aaaaa Aa Aaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaa Aaaaaaaaaaa Aaaaaaaaaaaa Aaaa Aaaaaaaa Aaa Aaa Aaaa Aaaa Aaa Aaaaaaaaaaaaa Aaaa Aaaaaaaaaaaaaaa Aa Aaaaaaaaa Aaaaaaa
a Aaaaaaaa Aaaaaaaaaa Aaaaaaaa Aa Aaa Aaaa Aaaa Aaaaaa Aaaa Aaaaa Aa Aaa Aaaaaaaaa Aaaaa Aaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaa Aaaaaaaaaa Aaaaaaaaa Aaa Aaaaa Aaaaa Aaaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaaa Aaaaa Aaaaaaa Aa Aaaaa Aaaa Aaaaaaaaa Aaaaaa Aaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaa Aa Aaaa Aaaa Aa Aaaaaaaa Aaaaaaaaaaa Aaaa Aa Aaaaaa Aaaaaaaaaaaa
a Aaaaaaaa Aaaaa Aaaaaaaaaa Aaaa Aaaaaaaaa Aaaaaa Aa Aaaaaaaaaaaaa Aaaaa Aaaaa Aaaaa Aaa Aaaa Aaaaa Aaaa Aaa Aaaaaaaaaaaaaaa Aa Aaaaaa Aaaaa Aaaa Aaaaaaaaaa a Aaaaaa Aaaa Aaa Aaaaa Aaaaa Aaaaaaa Aaa Aaaaaaaaa Aaaaa Aa Aaa Aaaaaa Aaaaa Aa Aaaaaaaaaaaaa Aaaaa Aaaaaa Aaaaaaaaaaa Aaaaaaaa Aaaaaaaaa Aaaaaaaaa Aaaaa Aaaa Aaaaaaa Aa Aaaaaaaa