tiran opened issue #4754:
Test Case
Steps to Reproduce
wasmtime run --tcplisten 127.0.0.1:8080 recv.wasmcurl http://localhost:8080Expected Results
I expect that
recv()can read from the fd that has been returned fromaccept.Actual Results
recv()calls always fail withBad file descriptor. Output from my reproducer:accept() on fd 3 ... accept returned fd 4 recv -1 recv(4, ...) failed: Bad file descriptor (8)Versions and Environment
Wasmtime version or commit: 0.39.1 and 418dbc15bd2a5269b338587661387e05fc77b983
Operating system: Linux
Architecture: x86_64
Extra Info
I did a bit of debugging with
rust-gdb. It looks likewasi_common::snapshots::preview_1::sock_recvcallswasi_common::file::WasiFile::sock_recvwhich always fails withbadf.sock_accepton the other hand is provided by wasi_cap_std_sync::net (crates/wasi-common/cap-std-sync/src/net.rs).(gdb) bt #0 wasi_common::file::WasiFile::sock_recv<wasi_cap_std_sync::net::TcpStream> (self=0x555557ab9230, _ri_data=&mut [std::io::IoSliceMut](size=1) = {...}, _ri_flags=...) at crates/wasi-common/src/file.rs:32 #1 0x0000555555c2d01f in wasi_common::snapshots::preview_1::{impl#19}::sock_recv::{async_block#0} () at crates/wasi-common/src/snapshots/preview_1.rs:1268 #2 0x0000555555c5e64c in core::future::from_generator::{impl#1}::poll<wasi_common::snapshots::preview_1::{impl#19}::sock_recv::{async_block_env#0}> (self=..., cx=0x7fffffff5718) at /builddir/build/BUILD/rustc-1.63.0-src/library/core/src/future/mod.rs:91 #3 0x0000555555a0504f in core::future::future::{impl#1}::poll<alloc::boxed::Box<(dyn core::future::future::Future<Output=core::result::Result<(u32, wasi_common::snapshots::preview_1::types::Roflags), anyhow::Error>> + core::marker::Send), alloc::alloc::Global>> (self=..., cx=0x7fffffff5718) at /builddir/build/BUILD/rustc-1.63.0-src/library/core/src/future/future.rs:124 #4 0x0000555555af06d7 in wasi_common::snapshots::preview_1::wasi_snapshot_preview1::sock_recv::{async_block#0}<wasi_common::ctx::WasiCtx> () at crates/wasi-common/src/snapshots/preview_1.rs:21 #5 0x0000555555a713e5 in core::future::from_generator::{impl#1}::poll<wasi_common::snapshots::preview_1::wasi_snapshot_preview1::sock_recv::{async_block_env#0}<wasi_common::ctx::WasiCtx>> (self=..., cx=0x7fffffff5718) at /builddir/build/BUILD/rustc-1.63.0-src/library/core/src/future/mod.rs:9reproducer
#define _POSIX_C_SOURCE 200809L #include <errno.h> #include <netinet/in.h> #include <stdio.h> #include <string.h> #include <sys/socket.h> #include <time.h> #include <unistd.h> #define ACCEPT_FD 3 int main(void) { char buf[128] = {0}; struct sockaddr_in addr; socklen_t addr_size = sizeof(struct sockaddr_in); size_t res; int fd = -1; struct timespec tv = {0, 500 * 1000 * 1000}; printf("accept() on fd %i\n", ACCEPT_FD); while (fd == -1) { fd = accept(ACCEPT_FD, (struct sockaddr *)&addr, &addr_size); nanosleep(&tv, NULL); // hack printf("."); fflush(stdout); } printf("\naccept returned fd %i\n", fd); res = recv(fd, buf, sizeof(buf)-1, 0); printf("recv %zd\n", res); if (res == -1) { printf("recv(%i, ...) failed: %s (%i)\n", fd, strerror(errno), errno); return 2; } else { printf("buf %.*s (%zd)", (int)res, buf, res); return 0; } }
tiran labeled issue #4754:
Test Case
Steps to Reproduce
wasmtime run --tcplisten 127.0.0.1:8080 recv.wasmcurl http://localhost:8080Expected Results
I expect that
recv()can read from the fd that has been returned fromaccept.Actual Results
recv()calls always fail withBad file descriptor. Output from my reproducer:accept() on fd 3 ... accept returned fd 4 recv -1 recv(4, ...) failed: Bad file descriptor (8)Versions and Environment
Wasmtime version or commit: 0.39.1 and 418dbc15bd2a5269b338587661387e05fc77b983
Operating system: Linux
Architecture: x86_64
Extra Info
I did a bit of debugging with
rust-gdb. It looks likewasi_common::snapshots::preview_1::sock_recvcallswasi_common::file::WasiFile::sock_recvwhich always fails withbadf.sock_accepton the other hand is provided by wasi_cap_std_sync::net (crates/wasi-common/cap-std-sync/src/net.rs).(gdb) bt #0 wasi_common::file::WasiFile::sock_recv<wasi_cap_std_sync::net::TcpStream> (self=0x555557ab9230, _ri_data=&mut [std::io::IoSliceMut](size=1) = {...}, _ri_flags=...) at crates/wasi-common/src/file.rs:32 #1 0x0000555555c2d01f in wasi_common::snapshots::preview_1::{impl#19}::sock_recv::{async_block#0} () at crates/wasi-common/src/snapshots/preview_1.rs:1268 #2 0x0000555555c5e64c in core::future::from_generator::{impl#1}::poll<wasi_common::snapshots::preview_1::{impl#19}::sock_recv::{async_block_env#0}> (self=..., cx=0x7fffffff5718) at /builddir/build/BUILD/rustc-1.63.0-src/library/core/src/future/mod.rs:91 #3 0x0000555555a0504f in core::future::future::{impl#1}::poll<alloc::boxed::Box<(dyn core::future::future::Future<Output=core::result::Result<(u32, wasi_common::snapshots::preview_1::types::Roflags), anyhow::Error>> + core::marker::Send), alloc::alloc::Global>> (self=..., cx=0x7fffffff5718) at /builddir/build/BUILD/rustc-1.63.0-src/library/core/src/future/future.rs:124 #4 0x0000555555af06d7 in wasi_common::snapshots::preview_1::wasi_snapshot_preview1::sock_recv::{async_block#0}<wasi_common::ctx::WasiCtx> () at crates/wasi-common/src/snapshots/preview_1.rs:21 #5 0x0000555555a713e5 in core::future::from_generator::{impl#1}::poll<wasi_common::snapshots::preview_1::wasi_snapshot_preview1::sock_recv::{async_block_env#0}<wasi_common::ctx::WasiCtx>> (self=..., cx=0x7fffffff5718) at /builddir/build/BUILD/rustc-1.63.0-src/library/core/src/future/mod.rs:9reproducer
#define _POSIX_C_SOURCE 200809L #include <errno.h> #include <netinet/in.h> #include <stdio.h> #include <string.h> #include <sys/socket.h> #include <time.h> #include <unistd.h> #define ACCEPT_FD 3 int main(void) { char buf[128] = {0}; struct sockaddr_in addr; socklen_t addr_size = sizeof(struct sockaddr_in); size_t res; int fd = -1; struct timespec tv = {0, 500 * 1000 * 1000}; printf("accept() on fd %i\n", ACCEPT_FD); while (fd == -1) { fd = accept(ACCEPT_FD, (struct sockaddr *)&addr, &addr_size); nanosleep(&tv, NULL); // hack printf("."); fflush(stdout); } printf("\naccept returned fd %i\n", fd); res = recv(fd, buf, sizeof(buf)-1, 0); printf("recv %zd\n", res); if (res == -1) { printf("recv(%i, ...) failed: %s (%i)\n", fd, strerror(errno), errno); return 2; } else { printf("buf %.*s (%zd)", (int)res, buf, res); return 0; } }
bjorn3 commented on issue #4754:
I think you have to use
fd_readandfd_writeto read from and write to sockets.
bjorn3 edited a comment on issue #4754:
I think you have to use
readandwriteto read from and write to sockets.
sunfishcode commented on issue #4754:
@bjorn3
sock_recvandsock_sendcorrespond to POSIXrecvandsend, and are expected to work with sockets. It seems likely something else is going on here.
tiran edited issue #4754:
Test Case
Steps to Reproduce
wasmtime run --tcplisten 127.0.0.1:8080 recv.wasmcurl http://localhost:8080Expected Results
I expect that
recv()can read from the fd that has been returned fromaccept.Actual Results
recv()calls always fail withBad file descriptor. Output from my reproducer:accept() on fd 3 ... accept returned fd 4 recv -1 recv(4, ...) failed: Bad file descriptor (8)Versions and Environment
Wasmtime version or commit: 0.39.1 and 418dbc15bd2a5269b338587661387e05fc77b983
Operating system: Linux
Architecture: x86_64
Extra Info
I did a bit of debugging with
rust-gdb. It looks likewasi_common::snapshots::preview_1::sock_recvcallswasi_common::file::WasiFile::sock_recvwhich always fails withbadf.sock_accepton the other hand is provided bywasi_cap_std_sync::net(crates/wasi-common/cap-std-sync/src/net.rs).(gdb) bt #0 wasi_common::file::WasiFile::sock_recv<wasi_cap_std_sync::net::TcpStream> (self=0x555557ab9230, _ri_data=&mut [std::io::IoSliceMut](size=1) = {...}, _ri_flags=...) at crates/wasi-common/src/file.rs:32 #1 0x0000555555c2d01f in wasi_common::snapshots::preview_1::{impl#19}::sock_recv::{async_block#0} () at crates/wasi-common/src/snapshots/preview_1.rs:1268 #2 0x0000555555c5e64c in core::future::from_generator::{impl#1}::poll<wasi_common::snapshots::preview_1::{impl#19}::sock_recv::{async_block_env#0}> (self=..., cx=0x7fffffff5718) at /builddir/build/BUILD/rustc-1.63.0-src/library/core/src/future/mod.rs:91 #3 0x0000555555a0504f in core::future::future::{impl#1}::poll<alloc::boxed::Box<(dyn core::future::future::Future<Output=core::result::Result<(u32, wasi_common::snapshots::preview_1::types::Roflags), anyhow::Error>> + core::marker::Send), alloc::alloc::Global>> (self=..., cx=0x7fffffff5718) at /builddir/build/BUILD/rustc-1.63.0-src/library/core/src/future/future.rs:124 #4 0x0000555555af06d7 in wasi_common::snapshots::preview_1::wasi_snapshot_preview1::sock_recv::{async_block#0}<wasi_common::ctx::WasiCtx> () at crates/wasi-common/src/snapshots/preview_1.rs:21 #5 0x0000555555a713e5 in core::future::from_generator::{impl#1}::poll<wasi_common::snapshots::preview_1::wasi_snapshot_preview1::sock_recv::{async_block_env#0}<wasi_common::ctx::WasiCtx>> (self=..., cx=0x7fffffff5718) at /builddir/build/BUILD/rustc-1.63.0-src/library/core/src/future/mod.rs:9reproducer
#define _POSIX_C_SOURCE 200809L #include <errno.h> #include <netinet/in.h> #include <stdio.h> #include <string.h> #include <sys/socket.h> #include <time.h> #include <unistd.h> #define ACCEPT_FD 3 int main(void) { char buf[128] = {0}; struct sockaddr_in addr; socklen_t addr_size = sizeof(struct sockaddr_in); size_t res; int fd = -1; struct timespec tv = {0, 500 * 1000 * 1000}; printf("accept() on fd %i\n", ACCEPT_FD); while (fd == -1) { fd = accept(ACCEPT_FD, (struct sockaddr *)&addr, &addr_size); nanosleep(&tv, NULL); // hack printf("."); fflush(stdout); } printf("\naccept returned fd %i\n", fd); res = recv(fd, buf, sizeof(buf)-1, 0); printf("recv %zd\n", res); if (res == -1) { printf("recv(%i, ...) failed: %s (%i)\n", fd, strerror(errno), errno); return 2; } else { printf("buf %.*s (%zd)", (int)res, buf, res); return 0; } }
tiran commented on issue #4754:
My example works when I replace
recv()withread(). But it's kinda the wrong API call. Portable applications like Python interpreter userecv()becauseread()does not work with sockets on Windows. Windows treats file handles and socket handles differently.
bjorn3 commented on issue #4754:
@bjorn3 sock_recv and sock_send correspond to POSIX recv and send, and are expected to work with sockets. It seems likely something else is going on here.
Rust's libstd uses fd_read and fd_write for WASI, not sock_recv and sock_send. Wasmtime also doesn't seem to implement sock_recv and sock_send at all.
tiran commented on issue #4754:
I'm using WASI-SDK 16 to compile a portable C application to wasm32-wasi. To be more precise I want to get CPython's socket module working under WASI. It uses
recvandsendto read to and write from a socket.
sunfishcode commented on issue #4754:
Ah, I was mistaken. Wasmtime has some code for
sock_sendandsock_recv, but they're not fully implemented.I've now submitted https://github.com/bytecodealliance/wasmtime/pull/4776 to implement them. With that patch, the reproducer reported above compiles and produces the expected output.
$ target/debug/wasmtime run --tcplisten 127.0.0.1:8080 recv.wasm accept() on fd 3 ..... accept returned fd 4 recv 78 buf GET / HTTP/1.1 Host: localhost:8080 User-Agent: curl/7.81.0 Accept: */* (78)
Last updated: Dec 13 2025 at 19:03 UTC