Stream: wasi

Topic: UDP bind and connect behavior in `wasi-libc`


view this post on Zulip Joel Dice (Feb 13 2026 at 18:17):

To the POSIX experts: I'm adding WASIp2 support to mio and currently triaging some test failures. Along the way, I've found and fixed a few bugs in wasi-libc, but am not sure how to address this one.

That test creates and binds three SOCK_DGRAM sockets, then reconnects them to different addresses than they were bound to. Then it uses the first socket to send a packet to the address which the third socket is bound to but not connected to, expecting the third socket to receive that packet. On wasi-libc and wasmtime-wasi's p2 implementation, this fails because the third socket only receives sockets from the address it was connected to, no the one it was bound to. If I comment out this line (i.e. not connect the third socket), the test passes.

The Linux man page for connect(2) says this:

If the socket sockfd is of type SOCK_DGRAM, then addr is the address to which datagrams are sent by default, and the only address from which datagrams are received.

...which seems to say that wasi-libc/wasmtime-wasi's behavior is correct, and that the mio test is wrong. However, the test does pass on native Linux (i.e. with no Wasm involved) regardless of whether I comment out that one line or not, so Linux seems to be behaving differently than the man page indicates, i.e. the socket is receiving packets for the address it was bound to, not the one it was connected to.

Can anyone comment on what the correct behavior is here? And maybe link to some authoritative document?

view this post on Zulip Joel Dice (Feb 13 2026 at 18:25):

This random SO answer seems to confirm the behavior I'm seeing on Linux, which is that connecting an already-bound UDP socket only affects where packets are sent using that socket, while the address from which packets are received remains the one the socket was bound to.

view this post on Zulip David Lloyd (Feb 13 2026 at 18:28):

https://pubs.opengroup.org/onlinepubs/9799919799/functions/V2_chap02.html#tag_16_10_06 is authoritative IMO (POSIX standard)

view this post on Zulip Joel Dice (Feb 13 2026 at 18:34):

Ah, that's helpful, it reading this helped me realize I was misreading the Linux man page:

...and the only address from which datagrams are received.

I read that as "the address on which datagrams are received", but now I see that's not what it's saying. The opengroup.org page spells it out:

For SOCK_DGRAM sockets, the peer address identifies where all datagrams are sent on subsequent *send*() functions, and limits the remote sender for subsequent *recv*() functions

view this post on Zulip Alex Crichton (Feb 13 2026 at 18:35):

I'm a bit confused on your interpretation of the test here Joel, you say that the test reconnects the sockets to different addresses, but the test is only connecting for the first time? The socket3 is configured to only receive from socket1's address which looks like it should work given the specified behavior of connect?

view this post on Zulip Alex Crichton (Feb 13 2026 at 18:36):

in the test only socket1 is reconnected but socket2 and socket3 stay connected to socket1 the whole time and socket1 then never receives anything

view this post on Zulip Joel Dice (Feb 13 2026 at 18:36):

Yeah, I misinterpreted the "receive from" as "receive on"; i.e. I confused the role of sender address and receiver address for incoming packets.

view this post on Zulip Joel Dice (Feb 13 2026 at 18:38):

In any case, wasi-libc/wasmtime-wasi are not doing the right thing, which clarifies the next steps.

view this post on Zulip Joel Dice (Feb 13 2026 at 19:55):

ok, this seems to be an issue with wasmtime-wasi. I added some logging and discovered that, when you connect a UDP socket for the first time, its local address remains unchanged from what it was bound to. However, when you connect it a second time, the local address changes, even though only the remote address should have changed. On native Linux, the local address always remains the same.

This seems to be related to the wasmtime-wasi code that proactively disconnects by calling rustix::net::connect_unspec before connecting to the new address. This patch "fixes" it, although based on the surrounding comments I don't think it's the right solution:

diff --git a/crates/wasi/src/p2/host/udp.rs b/crates/wasi/src/p2/host/udp.rs
index 1ef8350399..5dd426e736 100644
--- a/crates/wasi/src/p2/host/udp.rs
+++ b/crates/wasi/src/p2/host/udp.rs
@@ -71,9 +71,9 @@ impl udp::HostUdpSocket for WasiSocketsCtxView<'_> {
         //   if there isn't a disconnect in between.

         // Step #1: Disconnect
-        if socket.is_connected() {
-            socket.disconnect()?;
-        }
+        // if socket.is_connected() {
+        //     socket.disconnect()?;
+        // }

         // Step #2: (Re)connect
         if let Some(connect_addr) = remote_address {
diff --git a/crates/wasi/src/sockets/udp.rs b/crates/wasi/src/sockets/udp.rs
index 482c6aa090..ba1e93f18d 100644
--- a/crates/wasi/src/sockets/udp.rs
+++ b/crates/wasi/src/sockets/udp.rs
@@ -162,10 +162,10 @@ impl UdpSocket {
         //   if there isn't a disconnect in between.

         // Step #1: Disconnect
-        if let UdpState::Connected(..) = self.udp_state {
-            udp_disconnect(&self.socket)?;
-            self.udp_state = UdpState::Bound;
-        }
+        // if let UdpState::Connected(..) = self.udp_state {
+        //     udp_disconnect(&self.socket)?;
+        //     self.udp_state = UdpState::Bound;
+        // }
         // Step #2: (Re)connect
         connect(&self.socket, &addr).map_err(|error| match error {
             Errno::AFNOSUPPORT => ErrorCode::InvalidArgument, // See `udp_bind` implementation.

view this post on Zulip Joel Dice (Feb 13 2026 at 19:56):

I'll go ahead and open a Wasmtime issue and we can continue there.


Last updated: Feb 24 2026 at 04:36 UTC