Engine: Wizard 26.2985 · Tiers affected: int, dyn (x86-64 assembly interpreter). lazy/jit/spc are correct.
Symptom
v128.store{8,16,32,64}_lane stores the correct lane value to the wrong address:
it writes to mem_base + lane_immediate instead of the computed effective address.
No trap, silently wrong memory.
Minimal reproducer
(module
(memory (export "_memory") 1)
(func (export "main") (result i32)
i32.const 256
v128.const i32x4 0x0036fda8 0x0076015d 0xfccb0211 0xfdb5fd1a
v128.store8_lane offset=0 6 ;; should write byte 0x76 to addr 256
i32.const 256
i32.load8_u)) ;; reads back addr 256
int/dyn -> 0 (0x76 was written to addr 6 = the lane immediate)
spc/jit/lazy/wasmi -> 118 (0x76) ;; correct
Root cause
genStoreLane in src/engine/x86-64/X86_64Interpreter.v3 reuses r_tmp0 for two
things: decode_memarg builds the effective address in r_tmp0, then
load_imm8(idx) overwrites r_tmp0 with the lane immediate before the store runs,
so the store targets [mem_base + lane]. (genLoadLane is correct because it uses a
separate register for the address.)
Fix
In genStoreLane, keep the address in r_tmp0 and put the lane immediate in the
spare r_tmp2. (r_tmp0 must stay the memarg dest — genReadUleb32 rejects
r_scratch == r_tmp2.)
- def idx: X86_64Gpr = r_tmp0;
- def val: X86_64Gpr = r_tmp1;
- def mem_addr = decode_memarg(vsph[-2].value, idx, val);
- load_imm8(idx);
+ def addr: X86_64Gpr = r_tmp0; // effective store address (must survive)
+ def val: X86_64Gpr = r_tmp1;
+ def lane: X86_64Gpr = r_tmp2; // lane immediate in its own register
+ def mem_addr = decode_memarg(vsph[-2].value, addr, val);
+ load_imm8(lane);
asm.q.lea(val, data);
- asm_mov_r_m(val, X86_64Addr.new(val, idx, size, 0));
+ asm_mov_r_m(val, X86_64Addr.new(val, lane, size, 0));
asm_mov_m_r(mem_addr, val);
Verified: all store{8,16,32,64}_lane widths/lanes agree int==spc after the fix,
and the original fuzz seed now agrees across all 5 tiers + wasmi.
Additional information
A combination of AFL++ and Wasmlike, an Xsmith-based random program generator produced the snippet of code that found the issue. Xsmith Project
Engine: Wizard 26.2985 · Tiers affected:
int,dyn(x86-64 assembly interpreter).lazy/jit/spcare correct.Symptom
v128.store{8,16,32,64}_lanestores the correct lane value to the wrong address:it writes to
mem_base + lane_immediateinstead of the computed effective address.No trap, silently wrong memory.
Minimal reproducer
Root cause
genStoreLaneinsrc/engine/x86-64/X86_64Interpreter.v3reusesr_tmp0for twothings:
decode_memargbuilds the effective address inr_tmp0, thenload_imm8(idx)overwritesr_tmp0with the lane immediate before the store runs,so the store targets
[mem_base + lane]. (genLoadLaneis correct because it uses aseparate register for the address.)
Fix
In
genStoreLane, keep the address inr_tmp0and put the lane immediate in thespare
r_tmp2. (r_tmp0must stay the memarg dest —genReadUleb32rejectsr_scratch == r_tmp2.)Verified: all
store{8,16,32,64}_lanewidths/lanes agree int==spc after the fix,and the original fuzz seed now agrees across all 5 tiers + wasmi.
Additional information
A combination of AFL++ and Wasmlike, an Xsmith-based random program generator produced the snippet of code that found the issue. Xsmith Project