Stream: git-wasmtime

Topic: wasmtime / issue #13396 wasip3: `sockets-tcp-connect::tes...


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

cataggar opened issue #13396:

Summary

The upstream wasi-testsuite fixture wasm32-wasip3/sockets-tcp-connect (tests/rust/wasm32-wasip3/src/bin/sockets-tcp-connect.rs's test_explicit_bind_addrinuse) panics on wasmtime 44.0.1 (the first stable release with -Sp3 support). The fixture creates two sockets, binds the first to localhost:0, then asks the kernel for the assigned port and tries to bind a second socket to the same address, expecting ErrorCode::AddressInUse. wasmtime returns something other than Err(AddressInUse) and the matches! assertion panics.

Reproduction

# In a wasi-testsuite checkout at d8c30a7 with wasmtime 44.0.1 on PATH
cd tests/rust/wasm32-wasip3 && make build && cd -
wasmtime -Wcomponent-model-async -Sp3,inherit-network \
    tests/rust/testsuite/wasm32-wasip3/sockets-tcp-connect.wasm

Output (trimmed):

thread '<unnamed>' (1) panicked at src/bin/sockets-tcp-connect.rs:154:5:
assertion failed: matches!(result, Err(ErrorCode::AddressInUse))
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Error: failed to run main module `…/sockets-tcp-connect.wasm`

   12:   0xbbf1 - sockets_tcp_connect!sockets_tcp_connect::test_explicit_bind_addrinuse::{closure#0}

    1: wasm trap: wasm `unreachable` instruction executed

The asserting code (tests/wasi-testsuite tests/rust/wasm32-wasip3/src/bin/sockets-tcp-connect.rs):

async fn test_explicit_bind_addrinuse(family: IpAddressFamily) {
    let listener = {
        let bind_address = IpSocketAddress::localhost(family, 0);
        let listener = TcpSocket::create(family).unwrap();
        listener.bind(bind_address).unwrap();   // bind to ephemeral port
        listener
    };

    let listener_address = listener.get_local_address().unwrap();  // ask the kernel for the port
    let client = TcpSocket::create(family).unwrap();

    let result = client.bind(listener_address);   // second bind to the same port
    assert!(matches!(result, Err(ErrorCode::AddressInUse)));   // panics on wasmtime 44.0.1
}

Cross-runtime parity

The same fixture passes on the WAMR-Zig WASIp3 host (40 / 40 wasm32-wasip3 fixtures pass, this one included), which suggests the upstream WIT contract is for bind to return Err(AddressInUse) when a second socket tries to bind to a port still held by another socket. wasmtime 44 either lets the second bind succeed (perhaps because SO_REUSEADDR is set on the second socket) or maps the kernel EADDRINUSE to a different ErrorCode variant.

Why this matters

This is one of four wasm32-wasip3 fixtures that fail on a stock wasmtime 44.0.1 install but pass on the WAMR-Zig host; tracked in cataggar/wamr#583 C1 (the cross-runtime parity gate). Filing here so the parity gate can mark it as a documented wasmtime-side delta until a wasmtime fix lands.

Environment

view this post on Zulip Wasmtime GitHub notifications bot (May 21 2026 at 18:29):

dicej assigned dicej to issue #13396.

view this post on Zulip Wasmtime GitHub notifications bot (May 21 2026 at 19:15):

dicej commented on issue #13396:

Thanks for reporting this, @cataggar.

BTW, I couldn't find a d8c30a7 commit in https://github.com/WebAssembly/wasi-testsuite, nor a Makefile under tests/rust/wasm32-wasip3 in that repo, but I was able to reproduce the issue using (cd tests/rust/wasm32-wasip3 && cargo build --target wasm32-wasip2) && wasmtime -Wcomponent-model-async -Sp3,inherit-network tests/rust/wasm32-wasip3/target/wasm32-wasip2/debug/sockets-tcp-connect.wasm.

After a bit of debugging, I traced this back to these lines which, as you suspected, set SO_REUSEADDR on the socket. Commenting out those lines makes the test pass.

@badeend @rvolosatovs as the last ones to touch that code in e.g. https://github.com/bytecodealliance/wasmtime/pull/12597, can you shed some light on this? The comment says that it's to bypass the "TIME_WAIT state of a recently closed socket on the same local address", but seems to have the side effect of bypassing still-open sockets on the same local address, which presumably wasn't the intent.

view this post on Zulip Wasmtime GitHub notifications bot (May 21 2026 at 19:40):

badeend commented on issue #13396:

AFAIK, the wasi-testsuite is (partially) adapted from the wasmtime tests. Wasmtime contains spiritually the same test which "successfully" fails on all platforms:

https://github.com/bytecodealliance/wasmtime/blob/9a0b0e0df8839f8f76e8851f5c30ac5bbabc96dd/crates/test-programs/src/bin/p3_sockets_tcp_bind.rs#L39-L53

wasmtime returns something other than Err(AddressInUse)

Are you able to share what it actually returns? Even though the wasi-testsuite & wasmtime tests are structured slightly differently, I don't see any obvious reasons for the output to be different.

view this post on Zulip Wasmtime GitHub notifications bot (May 21 2026 at 19:52):

dicej commented on issue #13396:

Are you able to share what it actually returns? Even though the wasi-testsuite & wasmtime tests are structured slightly differently, I don't see any obvious reasons for the output to be different.

It returns Ok(()) for me.

view this post on Zulip Wasmtime GitHub notifications bot (May 23 2026 at 10:28):

badeend commented on issue #13396:

Spotted the discrepancy. The listener socket in the wasi-testsuite test is not actually listening.

On Linux the listen is needed in order to trigger the desired error code. From the man pages:

SO_REUSEADDR: (...) For AF_INET sockets this means that a socket may bind, except when there is an active listening socket bound to the address.

In other words; Yes, Linux allows multiple sockets the same address, but.. at most one _active_ socket. As soon as you actually want to use it (i.e. listen on it), the latter later calls fail. Take the following example sequence:

sockA.bind(someAddr);
sockB.bind(someAddr);
sockA.listen();
sockB.listen();

Windows fails immediately on the second bind (sockB.bind). On Linux this is deferred until the second listen (sockB.listen).

As far as I'm aware this is a Linux-specific peculiarity and there's not much we can do about it, other than updating the wasi-testsuite test to be more platform agnostic:

  async fn test_explicit_bind_addrinuse(family: IpAddressFamily) {
      let listener = {
          let bind_address = IpSocketAddress::localhost(family, 0);
          let listener = TcpSocket::create(family).unwrap();
          listener.bind(bind_address).unwrap();
+         listener.listen().unwrap();
          listener
      };

      let listener_address = listener.get_local_address().unwrap();
      let client = TcpSocket::create(family).unwrap();

      let result = client.bind(listener_address);
      assert!(matches!(result, Err(ErrorCode::AddressInUse)));
  }

The same fixture passes on the WAMR-Zig WASIp3 host

@cataggar I'm not familiar with that runtime, but if it passes this (broken-on-linux) test then I'm surpised it doesn't fail on test_reuseaddr, which tests the opposite side of the coin.


Last updated: Jun 01 2026 at 09:49 UTC