[Potential Bug]: Wizard x86-64 SPC: `i8x16.ne` (and i16x8/i32x4/i64x2) corrupts a live local
Nobody has claimed this yet.
- Dominant language
- WebAssembly
- Stars
- 512
- Forks
- 51
- Avg merge
- 7h 34m
- Merged PRs (30d)
- 21
Description
Summary
On the x86-64 single-pass compiler tiers (lazy, jit, spc), a vector
*.ne instruction whose operand is a register-cached local overwrites that
local with all-ones. Later reads of the local return -1 instead of its real
value. The interpreter tiers (int, dyn) are correct, so the tiers disagree.
Reproducer (ne_clobber_repro.wat)
(module
(func (export "main") (result i32)
(local $v v128)
v128.const i8x16 0 0 0 0 0 0x9c 0 0 0 0 0 0 0 0 0 0 ;; lane 5 = 0x9c (-100)
local.set $v
local.get $v local.get $v i8x16.ne drop ;; result discarded
local.get $v i8x16.extract_lane_s 5)) ;; return local back
wizeng --print-result --mode=int ne_clobber_repro.wasm -> -100 (correct)
wizeng --print-result --mode=spc ne_clobber_repro.wasm -> -1 (wrong)
The i8x16.ne result is discarded; it only acts as a side effect. The bug is
that it mutates $v, which main then returns directly.
Root cause
emit_v128_ne in src/engine/x86-64/X86_64MacroAssembler.v3 builds the all-ones
XOR mask for the complement-of-equal in the src operand register itself:
f(dst, src) ; dst = (dst == src) pcmpeqb
f(src, src) ; src = all-ones <-- clobbers src
pxor dst, src ; dst = (dst != src)
In the SPC, visit_I8X16_NE uses do_op2_x_x, which allocates no scratch and
passes the local's cached register as src. Overwriting it corrupts the local.
The interpreter is unaffected because it loads operands into throwaway scratch
xmms. Affects i8x16/i16x8/i32x4/i64x2.ne.
Fix
Build the all-ones mask in a dedicated scratch register instead of src
(the same pattern gt_u already uses):
X86_64MacroAssembler.v3:emit_v128_netakes ascratchparam; use
f(scratch, scratch)for the mask.X86_64SinglePassCompiler.v3: the fourvisit_*_NEusedo_op2_x_x_xtmp.X86_64Interpreter.v3: pass the existing scratchr_xmm2to the four
emit_*_necalls.
After the fix all five tiers agree (-100).
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
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in src/engine/x86-64/X86_64MacroAssembler.v3 at emit_v128_ne, then trace the four visit_*_NE methods in X86_64SinglePassCompiler.v3 and the matching calls in X86_64Interpreter.v3. Reproduce the issue with ne_clobber_repro.wat using --mode=int and --mode=spc, then verify that all five tiers return -100 after the fix.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- wasm
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 76/100