You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Found by footgun/adversarial differential testing while verifying the v0.17.0 stack-fwd flip (the flip itself is clean — this is flip-INDEPENDENT and pre-existing, reproduces on v0.16.0 and the v0.17.0 tag). Surfacing as a real soundness bug.
Symptom
A leaf function taking an i64 param and doing any i64 binop against a constant returns the wrong value — the param is effectively dropped.
t(7) → wasmtime: 12, synth: 5 (the constant; param ignored). Same for i64.sub, i64.or, i64.mul, and large constants.
Root cause (from disasm) — BOTH paths affected, differently
Optimized path (--target cortex-m4, no --relocatable): the i64 param (AAPCS R0:R1) is read from R4:R5, which are pushed callee-saved with no r0→r4 / r1→r5 homing move:
Relocatable / shipped path (--relocatable): the const-low is materialized into R1, clobbering the param's high half before the add-with-carry reads it:
i32 is clean: (i32.add (local.get 0) (i32.const 5)) → adds r5, r0, r4 reads R0 correctly. The bug is i64-param-specific.
i64 identity ((local.get 0)) is correct (R0:R1 passthrough), which is why the existing i64_lowering_doesnt_clobber_params CI oracle passes — it doesn't exercise an i64 param feeding an i64 binop-with-const.
An execution differential: compile (param i64)(result i64)(i64.<op> (local.get 0)(i64.const C)) for op ∈ {add,sub,mul,and,or,xor} and small+large C, run under unicorn with the param in R0:R1, diff vs wasmtime. Red today on both paths.
Byte-changing codegen fix ⇒ separate gated step (re-freeze + differential). Not urgent unless a real workload passes a bare i64 param to an exported leaf (falcon/gust use i32 entry params), but it is a silent miscompile of valid wasm.
Found by footgun/adversarial differential testing while verifying the v0.17.0 stack-fwd flip (the flip itself is clean — this is flip-INDEPENDENT and pre-existing, reproduces on v0.16.0 and the v0.17.0 tag). Surfacing as a real soundness bug.
Symptom
A leaf function taking an
i64param and doing any i64 binop against a constant returns the wrong value — the param is effectively dropped.Minimal repro:
t(7)→ wasmtime: 12, synth: 5 (the constant; param ignored). Same fori64.sub,i64.or,i64.mul, and large constants.Root cause (from disasm) — BOTH paths affected, differently
Optimized path (
--target cortex-m4, no--relocatable): the i64 param (AAPCS R0:R1) is read from R4:R5, which are pushed callee-saved with nor0→r4 / r1→r5homing move:Relocatable / shipped path (
--relocatable): the const-low is materialized into R1, clobbering the param's high half before the add-with-carry reads it:Scope / not-this
(i32.add (local.get 0) (i32.const 5))→adds r5, r0, r4reads R0 correctly. The bug is i64-param-specific.i64identity ((local.get 0)) is correct (R0:R1 passthrough), which is why the existingi64_lowering_doesnt_clobber_paramsCI oracle passes — it doesn't exercise an i64 param feeding an i64 binop-with-const.Suggested gate
An execution differential: compile
(param i64)(result i64)(i64.<op> (local.get 0)(i64.const C))for op ∈ {add,sub,mul,and,or,xor} and small+large C, run under unicorn with the param in R0:R1, diff vs wasmtime. Red today on both paths.Byte-changing codegen fix ⇒ separate gated step (re-freeze + differential). Not urgent unless a real workload passes a bare i64 param to an exported leaf (falcon/gust use i32 entry params), but it is a silent miscompile of valid wasm.