Stream: git-wasmtime

Topic: wasmtime / issue #12402 SO_REUSEADDR should be set on TCP...


view this post on Zulip Wasmtime GitHub notifications bot (Jan 23 2026 at 14:06):

wingo opened issue #12402:

When writing a test that sockets have SO_REUSEADDR set for wasip3, @saulecabrera ran into a bug that shows up only on Linux (not mac, not windows):

async fn test_reuseaddr(family: IpAddressFamily) {
    let client = TcpSocket::create(family).unwrap();
    let local_addr = {
        let server = TcpSocket::create(family).unwrap();
        let addr = IpSocketAddress::localhost(family, 0);
        server.bind(addr).unwrap();
        let local_addr = server.get_local_address().unwrap();
        let mut accept = server.listen().unwrap();
        join!(
            // Change the state to connected.
            async {
                client.connect(local_addr).await.unwrap();
            },
            async {
                let sock = accept.next().await.unwrap();
                let (mut send_tx, send_rx) = wit_stream::new();
                join!(
                    async {
                        sock.send(send_rx).await.unwrap();
                    },
                    async {
                        let remaining = send_tx.write_all(vec![0; 1]).await;
                        assert!(remaining.is_empty());
                        drop(send_tx);
                    }
                );
            }
        );
        local_addr
    };

    // Immediately try to connect to the same after the connection is
    // dropped.  According to the spec, `SO_REUSEADDR` should be set
    // by default, so the next connection should not be affected by
    // the `TIME_WAIT` state.
    let next = TcpSocket::create(family).unwrap();
    next.bind(local_addr).unwrap();
    next.listen().unwrap();
}

When next is bound to the same local_addr, the bind fails. It is apparently because the first socket server wasn't opened with REUSEADDR, because port is 0 : https://github.com/bytecodealliance/wasmtime/blob/main/crates/wasi/src/sockets/util.rs#L345. If I change the condition on that line to unconditional "true", Saúl's wasi test passes, and no wasmtime test fails.

So, I propose that wasmtime should just always set REUSEADDR (unless we are on a windows system).

Full test here: https://github.com/WebAssembly/wasi-testsuite/pull/205

view this post on Zulip Wasmtime GitHub notifications bot (Jan 23 2026 at 14:06):

wingo added the bug label to Issue #12402.

view this post on Zulip Wasmtime GitHub notifications bot (Jan 23 2026 at 23:30):

alexcrichton commented on issue #12402:

cc @badeend, do you know why this explicitly excludes port 0 from setting reuseaddr? Changing that gets this test passing, and naively I'd expect true to be unconditional there, but you're far more knowledgable about this all than I

view this post on Zulip Wasmtime GitHub notifications bot (Jan 23 2026 at 23:30):

alexcrichton added the wasi label to Issue #12402.

view this post on Zulip Wasmtime GitHub notifications bot (Jan 23 2026 at 23:32):

alexcrichton commented on issue #12402:

Also @rvolosatovs you might be able to shed more light on my above question too

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

badeend commented on issue #12402:

Yes, this is indeed a consequence of the current setup.

For listener sockets I think it's fine to set it unconditionally.
For client sockets I'm not sure.
Let me think about it

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

badeend commented on issue #12402:

I think it should be fine :+1:

PTAL at the two PRs I just opened

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

alexcrichton closed issue #12402:

When writing a test that sockets have SO_REUSEADDR set for wasip3, @saulecabrera ran into a bug that shows up only on Linux (not mac, not windows):

async fn test_reuseaddr(family: IpAddressFamily) {
    let client = TcpSocket::create(family).unwrap();
    let local_addr = {
        let server = TcpSocket::create(family).unwrap();
        let addr = IpSocketAddress::localhost(family, 0);
        server.bind(addr).unwrap();
        let local_addr = server.get_local_address().unwrap();
        let mut accept = server.listen().unwrap();
        join!(
            // Change the state to connected.
            async {
                client.connect(local_addr).await.unwrap();
            },
            async {
                let sock = accept.next().await.unwrap();
                let (mut send_tx, send_rx) = wit_stream::new();
                join!(
                    async {
                        sock.send(send_rx).await.unwrap();
                    },
                    async {
                        let remaining = send_tx.write_all(vec![0; 1]).await;
                        assert!(remaining.is_empty());
                        drop(send_tx);
                    }
                );
            }
        );
        local_addr
    };

    // Immediately try to connect to the same after the connection is
    // dropped.  According to the spec, `SO_REUSEADDR` should be set
    // by default, so the next connection should not be affected by
    // the `TIME_WAIT` state.
    let next = TcpSocket::create(family).unwrap();
    next.bind(local_addr).unwrap();
    next.listen().unwrap();
}

When next is bound to the same local_addr, the bind fails. It is apparently because the first socket server wasn't opened with REUSEADDR, because port is 0 : https://github.com/bytecodealliance/wasmtime/blob/main/crates/wasi/src/sockets/util.rs#L345. If I change the condition on that line to unconditional "true", Saúl's wasi test passes, and no wasmtime test fails.

So, I propose that wasmtime should just always set REUSEADDR (unless we are on a windows system).

Full test here: https://github.com/WebAssembly/wasi-testsuite/pull/205


Last updated: Feb 24 2026 at 05:28 UTC