Stream: git-wasmtime

Topic: wasmtime / issue #13751 Concurrent C-API type queries abo...


view this post on Zulip Wasmtime GitHub notifications bot (Jun 29 2026 at 10:43):

sdjasj opened issue #13751:

Test Case

This is not triggered by a .wasm file; it is a host-embedding issue in the C API, so no Wasm module is needed. Below is a self-contained two-file Cargo project that links against the in-tree wasmtime-c-api and calls wasm_memorytype_limits concurrently from 32 threads on 10,000 shared wasm_memorytype_t objects. Copy the two files into a new directory inside the Wasmtime source tree (e.g. <wasmtime-repo>/repro/poc/), then replace the CRATES_CAPI placeholder with the relative path back to crates/c-api (one-liner included below), and cargo run.

Cargo.toml:

[package]
name = "oncecell-race-poc"
version = "0.1.0"
edition = "2021"

[workspace]

[dependencies]
wasmtime-c-api-impl = { path = "CRATES_CAPI" }

src/main.rs:

use std::sync::{Arc, Barrier};
use std::thread;
use wasmtime_c_api::{wasm_limits_t, wasm_memorytype_limits, wasm_memorytype_new};

#[derive(Copy, Clone)]
struct SharedPtr(usize);
unsafe impl Send for SharedPtr {}
unsafe impl Sync for SharedPtr {}

fn main() {
    let limits = wasm_limits_t { min: 1, max: u32::MAX };
    // 10_000 distinct wasm_memorytype_t objects, all shared across 32 threads.
    let ptrs: Vec<_> = (0..10_000)
        .map(|_| SharedPtr(Box::into_raw(wasm_memorytype_new(&limits)) as usize))
        .collect();
    let ptrs = Arc::new(ptrs);
    let start = Arc::new(Barrier::new(32));

    let mut threads = Vec::new();
    for _ in 0..32 {
        let start = start.clone();
        let ptrs = ptrs.clone();
        threads.push(thread::spawn(move || {
            for ptr in ptrs.iter().copied() {
                // First call on each object races across all 32 threads at once.
                start.wait();
                let mt = unsafe { &*(ptr.0 as *const _) };
                let limits = wasm_memorytype_limits(mt);
                assert_eq!(limits.min, 1);
                start.wait();
            }
        }));
    }
    for t in threads { t.join().unwrap(); }
}

Steps to Reproduce

  1. From the Wasmtime main branch (repo HEAD 08c456e887; the affected file was last touched by 7948e0ff62, "Expose custom page sizes in the C and C++ APIs (#11890)"), create the two files above inside the repo (e.g. at <repo>/repro/poc/).
  2. Fix the dependency path, relative to that directory:
sed -i 's#CRATES_CAPI#../../crates/c-api#' Cargo.toml
  1. Build and run it (first run compiles the C API, ~a few minutes):
cargo run
  1. 32 threads concurrently make the first call to wasm_memorytype_limits on each shared wasm_memorytype_t.

Expected Results

Per the C API's documented thread-safety contract, the program should run to completion. Every wasm_memorytype_limits call returns the cached wasm_limits_t { min: 1, ... }, all assertions pass, and all threads join.

Actual Results

The process aborts. A std::cell::OnceCell::get_or_init reentrancy check trips during the concurrent first-initialization race, panicking with reentrant init; because the panic occurs inside an extern "C" function it cannot unwind, so the process aborts (exit status 141 / SIGABRT). Aborted output:

thread '<unnamed>' (707599) panicked at /rustc/.../library/core/src/cell/once.rs:300:66:
reentrant init
...
thread '<unnamed>' (707599) panicked at /rustc/.../library/core/src/panicking.rs:225:5:
panic in a function that cannot unwind
stack backtrace:
   ...
  19:     wasm_memorytype_limits
  20:     capi_oncecell_race_poc::main::{{closure}}
thread caused non-unwinding panic. aborting.

Versions and Environment

Wasmtime version or commit: Repo main HEAD 08c456e8870b0b85566f9e9808b7a2f044eaa265 (affected file crates/c-api/src/types/memory.rs, last modified by commit 7948e0ff62). PHPUnit/driver builds of the CLI report Wasmtime 47.0.0.

Operating system: Linux 5.15.0-139-generic (Ubuntu 20.04-based)

Architecture: x86_64

Rust: rustc 1.96.0 (ac68faa20 2026-05-25)

Extra Info

view this post on Zulip Wasmtime GitHub notifications bot (Jun 29 2026 at 10:43):

sdjasj added the bug label to Issue #13751.

view this post on Zulip Wasmtime GitHub notifications bot (Jun 30 2026 at 18:50):

alexcrichton closed issue #13751:

Test Case

This is not triggered by a .wasm file; it is a host-embedding issue in the C API, so no Wasm module is needed. Below is a self-contained two-file Cargo project that links against the in-tree wasmtime-c-api and calls wasm_memorytype_limits concurrently from 32 threads on 10,000 shared wasm_memorytype_t objects. Copy the two files into a new directory inside the Wasmtime source tree (e.g. <wasmtime-repo>/repro/poc/), then replace the CRATES_CAPI placeholder with the relative path back to crates/c-api (one-liner included below), and cargo run.

Cargo.toml:

[package]
name = "oncecell-race-poc"
version = "0.1.0"
edition = "2021"

[workspace]

[dependencies]
wasmtime-c-api-impl = { path = "CRATES_CAPI" }

src/main.rs:

use std::sync::{Arc, Barrier};
use std::thread;
use wasmtime_c_api::{wasm_limits_t, wasm_memorytype_limits, wasm_memorytype_new};

#[derive(Copy, Clone)]
struct SharedPtr(usize);
unsafe impl Send for SharedPtr {}
unsafe impl Sync for SharedPtr {}

fn main() {
    let limits = wasm_limits_t { min: 1, max: u32::MAX };
    // 10_000 distinct wasm_memorytype_t objects, all shared across 32 threads.
    let ptrs: Vec<_> = (0..10_000)
        .map(|_| SharedPtr(Box::into_raw(wasm_memorytype_new(&limits)) as usize))
        .collect();
    let ptrs = Arc::new(ptrs);
    let start = Arc::new(Barrier::new(32));

    let mut threads = Vec::new();
    for _ in 0..32 {
        let start = start.clone();
        let ptrs = ptrs.clone();
        threads.push(thread::spawn(move || {
            for ptr in ptrs.iter().copied() {
                // First call on each object races across all 32 threads at once.
                start.wait();
                let mt = unsafe { &*(ptr.0 as *const _) };
                let limits = wasm_memorytype_limits(mt);
                assert_eq!(limits.min, 1);
                start.wait();
            }
        }));
    }
    for t in threads { t.join().unwrap(); }
}

Steps to Reproduce

  1. From the Wasmtime main branch (repo HEAD 08c456e887; the affected file was last touched by 7948e0ff62, "Expose custom page sizes in the C and C++ APIs (#11890)"), create the two files above inside the repo (e.g. at <repo>/repro/poc/).
  2. Fix the dependency path, relative to that directory:
sed -i 's#CRATES_CAPI#../../crates/c-api#' Cargo.toml
  1. Build and run it (first run compiles the C API, ~a few minutes):
cargo run
  1. 32 threads concurrently make the first call to wasm_memorytype_limits on each shared wasm_memorytype_t.

Expected Results

Per the C API's documented thread-safety contract, the program should run to completion. Every wasm_memorytype_limits call returns the cached wasm_limits_t { min: 1, ... }, all assertions pass, and all threads join.

Actual Results

The process aborts. A std::cell::OnceCell::get_or_init reentrancy check trips during the concurrent first-initialization race, panicking with reentrant init; because the panic occurs inside an extern "C" function it cannot unwind, so the process aborts (exit status 141 / SIGABRT). Aborted output:

thread '<unnamed>' (707599) panicked at /rustc/.../library/core/src/cell/once.rs:300:66:
reentrant init
...
thread '<unnamed>' (707599) panicked at /rustc/.../library/core/src/panicking.rs:225:5:
panic in a function that cannot unwind
stack backtrace:
   ...
  19:     wasm_memorytype_limits
  20:     capi_oncecell_race_poc::main::{{closure}}
thread caused non-unwinding panic. aborting.

Versions and Environment

Wasmtime version or commit: Repo main HEAD 08c456e8870b0b85566f9e9808b7a2f044eaa265 (affected file crates/c-api/src/types/memory.rs, last modified by commit 7948e0ff62). PHPUnit/driver builds of the CLI report Wasmtime 47.0.0.

Operating system: Linux 5.15.0-139-generic (Ubuntu 20.04-based)

Architecture: x86_64

Rust: rustc 1.96.0 (ac68faa20 2026-05-25)

Extra Info


Last updated: Jul 29 2026 at 05:03 UTC