Stream: git-wasmtime

Topic: wasmtime / issue #12318 Wasmtime fuzzbug: i32.shr_u with ...


view this post on Zulip Wasmtime GitHub notifications bot (Jan 12 2026 at 15:03):

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 for i32.shr_u where 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.7 against wasmtime 40.0.1 using 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.rs

fn 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).

view this post on Zulip Wasmtime GitHub notifications bot (Jan 12 2026 at 15:03):

louismerlin added the bug label to Issue #12318.

view this post on Zulip Wasmtime GitHub notifications bot (Jan 12 2026 at 15:03):

louismerlin added the fuzz-bug label to Issue #12318.

view this post on Zulip Wasmtime GitHub notifications bot (Jan 12 2026 at 16:21):

alexcrichton commented on issue #12318:

Thanks for the report! Should be fixed in https://github.com/bytecodealliance/wasmtime/pull/12321

view this post on Zulip Wasmtime GitHub notifications bot (Jan 12 2026 at 16:58):

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.

view this post on Zulip Wasmtime GitHub notifications bot (Jan 12 2026 at 17:04):

alexcrichton commented on issue #12318:

Would you be able to share a test case for i64x2.shr_s too?

view this post on Zulip Wasmtime GitHub notifications bot (Jan 12 2026 at 17:27):

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.

view this post on Zulip Wasmtime GitHub notifications bot (Jan 12 2026 at 19:01):

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 for i32.shr_u where 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.7 against wasmtime 40.0.1 using 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.rs

fn 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