louismerlin opened issue #12318:
Description
When running with
cranelift_opt_level(OptLevel::None), Wasmtime returns a different value than the WebAssembly spec and other engines fori32.shr_uwhere the shift amount is 32. In this mode, Wasmtime yields the same result as if the shift amount were taken verbatim, rather than masked to 5 bits.This bug was found during differential fuzzing of wasmi
1.0.7against wasmtime40.0.1using ziggy (which uses AFL++ and honggfuzz under the hood) instead of libfuzzer.Expected behavior
The provided example should execute in the same way with wasmi and wasmtime.
Instead we get:
wasmi says Ok(25), but wasmtime says Ok(1)
Cargo.toml[package] name = "reproducing_the_bug" version = "0.1.0" edition = "2024" [dependencies] wasmi = "1.0.7" wasmtime = "40.0.1" wat = "1.228.0"
src/main.rsfn main() -> Result<(), Box<dyn std::error::Error>> { let wat = r#" (module (func (export "") (result i32) i32.const 24 i32.const 32 i32.shr_u i32.const 1 i32.const 0 i32.shl i32.or) ) "#; let wasm = wat::parse_str(wat)?; let wasmi_config = wasmi::Config::default(); let wasmi_engine = wasmi::Engine::new(&wasmi_config); let wasmi_module = wasmi::Module::new(&wasmi_engine, &wasm)?; let mut wasmtime_config = wasmtime::Config::new(); wasmtime_config.cranelift_opt_level(wasmtime::OptLevel::None); let wasmtime_engine = wasmtime::Engine::new(&wasmtime_config)?; let wasmtime_module = wasmtime::Module::new(&wasmtime_engine, &wasm)?; let mut wasmi_store = wasmi::Store::new(&wasmi_engine, ()); let wasmi_instance = wasmi::Instance::new(&mut wasmi_store, &wasmi_module, &[])?; let mut wasmtime_store = wasmtime::Store::new(&wasmtime_engine, ()); let wasmtime_instance = wasmtime::Instance::new(&mut wasmtime_store, &wasmtime_module, &[])?; let wasmi_func = wasmi_instance.get_typed_func::<(), i32>(&wasmi_store, "")?; let wasmtime_func = wasmtime_instance.get_typed_func::<(), i32>(&mut wasmtime_store, "")?; let args = (); println!( "wasmi says {:?}, but wasmtime says {:?}", wasmi_func.call(&mut wasmi_store, args), wasmtime_func.call(&mut wasmtime_store, args), ); Ok(()) }This example was also tested against wasmer, which agrees with wasmi (result is 25).
louismerlin added the bug label to Issue #12318.
louismerlin added the fuzz-bug label to Issue #12318.
alexcrichton commented on issue #12318:
Thanks for the report! Should be fixed in https://github.com/bytecodealliance/wasmtime/pull/12321
louismerlin commented on issue #12318:
Awesome thank you for the fast response! A colleague just mentioned to me that the issue was also present for i64x2.shr_s fyi.
alexcrichton commented on issue #12318:
Would you be able to share a test case for i64x2.shr_s too?
louismerlin commented on issue #12318:
I wasn't able to reproduce on my machine, I've asked him to share a test case (he only gave me the wasm). He or I will get back to you here.
alexcrichton closed issue #12318:
Description
When running with
cranelift_opt_level(OptLevel::None), Wasmtime returns a different value than the WebAssembly spec and other engines fori32.shr_uwhere the shift amount is 32. In this mode, Wasmtime yields the same result as if the shift amount were taken verbatim, rather than masked to 5 bits.This bug was found during differential fuzzing of wasmi
1.0.7against wasmtime40.0.1using ziggy (which uses AFL++ and honggfuzz under the hood) instead of libfuzzer.Expected behavior
The provided example should execute in the same way with wasmi and wasmtime.
Instead we get:
wasmi says Ok(25), but wasmtime says Ok(1)
Cargo.toml[package] name = "reproducing_the_bug" version = "0.1.0" edition = "2024" [dependencies] wasmi = "1.0.7" wasmtime = "40.0.1" wat = "1.228.0"
src/main.rsfn main() -> Result<(), Box<dyn std::error::Error>> { let wat = r#" (module (func (export "") (result i32) i32.const 24 i32.const 32 i32.shr_u i32.const 1 i32.const 0 i32.shl i32.or) ) "#; let wasm = wat::parse_str(wat)?; let wasmi_config = wasmi::Config::default(); let wasmi_engine = wasmi::Engine::new(&wasmi_config); let wasmi_module = wasmi::Module::new(&wasmi_engine, &wasm)?; let mut wasmtime_config = wasmtime::Config::new(); wasmtime_config.cranelift_opt_level(wasmtime::OptLevel::None); let wasmtime_engine = wasmtime::Engine::new(&wasmtime_config)?; let wasmtime_module = wasmtime::Module::new(&wasmtime_engine, &wasm)?; let mut wasmi_store = wasmi::Store::new(&wasmi_engine, ()); let wasmi_instance = wasmi::Instance::new(&mut wasmi_store, &wasmi_module, &[])?; let mut wasmtime_store = wasmtime::Store::new(&wasmtime_engine, ()); let wasmtime_instance = wasmtime::Instance::new(&mut wasmtime_store, &wasmtime_module, &[])?; let wasmi_func = wasmi_instance.get_typed_func::<(), i32>(&wasmi_store, "")?; let wasmtime_func = wasmtime_instance.get_typed_func::<(), i32>(&mut wasmtime_store, "")?; let args = (); println!( "wasmi says {:?}, but wasmtime says {:?}", wasmi_func.call(&mut wasmi_store, args), wasmtime_func.call(&mut wasmtime_store, args), ); Ok(()) }This example was also tested against wasmer, which agrees with wasmi (result is 25).
Last updated: Jan 29 2026 at 13:25 UTC