Stream: git-wasmtime

Topic: wasmtime / issue #9938 WASI socket read returns 0 when it...


view this post on Zulip Wasmtime GitHub notifications bot (Jan 06 2025 at 22:49):

sunfishcode added the bug label to Issue #9938.

view this post on Zulip Wasmtime GitHub notifications bot (Jan 06 2025 at 22:49):

sunfishcode opened issue #9938:

Test Case

Compile this Rust program with the wasm32-wasip2 target and the wasi crate as a dependency:

use wasi::sockets::network::{IpAddressFamily, IpSocketAddress, Ipv4SocketAddress};
use wasi::io::streams::StreamError;
use std::net::Ipv4Addr;

fn main() {
    let network = wasi::sockets::instance_network::instance_network();

    let family = IpAddressFamily::Ipv4;

    let socket = wasi::sockets::tcp_create_socket::create_tcp_socket(family).unwrap();
    let pollable = socket.subscribe();

    let ip = Ipv4Addr::new(127, 0, 0, 1).octets();
    let address = (ip[0], ip[1], ip[2], ip[3]);
    let port = 8080;
    let local_address = IpSocketAddress::Ipv4(Ipv4SocketAddress { port, address });

    socket.start_bind(&network, local_address).unwrap();
    pollable.block();
    socket.finish_bind().unwrap();

    socket.start_listen().unwrap();
    pollable.block();
    socket.finish_listen().unwrap();

    pollable.block();
    let (_socket, input, _output) = socket.accept().unwrap();

    let input_subscription = input.subscribe();

    loop {
        input_subscription.block();

        match input.read(4096) {
            Ok(r) => {
                // We blocked on our input before reading, so there should be
                // at least one byte ready.
                assert!(!r.is_empty(), "read after blocking should return at least one byte");
            }
            Err(StreamError::Closed) => return,
            Err(StreamError::LastOperationFailed(err)) => {
                unreachable!("error! {:?}", std::io::Error::other(err.to_debug_string()));
            }
        };
    }
}

Steps to Reproduce

Run it with wasmtime -Sinherit-network and it'll start a server listening for input on port 8080.

In another terminal, run nc localhost 8080 and type some text and press enter. This causes the server to fail the "read after blocking should return at least one byte" assert.

Expected Results

The server should not panic. read after blocking should produce at least one byte, or fail.

Actual Results

read returns 0 bytes, and trips the assert in the testcase.

Versions and Environment

I can reproduce this on Wasmtime 27.0.0 (8eefa236f 2024-11-20) and on a Wasmtime built from top-of-tree today.

Operating system: Linux

Architecture: x86_64

view this post on Zulip Wasmtime GitHub notifications bot (Jan 06 2025 at 22:51):

sunfishcode commented on issue #9938:

The relevant line in the spec is at https://github.com/WebAssembly/wasi-io/blob/main/wit/streams.wit#L51:

The pollable given by subscribe will be ready when more bytes are available.

The specific issue here is that the pollable reports being ready when zero bytes are available.

view this post on Zulip Wasmtime GitHub notifications bot (Jan 07 2025 at 00:46):

pchickey commented on issue #9938:

Is this issue related? https://github.com/bytecodealliance/wasmtime/pull/9691

view this post on Zulip Wasmtime GitHub notifications bot (Jan 07 2025 at 01:23):

sunfishcode commented on issue #9938:

Yes, that looks very similar.

view this post on Zulip Wasmtime GitHub notifications bot (Jan 22 2025 at 18:18):

alexcrichton closed issue #9938:

Test Case

Compile this Rust program with the wasm32-wasip2 target and the wasi crate as a dependency:

use wasi::sockets::network::{IpAddressFamily, IpSocketAddress, Ipv4SocketAddress};
use wasi::io::streams::StreamError;
use std::net::Ipv4Addr;

fn main() {
    let network = wasi::sockets::instance_network::instance_network();

    let family = IpAddressFamily::Ipv4;

    let socket = wasi::sockets::tcp_create_socket::create_tcp_socket(family).unwrap();
    let pollable = socket.subscribe();

    let ip = Ipv4Addr::new(127, 0, 0, 1).octets();
    let address = (ip[0], ip[1], ip[2], ip[3]);
    let port = 8080;
    let local_address = IpSocketAddress::Ipv4(Ipv4SocketAddress { port, address });

    socket.start_bind(&network, local_address).unwrap();
    pollable.block();
    socket.finish_bind().unwrap();

    socket.start_listen().unwrap();
    pollable.block();
    socket.finish_listen().unwrap();

    pollable.block();
    let (_socket, input, _output) = socket.accept().unwrap();

    let input_subscription = input.subscribe();

    loop {
        input_subscription.block();

        match input.read(4096) {
            Ok(r) => {
                // We blocked on our input before reading, so there should be
                // at least one byte ready.
                assert!(!r.is_empty(), "read after blocking should return at least one byte");
            }
            Err(StreamError::Closed) => return,
            Err(StreamError::LastOperationFailed(err)) => {
                unreachable!("error! {:?}", std::io::Error::other(err.to_debug_string()));
            }
        };
    }
}

Steps to Reproduce

Run it with wasmtime -Sinherit-network and it'll start a server listening for input on port 8080.

In another terminal, run nc localhost 8080 and type some text and press enter. This causes the server to fail the "read after blocking should return at least one byte" assert.

Expected Results

The server should not panic. read after blocking should produce at least one byte, or fail.

Actual Results

read returns 0 bytes, and trips the assert in the testcase.

Versions and Environment

I can reproduce this on Wasmtime 27.0.0 (8eefa236f 2024-11-20) and on a Wasmtime built from top-of-tree today.

Operating system: Linux

Architecture: x86_64

view this post on Zulip Wasmtime GitHub notifications bot (Jan 22 2025 at 18:18):

alexcrichton commented on issue #9938:

I also suspect that https://github.com/bytecodealliance/wasmtime/issues/9667 is the same issue as this. I wrote up some thoughts over there as to why I don't think we can get this program to work because a relatively important perf optimization prevents us from doing so. I'm going to close this issue in favor of https://github.com/bytecodealliance/wasmtime/issues/9667 under the assumption it's a duplicate, but I'm of course happy to continue discussion there (or here)


Last updated: Jan 24 2025 at 00:11 UTC