Fix WASIX socket connect to detect connection failures#6059
Fix WASIX socket connect to detect connection failures#6059
Conversation
Handle POLLERR in poll_write_ready to detect failed TCP connections. When POLLERR is detected, use getsockopt with SO_ERROR to retrieve the actual socket error and propagate it properly. This fixes the issue where socket connections always appeared successful even when they failed (e.g., connection refused). Co-authored-by: zebreus <1557253+zebreus@users.noreply.github.com>
- Use explicit cast to libc::c_void for better clarity - Properly handle getsockopt failure case with last_os_error - Handle edge case where POLLERR is set but SO_ERROR is 0 - Improve error handling robustness with better fallback logic Co-authored-by: zebreus <1557253+zebreus@users.noreply.github.com>
|
@copilot check again |
|
Closing as the PR is not ready. |
|
@theduke wanted to take a look at this PR |
|
@copilot fix CI failures (lints) |
There was a problem hiding this comment.
Pull request overview
Fixes WASIX TCP connect reporting success even when the underlying connection fails (e.g., ECONNREFUSED) by improving error detection in write-readiness polling.
Changes:
- Extend
LocalTcpStream::poll_write_readypolling mask to includePOLLERR. - When
POLLERRis observed, querySO_ERRORviagetsockoptand propagate the resulting error asNetworkError. - Add handling for
getsockoptfailure and the edge case wherePOLLERRis set butSO_ERROR == 0.
Comments suppressed due to low confidence (1)
lib/virtual-net/src/host.rs:756
poll_write_readyonly callsgetsockopt(SO_ERROR)whenPOLLERRis set. For nonblockingconnect(2),poll/selectcommonly reports the socket as writable (POLLOUT) on both success and failure, and the recommended way to distinguish the two is always checkingSO_ERRORonce the fd becomes writable. As-is, a failed connect could still be treated as ready if the OS setsPOLLOUTwithoutPOLLERR. Consider checkingSO_ERRORwheneverreventsincludesPOLLOUT(and/orPOLLERR) before returningOk(10240).
match libc_poll(
stream.as_raw_fd(),
libc::POLLOUT | libc::POLLHUP | libc::POLLERR,
) {
Some(val) if (val & libc::POLLERR) != 0 => {
// Get the actual socket error using SO_ERROR
let mut error: libc::c_int = 0;
let mut len = std::mem::size_of::<libc::c_int>() as libc::socklen_t;
let result = unsafe {
libc::getsockopt(
stream.as_raw_fd(),
libc::SOL_SOCKET,
libc::SO_ERROR,
&mut error as *mut libc::c_int as *mut libc::c_void,
&mut len,
)
};
if result != 0 {
// getsockopt itself failed
let io_error = std::io::Error::last_os_error();
return Poll::Ready(Err(io_err_into_net_error(io_error)));
}
if error != 0 {
// Socket has a pending error
let io_error = std::io::Error::from_raw_os_error(error);
return Poll::Ready(Err(io_err_into_net_error(io_error)));
}
// POLLERR was set but SO_ERROR is 0 - this shouldn't normally happen,
// but we'll treat it as a generic IO error
return Poll::Ready(Err(NetworkError::IOError));
}
Some(val) if (val & libc::POLLHUP) != 0 => {
return Poll::Ready(Ok(0));
}
Some(val) if (val & libc::POLLOUT) != 0 => return Poll::Ready(Ok(10240)),
_ => {}
| match libc_poll( | ||
| stream.as_raw_fd(), | ||
| libc::POLLOUT | libc::POLLHUP | libc::POLLERR, | ||
| ) { | ||
| Some(val) if (val & libc::POLLERR) != 0 => { |
There was a problem hiding this comment.
This change fixes a subtle readiness/connection-error behavior; please add a regression test to cover it. A stable approach is to create a nonblocking TCP socket to a known-closed localhost port (e.g., bind a TcpListener to 127.0.0.1:0, capture the port, drop the listener, then attempt connect), wrap it as LocalTcpStream, and assert poll_write_ready resolves to Err(NetworkError::ConnectionRefused) (or appropriate mapped error) instead of reporting writable.
theduke
left a comment
There was a problem hiding this comment.
NOTE: this is an incomplete/incorrect fix and requires more work.
|
Closing as moved to Issue for now. |
Description
Socket connect operations in WASIX always returned success, even when connections failed (e.g., ECONNREFUSED). This broke applications that probe ports for availability.
Root Cause
poll_write_readyinLocalTcpStreamonly checkedPOLLOUTandPOLLHUPflags. When TCP connections fail, the OS setsPOLLERR, which was being ignored.Changes
POLLERRto poll flags inpoll_write_ready(lib/virtual-net/src/host.rs)POLLERRis detected, retrieve actual error viagetsockopt(SO_ERROR)and propagate asNetworkErrorgetsockoptfailure,POLLERRwithSO_ERROR=0Before:
After:
This follows standard POSIX socket error handling and enables port probing logic.
Warning
Firewall rules blocked me from connecting to one or more addresses (expand for details)
I tried to connect to the following addresses, but was blocked by firewall rules:
registry.wasmer.io/home/REDACTED/work/wasmer/wasmer/target/debug/deps/wasmer_wasix-539a270b3a08905e /home/REDACTED/work/wasmer/wasmer/target/debug/deps/wasmer_wasix-539a270b3a08905e 5-cgu.0.rcgu.o stup/toolchains//home/REDACTED/work/wasmer/wasmer/target/debug/build/blake3-de6d20debuginfo=2 n/rustc -f39�� o ld/syn-f84641bab/home/REDACTED/work/wasmer/wasmer/target/debug/deps/wasmer_derive-feature="std" f/num_enum-0.7.5/src/lib.rs c6597.tracing_test_macro.db42951b32e2e2f8-cgu.0.rcgu.o c6597.tracing_test_macro.db42951b32e2e2f8-cgu.1.rcgu.o own-linux-gnu/li--64 c6597.28uagvhqakf0ge67mb1wav86y.rcgu.o own-�� a8ae0c185c/out/1/tmp/ccDYIUaT.s 6b65.rlib(dns block)If you need me to access, download, or install something from one of these locations, you can either:
Original prompt
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.