fitzgen commented on issue #14568:
<details><summary>Full LLM report</summary>
Cranelift interpreter traps on
srem INT_MIN, -1instead of returning 0
Date 2026-10-05 Wasmtime commit 73b04cff3317d1e308866eb24359483ac6116669(main)Host macOS 15.8.1 (Darwin 24.6.0), aarch64-apple-darwinModel Claude Opus 5.5 ( claude-opus-5-5)Component cranelift/interpreter/src/value.rs(DataValue::srem)Class Wrong semantics in the differential-testing oracle (same class as report 011) Severity Low for production; it causes false positives in any interpreter-vs-native harness Summary
DataValue::srem(cranelift/interpreter/src/value.rs:630-637) returns
ValueError::IntegerOverflowwhen the dividend isINT_MINand the divisor is
-1.step.rs:182turns that into anint_ovfuser trap (TrapCode(252)):fn srem(self, other: Self) -> ValueResult<Self> { ... // Check if we are dividing INT_MIN / -1. This causes an integer overflow trap. if self == min && denominator == -1 { return Err(ValueError::IntegerOverflow); }That check is right for
sdivand wrong forsrem. The case is mathematically
well defined and has remainder 0, and everything else in the system agrees:
The CLIF definition of
srem
(cranelift/codegen/meta/src/shared/instructions.rs:1857-1862) says only
"This operation traps if the divisor is zero."Native code on aarch64 and pulley64 returns 0, as does every backend's
lowering. Each one special-cases-1to avoid the hardware fault on x86.The mid-end rule
srem x, (iconst -1) => 0
(cranelift/codegen/src/opts/arithmetic.isle:132) already encodes 0.Wasm's
i32.rem_s/i64.rem_sare specified to return 0.So the interpreter, Cranelift's reference oracle, disagrees with compiled code
on a valid input. The check was added in3f6b889067(2021, "Prevent panics
when dividing INT_MIN / -1 in interpreter"), which applied thesdivoverflow
rule to both operations.
cranelift-fuzzgenhappens to mask this. Itsint_divzpass
(cranelift/fuzzgen/src/passes/int_divz.rs:51-65) rewrites the divisor
wheneverlhs == INT_MIN && rhs == -1forsremas well assdiv, which is
itself unnecessary forsrem. Any other differential harness, including
filetests that combinetest interpretwithtest run, hits a spurious
mismatch. This audit's mid-end differential harness hit exactly that.Reproduction
$ target/debug/clif-util test reports/017-interpreter-srem-int-min-traps/srem.clif panicked at cranelift/filetests/src/test_interpret.rs:111:29: Unexpected returned control flow: Trap(User(TrapCode(252))) $ target/debug/clif-util test reports/017-interpreter-srem-int-min-traps/srem-run-only.clif 1 tests # native aarch64 and pulley64: all three return 0
srem.clifcheckssrem INT_MIN, -1 == 0at i8, i32 and i64, under both
test interpretandtest run(target aarch64,target pulley64).
srem-run-only.clifholds the same functions with onlytest run.Suggested fix
In
DataValue::srem, drop theIntegerOverflowcheck. Then compute the
result withwrapping_rem, or return 0 directly when the divisor is-1, so
Rust's%does not panic onMIN % -1. Keep the check insdiv.Optionally, narrow fuzzgen's
int_divzrewrite to apply theINT_MIN / -1
guard only tosdiv, so the fuzzer covers this case again.</details>
fitzgen opened issue #14568:
sremonly traps on a zero divisor, andsrem INT_MIN, -1is 0. Native
code agrees, and so does the mid-end rulesrem x, -1 => 0. The
interpreter'sDataValue::srem, however, copiessdiv's overflow check and
traps withint_ovf.
.clifTest Casetest interpret test run target aarch64 function %f(i32, i32) -> i32 { block0(v0: i32, v1: i32): v2 = srem v0, v1 return v2 } ; run: %f(0x80000000, -1) == 0Steps to Reproduce
clif-util test test.clifExpected Results
Both
test interpretandtest runpass.Actual Results
test runpasses.test interpretfails:Unexpected returned control flow: Trap(User(TrapCode(252)))Versions and Environment
Cranelift version or commit:
73b04cff33Operating system: macOS 15.8.1
Architecture: aarch64
fitzgen added the bug label to Issue #14568.
fitzgen added the cranelift:area:interpreter label to Issue #14568.
Last updated: Oct 11 2026 at 04:10 UTC