Summary
On the ARM/thumb-2 backend (-t cortex-m3), i64.div_s(INT64_MIN, -1) silently returns a wrong value instead of trapping. Per WebAssembly Core §4.3.2 (idiv_s), the result of -2^63 / -1 is +2^63, which is not representable in i64, so the operation must trap (integer overflow). wasmtime traps; synth computes 0x8000000000000000 (= INT64_MIN) and returns it with no trap.
This is a silent overflow miscompile. The i32 path already emits the overflow guard; the i64 path does not — an asymmetry.
- synth
0.31.0 (v0.31.0-1-g28296d3)
- target
cortex-m3 (thumb-2), --relocatable
- oracle: wasmtime 42.0.1
Reproducer
(module
(func (export "f") (param i32 i32) (result i32)
(i32.wrap_i64
(i64.div_s
(i64.or (i64.shl (i64.extend_i32_u (local.get 1)) (i64.const 32))
(i64.extend_i32_u (local.get 0)))
(i64.const -1)))))
Dividend assembled from local 0 (low) / local 1 (high); divisor is the constant -1.
| dividend |
divisor |
WASM spec |
wasmtime |
synth (qemu) |
INT64_MIN (lo=0, hi=0x80000000) |
-1 |
trap (overflow) |
wasm trap |
returns 0 (low word of 0x8000000000000000, no trap) |
100 (lo=100, hi=0) |
-1 |
-100 |
-100 |
-100 ✓ (normal path OK) |
Normal signed 64-bit division is correct; only the INT64_MIN / -1 overflow case is mishandled.
Root cause
The i64 signed-division helper emits only the divide-by-zero trap and no INT64_MIN / -1 overflow guard. It negates the dividend (INT64_MIN negates to itself — the wrap), negates the divisor (-1→1), divides, and returns 0x8000000000000000. The corresponding i32 path does emit the overflow guard, so this is a missing-guard asymmetry between the i32 and i64 signed-division lowerings.
Fix-guard twin (please keep passing after the fix)
i64.rem_s(INT64_MIN, -1) must return 0 and must not trap (WASM Core §4.3.2 irem_s). synth currently returns 0 correctly — a fix for the div_s overflow trap must not over-eagerly make rem_s trap. (Verified: synth rem_s(INT64_MIN,-1)=0 today, matches wasmtime.)
Cross-reference
RV32 had related signed-division guard bugs (#232 i32 overflow-guard clobber, #317 i64 sign clobber) — both closed. This is the ARM/thumb-2 i64 backend missing the overflow guard entirely.
Found via QEMU Cortex-M semihosting execution-differential vs wasmtime; source location narrowed by a spec-mining pass.
Summary
On the ARM/thumb-2 backend (
-t cortex-m3),i64.div_s(INT64_MIN, -1)silently returns a wrong value instead of trapping. Per WebAssembly Core §4.3.2 (idiv_s), the result of-2^63 / -1is+2^63, which is not representable ini64, so the operation must trap (integer overflow). wasmtime traps; synth computes0x8000000000000000(= INT64_MIN) and returns it with no trap.This is a silent overflow miscompile. The i32 path already emits the overflow guard; the i64 path does not — an asymmetry.
0.31.0(v0.31.0-1-g28296d3)cortex-m3(thumb-2),--relocatableReproducer
Dividend assembled from
local 0(low) /local 1(high); divisor is the constant-1.INT64_MIN(lo=0, hi=0x80000000)wasm trap0x8000000000000000, no trap)100(lo=100, hi=0)Normal signed 64-bit division is correct; only the
INT64_MIN / -1overflow case is mishandled.Root cause
The i64 signed-division helper emits only the divide-by-zero trap and no
INT64_MIN / -1overflow guard. It negates the dividend (INT64_MIN negates to itself — the wrap), negates the divisor (-1→1), divides, and returns0x8000000000000000. The corresponding i32 path does emit the overflow guard, so this is a missing-guard asymmetry between the i32 and i64 signed-division lowerings.Fix-guard twin (please keep passing after the fix)
i64.rem_s(INT64_MIN, -1)must return 0 and must not trap (WASM Core §4.3.2irem_s). synth currently returns0correctly — a fix for the div_s overflow trap must not over-eagerly makerem_strap. (Verified: synthrem_s(INT64_MIN,-1)=0today, matches wasmtime.)Cross-reference
RV32 had related signed-division guard bugs (#232 i32 overflow-guard clobber, #317 i64 sign clobber) — both closed. This is the ARM/thumb-2 i64 backend missing the overflow guard entirely.
Found via QEMU Cortex-M semihosting execution-differential vs wasmtime; source location narrowed by a spec-mining pass.