Stream: git-wasmtime

Topic: wasmtime / PR #12599 Fix wasip1 memory leak


view this post on Zulip Wasmtime GitHub notifications bot (Feb 16 2026 at 09:02):

mbund requested wasmtime-wasi-reviewers for a review on PR #12599.

view this post on Zulip Wasmtime GitHub notifications bot (Feb 16 2026 at 09:02):

mbund opened PR #12599 from mbund:wasip1-resource-table-memory-leak-fix to bytecodealliance:main:

I use wasmtime to host a long lived wasip1 program, and I discovered a memory leak.

The issue is in async wasip1 contexts. The minimal code to reproduce (is taken from the existing wasip1-async example):

// cargo build --example wasip1-async --release
use wasmtime::Result;
use wasmtime::{Engine, Linker, Module, Store};
use wasmtime_wasi::WasiCtx;
use wasmtime_wasi::p1::{self, WasiP1Ctx};

#[tokio::main]
async fn main() -> Result<()> {
    let engine = Engine::default();

    let mut linker: Linker<WasiP1Ctx> = Linker::new(&engine);
    p1::add_to_linker_async(&mut linker, |t| t)?;

    let wasi_ctx = WasiCtx::builder().inherit_stdio().build_p1();

    let mut store = Store::new(&engine, wasi_ctx);

    let module = Module::from_file(&engine, "target/wasm32-wasip1/release/wasi.wasm")?;
    let func = linker
        .module_async(&mut store, "", &module)
        .await?
        .get_default(&mut store, "")?
        .typed::<(), ()>(&store)?;

    func.call_async(&mut store, ()).await?;

    Ok(())
}

As well as a minimal wasm program:

// cargo build -p example-wasi-wasm --target wasm32-wasip1 --release
fn main() {
    loop {
        std::thread::sleep(std::time::Duration::from_millis(1));
    }
}

Now, running the program in heaptrack with heaptrack ./target/release/examples/wasip1-async, we can see that there is a problem:

<img width="1338" height="929" alt="image" src="https://github.com/user-attachments/assets/a49f7bf7-2dee-4a43-866a-f8f3a963924e" />

It grows by megabytes in minutes.

The issue was in wasmtime_wasi::p2::host::clocks::subscribe_to_duration (and an underlying Vec grow). If I understand correctly, wasip1 is implemented in terms of wasip2. Wasip2 creates pollables and hands them off to a guest app, but the wasip1 code was just creating pollables and never dropping them. So to fix it, I just drop those pollables after we are done using them. Also, I added code to drop the partial pollable list if it encounters an error while still creating more pollables.

And here is a screenshot of heaptrack on my fix branch, after :

<img width="1338" height="929" alt="image" src="https://github.com/user-attachments/assets/6218c8c1-9dc0-45ac-adf3-8b6e5aeea684" />

Perfectly flat, even after a long time!

I was not able to find an open issue about this, so I just made this PR. Let me know if this fix is sensible or what I can do to make it more robust.

view this post on Zulip Wasmtime GitHub notifications bot (Feb 16 2026 at 09:29):

mbund updated PR #12599.

view this post on Zulip Wasmtime GitHub notifications bot (Feb 16 2026 at 11:00):

github-actions[bot] added the label wasi on PR #12599.

view this post on Zulip Wasmtime GitHub notifications bot (Feb 16 2026 at 18:20):

alexcrichton requested alexcrichton for a review on PR #12599.


Last updated: Feb 24 2026 at 04:36 UTC