[JSC] IPInt fast path for `memory.atomic.wait32`/`wait64` wraps the Memory64 effective address
Component: JSC | 370255e
Source/JavaScriptCore/llint/InPlaceInterpreter64.asm
IPInt is WebKit's assembly-level WebAssembly bytecode interpreter. Memory64 extends Wasm to full i64 addressing, which makes both the runtime base pointer and the static immediate offset encoded in the instruction 64-bit — and therefore makes their sum capable of overflowing. The baddpc macro is the carry-aware add already used by the regular Memory64 load/store fast paths: it performs the addition and, if the carry flag is set, redirects to _ipint_throw_OutOfBoundsMemoryAccess. The atomic wait fast path was the one Memory64 fast path that had not adopted the guard, computing pointer + offset with a bare addq and handing the result to the slow path, which carries no independent overflow check of its own.
Memory64 atomic wait — effective address computation:
Before (pointer=0xFFFF_FFFF_FFFF_FFF8, offset=8):
addq t1, t0 → result = 0x0000_0000_0000_0000 (carry ignored)
storeq → stack ↓
slow path receives 0 → in-bounds, no trap ← WRONG
After:
baddpc(t1, t0, OOB)
├─ no carry → result stored, continue to slow path (correct)
└─ carry → jump to _ipint_throw_OutOfBoundsMemoryAccess ← FIXED
Significance
A Memory64 module using atomic wait could bypass the mandatory out-of-bounds trap, blocking on or reading from a wrapped arbitrary address. That collapses the memory safety guarantee for this instruction class — the trap is the entire bounds enforcement here.
Audit directions
The primitive is concrete: a Memory64 module with memory.atomic.wait32 offset=8 and pointer 0xFFFF_FFFF_FFFF_FFF8n made the engine see address 0 and proceed. Two follow-ups matter most. First, audit every other Memory64 instruction handler in InPlaceInterpreter64.asm for bare addq in pointer+offset computations — the atomic wait handlers appear to have been written separately from the main memory access fast paths, and may not be the only ones that missed baddpc. In review, an addq producing an effective address in a Memory64 path, with no adjacent branch target, is the visual tell; the correct form always names the OOB handler on the same line. Second, audit the atomic slow path itself (ipint_slow_path_memory_atomic_wait32/wait64) to confirm it performs an independent bounds check on the address it receives — it demonstrably had no overflow detection and cannot distinguish a wrapped address from a legitimately small pointer.