Stream: git-wasmtime

Topic: wasmtime / issue #14205 Exempt module instantiation from ...


view this post on Zulip Wasmtime GitHub notifications bot (Aug 25 2026 at 15:51):

AndSDev opened issue #14205:

Feature

Add a Config/Tunables option that exempts the synthesized ModuleStartup function from fuel metering, restoring the ≤ v36 behavior where Instance::new with store.set_fuel(0) succeeds for modules that have no (start ...) function. Since #13487 ("Move most module initialization to compiled code"), module initialization (globals, segments, tables, etc.) runs as compiled Wasm inside ModuleStartup (crates/cranelift/src/func_environ.rs) and is intentionally fuel-metered, so Instance::new now consumes fuel even for modules with no (start ...) and a small fuel budget makes instantiation itself trap with Trap::OutOfFuel. Fuel is meant to bound Wasm function execution, not to forbid instantiation.

Example

A module that requires a startup function but has no (start ...) — here a passive element segment, which is initialized once during instantiation:

(module
  (func $f (result i32) i32.const 42)
  (table 1 funcref)
  (elem $passive func $f))
// Cargo.toml: [dependencies] wasmtime = "48"
use wasmtime::*;

const MODULE: &str = r#"
    (module
      (func $f (result i32) i32.const 42)
      (table 1 funcref)
      (elem $passive func $f))
"#;

fn main() -> Result<()> {
    let mut config = Config::new();
    config.consume_fuel(true);
    let engine = Engine::new(&config)?;
    let module = Module::new(&engine, MODULE)?;

    // No `(start ...)`, but the passive element segment forces Wasmtime to
    // synthesize a `ModuleStartup` function, and that function is fuel-metered.
    for fuel in [0, 1, 2] {
        let mut store = Store::new(&engine, ());
        store.set_fuel(fuel)?;

        match Instance::new(&mut store, &module, &[]) {
            Ok(_) => {
                let consumed = fuel - store.get_fuel()?;
                println!("fuel={fuel}: instantiation succeeded, consumed {consumed} unit(s)");
            }
            Err(e) => println!("fuel={fuel}: instantiation failed: {e}"),
        }
    }
    Ok(())
}

Output (reproduced on main at f1412a598f, Wasmtime 48.0.0, Linux x86_64):

fuel=0: instantiation failed: wasm trap: all fuel consumed by WebAssembly
fuel=1: instantiation failed: wasm trap: all fuel consumed by WebAssembly
fuel=2: instantiation succeeded, consumed 1 unit(s)

fuel=0 traps inside Instance::new even though the module has no (start ...), and fuel=1 traps as well because the startup function's flat entry charge of 1 is mandatory; only fuel=2 succeeds, showing that instantiation consumes exactly 1 unit for this module. In ≤ v36, set_fuel(0) + Instance::new succeeded here, with fuel spent only when running an exported function. A module that needs no startup function still instantiates fine at fuel=0 today.

Why a cost parameter alone is insufficient

With store.set_fuel(0) the fuel counter is 0. fuel_check (func_environ.rs:626) traps once the counter becomes >= 0, and fuel_function_entry runs that check plus the mandatory initial fuel_consumed: 1 (func_environ.rs:296) at the entry of every compiled function, including ModuleStartup. So even setting every startup cost to 0 still leaves 0 + 1 >= 0 → trap. Only skipping the fuel entry/exit handling for ModuleStartup restores the v36 behavior.

Benefit

Implementation

Alternatives

view this post on Zulip Wasmtime GitHub notifications bot (Aug 25 2026 at 18:06):

alexcrichton added the wasmtime:fuel label to Issue #14205.

view this post on Zulip Wasmtime GitHub notifications bot (Aug 27 2026 at 16:46):

fitzgen commented on issue #14205:

FWIW, we don't make any hard guarantees for fuel consumption across versions of Wasmtime. Happy to have a new config option for disabling fuel consumption during instantiation, however.


Last updated: Aug 30 2026 at 09:07 UTC