Skip to content

[wasi-sockets] reconnecting UDP socket unexpectedly changes local address #12589

@dicej

Description

@dicej

I've been adding WASIp2 support to mio and triaging failures in its test suite. This test is failing, which I tracked down to wasmtime-wasi spuriously changing the local address of a UDP socket when wasi:sockets/udp#udp-socket.stream is called a second time on the same socket (equivalent to calling connect(2) a second time in POSIX). The test fails because it uses the old local address, not realizing the socket suddenly has a new local address.

Here's a minimal test case:

// udp.rs
use std::{error::Error, net::UdpSocket};

fn main() -> Result<(), Box<dyn Error>> {
    let socket1 = UdpSocket::bind("127.0.0.1:0")?;
    let socket2 = UdpSocket::bind("127.0.0.1:0")?;

    let address1 = socket1.local_addr()?;
    let address2 = socket2.local_addr()?;

    println!(
        "before:\n  \
         socket1 local address: {address1:?}\n  \
         socket2 local address: {address2:?}"
    );

    socket1.connect(address1)?;
    socket1.connect(address2)?;
    socket2.connect(address1)?;

    println!(
        "after:\n  \
         socket1 local address: {address1:?} remote address: {remote1:?}\n  \
         socket2 local address: {address2:?} remote address: {remote2:?}",
        address1 = socket1.local_addr()?,
        address2 = socket2.local_addr()?,
        remote1 = socket1.peer_addr()?,
        remote2 = socket2.peer_addr()?
    );

    let data = b"foobar";
    socket1.send(data)?;

    let mut buffer = [0u8; 20];
    let count = socket2.recv(&mut buffer)?;

    assert_eq!(data, &buffer[..count]);

    println!("success!");

    Ok(())
}

If you build and run that natively on e.g. Linux using rustc udp.rs && ./udp, you'll see it succeed, with socket1's local address remaining unchanged before and after the UdpSocket::connect calls.

If you build and run for WASIp2 using rustc --target=wasm32-wasip2 udp.rs && wasmtime run -Sudp,inherit-network udp.wasm, you'll see it hang indefinitely due to socket1's local address changing due to the second UdpSocket::connect call.

While digging into this, I found that wasmtime-wasi proactively disconnects the socket by calling rustix::net::connect_unspec prior to reconnecting. When I comment out that code, the issue is fixed:

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.

However, per the surrounding comments, that code is there for a reason, so I assume that removing it isn't the right solution. @badeend this seems to be related to #7411, so perhaps you have thoughts?

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugIncorrect behavior in the current implementation that needs fixingwasiIssues pertaining to WASI

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions