WebAssembly / wasi-libc

WASI libc implementation for WebAssembly
https://wasi.dev
Other
860 stars 202 forks source link

wasi-sockets: TCP connect from a Bound state. #540

Closed pavelsavara closed 3 weeks ago

pavelsavara commented 1 month ago

__wasi_sockets_utils__map_error is missing conversion for NETWORK_ERROR_CODE_INVALID_STATE for connect in wasi:socket

Test case

TcpClientSocket_WhenBoundToWildcardAddress_LocalEPChangeToSpecificOnConnect https://github.com/dotnet/runtime/blob/89f245e722eda87869dbb04690cb91361af8174c/src/libraries/System.Net.Sockets/tests/FunctionalTests/LocalEndPointTest.cs#L82-L103

Place to fix https://github.com/WebAssembly/wasi-libc/blob/7d4d3b83fc66c79b3faa5989e67ed2d1042dacaf/libc-bottom-half/sources/sockets_utils.c#L20-L22

Log

  info: [STRT] System.Net.Sockets.Tests.LocalEndPointTestIPv4Task.TcpClientSocket_WhenBoundToWildcardAddress_LocalEPChangeToSpecificOnConnect
{module="streams" function="[method]output-stream.blocking-write-and-flush"}: wasmtime_wasi::bindings::sync::generated::wasi::io::streams: return result=Ok(())
{module="monotonic-clock" function="now"}: wasmtime_wasi::bindings::async_io::wasi::clocks::monotonic_clock: call
{module="monotonic-clock" function="now"}: wasmtime_wasi::bindings::async_io::wasi::clocks::monotonic_clock: return result=Ok(4071713400)
{module="monotonic-clock" function="now"}: wasmtime_wasi::bindings::async_io::wasi::clocks::monotonic_clock: call
{module="monotonic-clock" function="now"}: wasmtime_wasi::bindings::async_io::wasi::clocks::monotonic_clock: return result=Ok(4072435500)
{module="monotonic-clock" function="now"}: wasmtime_wasi::bindings::async_io::wasi::clocks::monotonic_clock: call
{module="monotonic-clock" function="now"}: wasmtime_wasi::bindings::async_io::wasi::clocks::monotonic_clock: return result=Ok(4074574100)
{module="tcp-create-socket" function="create-tcp-socket"}: wasmtime_wasi::bindings::async_io::wasi::sockets::tcp_create_socket: call address_family=IpAddressFamily::Ipv4
{module="tcp-create-socket" function="create-tcp-socket"}: wasmtime_wasi::bindings::async_io::wasi::sockets::tcp_create_socket: return result=Ok(Resource { rep: 6, state: "own (not in table)" })
{module="tcp" function="[method]tcp-socket.subscribe"}: wasmtime_wasi::bindings::sync::generated::wasi::sockets::tcp: call self_=Resource { rep: 6, state: "borrow" }
{module="tcp" function="[method]tcp-socket.subscribe"}: wasmtime_wasi::bindings::sync::generated::wasi::sockets::tcp: return result=Ok(Resource { rep: 7, state: "own (not in table)" })
{module="tcp-create-socket" function="create-tcp-socket"}: wasmtime_wasi::bindings::async_io::wasi::sockets::tcp_create_socket: call address_family=IpAddressFamily::Ipv4
{module="tcp-create-socket" function="create-tcp-socket"}: wasmtime_wasi::bindings::async_io::wasi::sockets::tcp_create_socket: return result=Ok(Resource { rep: 8, state: "own (not in table)" })
{module="tcp" function="[method]tcp-socket.subscribe"}: wasmtime_wasi::bindings::sync::generated::wasi::sockets::tcp: call self_=Resource { rep: 8, state: "borrow" }
{module="tcp" function="[method]tcp-socket.subscribe"}: wasmtime_wasi::bindings::sync::generated::wasi::sockets::tcp: return result=Ok(Resource { rep: 9, state: "own (not in table)" })
{module="tcp" function="[method]tcp-socket.start-bind"}: wasmtime_wasi::bindings::sync::generated::wasi::sockets::tcp: call self_=Resource { rep: 6, state: "borrow" } network=Resource { rep: 4, state: "borrow" } local_address=IpSocketAddress::Ipv4(Ipv4SocketAddress { port: 0, address: (0, 0, 0, 0) })
{module="tcp" function="[method]tcp-socket.start-bind"}: wasmtime_wasi::bindings::sync::generated::wasi::sockets::tcp: return result=Ok(())
{module="tcp" function="[method]tcp-socket.finish-bind"}: wasmtime_wasi::bindings::sync::generated::wasi::sockets::tcp: call self_=Resource { rep: 6, state: "borrow" }
{module="tcp" function="[method]tcp-socket.finish-bind"}: wasmtime_wasi::bindings::sync::generated::wasi::sockets::tcp: return result=Ok(())
{module="tcp" function="[method]tcp-socket.local-address"}: wasmtime_wasi::bindings::sync::generated::wasi::sockets::tcp: call self_=Resource { rep: 6, state: "borrow" }
{module="tcp" function="[method]tcp-socket.local-address"}: wasmtime_wasi::bindings::sync::generated::wasi::sockets::tcp: return result=Ok(IpSocketAddress::Ipv4(Ipv4SocketAddress { port: 60558, address: (0, 0, 0, 0) }))
{module="tcp" function="[method]tcp-socket.start-bind"}: wasmtime_wasi::bindings::sync::generated::wasi::sockets::tcp: call self_=Resource { rep: 8, state: "borrow" } network=Resource { rep: 4, state: "borrow" } local_address=IpSocketAddress::Ipv4(Ipv4SocketAddress { port: 0, address: (0, 0, 0, 0) })
{module="tcp" function="[method]tcp-socket.start-bind"}: wasmtime_wasi::bindings::sync::generated::wasi::sockets::tcp: return result=Ok(())
{module="tcp" function="[method]tcp-socket.finish-bind"}: wasmtime_wasi::bindings::sync::generated::wasi::sockets::tcp: call self_=Resource { rep: 8, state: "borrow" }
{module="tcp" function="[method]tcp-socket.finish-bind"}: wasmtime_wasi::bindings::sync::generated::wasi::sockets::tcp: return result=Ok(())
{module="tcp" function="[method]tcp-socket.local-address"}: wasmtime_wasi::bindings::sync::generated::wasi::sockets::tcp: call self_=Resource { rep: 8, state: "borrow" }
{module="tcp" function="[method]tcp-socket.local-address"}: wasmtime_wasi::bindings::sync::generated::wasi::sockets::tcp: return result=Ok(IpSocketAddress::Ipv4(Ipv4SocketAddress { port: 60559, address: (0, 0, 0, 0) }))
{module="tcp" function="[method]tcp-socket.set-listen-backlog-size"}: wasmtime_wasi::bindings::sync::generated::wasi::sockets::tcp: call self_=Resource { rep: 6, state: "borrow" } value=2147483647
{module="tcp" function="[method]tcp-socket.set-listen-backlog-size"}: wasmtime_wasi::bindings::sync::generated::wasi::sockets::tcp: return result=Ok(())
{module="tcp" function="[method]tcp-socket.start-listen"}: wasmtime_wasi::bindings::sync::generated::wasi::sockets::tcp: call self_=Resource { rep: 6, state: "borrow" }
{module="tcp" function="[method]tcp-socket.start-listen"}: wasmtime_wasi::bindings::sync::generated::wasi::sockets::tcp: return result=Ok(())
{module="tcp" function="[method]tcp-socket.finish-listen"}: wasmtime_wasi::bindings::sync::generated::wasi::sockets::tcp: call self_=Resource { rep: 6, state: "borrow" }
{module="tcp" function="[method]tcp-socket.finish-listen"}: wasmtime_wasi::bindings::sync::generated::wasi::sockets::tcp: return result=Ok(())
{module="tcp" function="[method]tcp-socket.accept"}: wasmtime_wasi::bindings::sync::generated::wasi::sockets::tcp: call self_=Resource { rep: 6, state: "borrow" }
{module="tcp" function="[method]tcp-socket.accept"}: wasmtime_wasi::bindings::sync::generated::wasi::sockets::tcp: return result=Err(would-block (error 8))
{module="terminal-stdout" function="get-terminal-stdout"}: wasmtime_wasi::bindings::async_io::wasi::cli::terminal_stdout: call
{module="terminal-stdout" function="get-terminal-stdout"}: wasmtime_wasi::bindings::async_io::wasi::cli::terminal_stdout: return result=Ok(None)
{module="streams" function="[method]output-stream.blocking-write-and-flush"}: wasmtime_wasi::bindings::sync::generated::wasi::io::streams: call self_=Resource { rep: 1, state: "borrow" } contents=[82, 101, 103, 105, 115, 116, 101, 114, 87, 97, 115, 105, 80, 111, 108, 108, 72, 111, 111, 107, 32, 65, 10]
{module="streams" function="[method]output-stream.blocking-write-and-flush"}: wasmtime_wasi::bindings::sync::generated::wasi::io::streams: return result=Ok(())
  info: RegisterWasiPollHook A
{module="streams" function="[method]output-stream.blocking-write-and-flush"}: wasmtime_wasi::bindings::sync::generated::wasi::io::streams: call self_=Resource { rep: 1, state: "borrow" } contents=[83, 99, 104, 101, 100, 117, 108, 101, 67, 104, 101, 99, 107, 32, 65, 10]
{module="streams" function="[method]output-stream.blocking-write-and-flush"}: wasmtime_wasi::bindings::sync::generated::wasi::io::streams: return result=Ok(())
  info: ScheduleCheck A
{module="streams" function="[method]output-stream.blocking-write-and-flush"}: wasmtime_wasi::bindings::sync::generated::wasi::io::streams: call self_=Resource { rep: 1, state: "borrow" } contents=[83, 99, 104, 101, 100, 117, 108, 101, 67, 104, 101, 99, 107, 32, 66, 10]
  info: ScheduleCheck B
{module="streams" function="[method]output-stream.blocking-write-and-flush"}: wasmtime_wasi::bindings::sync::generated::wasi::io::streams: return result=Ok(())
{module="tcp" function="[method]tcp-socket.start-connect"}: wasmtime_wasi::bindings::sync::generated::wasi::sockets::tcp: call self_=Resource { rep: 8, state: "borrow" } network=Resource { rep: 4, state: "borrow" } remote_address=IpSocketAddress::Ipv4(Ipv4SocketAddress { port: 60558, address: (127, 0, 0, 1) })
{module="tcp" function="[method]tcp-socket.start-connect"}: wasmtime_wasi::bindings::sync::generated::wasi::sockets::tcp: return result=Err(invalid-state (error 9))
  info: Error: failed to run main module `dotnet.wasm`
  info: 
  info: Caused by:
  info:     0: failed to invoke `run` function
  info:     1: error while executing at wasm backtrace:
  info:            0: 0x5dc0c5 - abort
  info:                            at C:\wasi-sdk\sysroot\wasi-libc-wasm32-wasip2\libc-bottom-half/sources\abort.c:5:5
  info:            1: 0x5e0a6d - __wasi_sockets_utils__map_error
  info:                            at C:\wasi-sdk\sysroot\wasi-libc-wasm32-wasip2\libc-bottom-half/sources\sockets_utils.c
  info:            2: 0x5dd1dd - tcp_connect
  info:                            at C:\wasi-sdk\sysroot\wasi-libc-wasm32-wasip2\libc-bottom-half/sources\connect.c:44:11              - connect
  info:                            at C:\wasi-sdk\sysroot\wasi-libc-wasm32-wasip2\libc-bottom-half/sources\connect.c:190:10
  info:            3: 0x48580e - SystemNative_Connect
  info:                            at C:\Dev\runtime\artifacts\obj\native\net9.0-wasi-Debug-wasm\C:/Dev/runtime/src/native/libs/System.Native\pal_networking.c:1759:19
  info:            4: 0x29dad - do_icall
  info:                            at C:\Dev\runtime\artifacts\obj\mono\wasi.wasm.Debug\C:/Dev/runtime/src/mono/mono/mini/interp\interp.c:2402:20
  info:           23: 0x2f3c65 - mono_runtime_run_main
  info:                            at C:\Dev\runtime\artifacts\obj\mono\wasi.wasm.Debug\C:/Dev/runtime/src/mono/mono/metadata\object.c:4343:9
  info:           24: 0xd3e4 - __main_argc_argv
  info:                            at C:\Dev\runtime\artifacts\bin\native\net9.0-wasi-Debug-wasm\C:/Dev/runtime/src/mono/wasi/runtime\main.c:108:12
  info:           25: 0x5dbb0f - .tmpFt70P5!__main_void
  info:           26: 0xbb9c - _start
  info:                            at C:\wasi-sdk\sysroot\wasi-libc-wasm32-wasip2\libc-bottom-half/crt\crt1-command.c:43:13
  info:           27: 0xdfa16b - wit-component:adapter:wasi_snapshot_preview1!wasi:cli/run@0.2.0#run
  info:     2: wasm trap: wasm `unreachable` instruction executed
abrown commented 1 month ago

cc: @badeend, @dicej

badeend commented 1 month ago

INVALID_STATE is already mapped:

    case NETWORK_ERROR_CODE_INVALID_STATE:
    case NETWORK_ERROR_CODE_NOT_IN_PROGRESS:
        abort(); // If our internal state checks are working right, these errors should never show up.
        break;

Given that wasi-libc correctly follows the state diagram this behavior is the correct thing to do, IMO.

It's wasmtime that's at fault here. It (and every other engine) should either: