Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 87 additions & 11 deletions crates/kernel/src/syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2680,9 +2680,19 @@ pub fn sys_write(
return Err(Errno::EAGAIN);
}
}
// External path: delegate to host
// External path: delegate to host. A stream write that
// fails with EPIPE must also generate SIGPIPE; the host
// only reports the errno, so mirror the POSIX side effect
// in the kernel just like the in-kernel pipe-backed TCP
// path above.
let net_handle = sock.host_net_handle.ok_or(Errno::ENOTCONN)?;
host.host_net_send(net_handle, buf, 0)
match host.host_net_send(net_handle, buf, 0) {
Err(Errno::EPIPE) => {
proc.signals.raise(wasm_posix_shared::signal::SIGPIPE);
Err(Errno::EPIPE)
}
other => other,
}
}
SocketDomain::Unix => {
if sock.send_buf_idx.is_none()
Expand Down Expand Up @@ -6677,10 +6687,26 @@ pub fn sys_send(
SocketDomain::Inet | SocketDomain::Inet6 => {
// Loopback path: use pipe buffers if available
if sock.send_buf_idx.is_some() {
return sys_write(proc, host, fd, buf);
let nosignal = flags & MSG_NOSIGNAL != 0;
let sigpipe_was_pending =
proc.signals.is_pending(wasm_posix_shared::signal::SIGPIPE);
let result = sys_write(proc, host, fd, buf);
// MSG_NOSIGNAL: suppress SIGPIPE raised by write.
if nosignal && !sigpipe_was_pending {
proc.signals.clear(wasm_posix_shared::signal::SIGPIPE);
}
return result;
}
let net_handle = sock.host_net_handle.ok_or(Errno::ENOTCONN)?;
host.host_net_send(net_handle, buf, flags)
match host.host_net_send(net_handle, buf, flags) {
Err(Errno::EPIPE) => {
if flags & MSG_NOSIGNAL == 0 {
proc.signals.raise(wasm_posix_shared::signal::SIGPIPE);
}
Err(Errno::EPIPE)
}
other => other,
}
}
SocketDomain::Unix => {
// DGRAM bit-bucket (syslog pattern): data is discarded
Expand Down Expand Up @@ -11048,6 +11074,11 @@ mod tests {
/// Override for `gl_submit`'s return value (0 = success, negative
/// = errno). Defaults to 0.
gl_submit_rc: i32,
net_connect_result: Result<(), Errno>,
net_connect_status_result: Result<(), Errno>,
net_send_result: Result<usize, Errno>,
net_connect_calls: Vec<(i32, Vec<u8>, u16)>,
net_listen_calls: Vec<(i32, u16, [u8; 4])>,
}

impl MockHostIO {
Expand All @@ -11071,6 +11102,11 @@ mod tests {
gl_unbind_calls: Vec::new(),
gbm_bo_bind_rc: 0,
gl_submit_rc: 0,
net_connect_result: Err(Errno::ECONNREFUSED),
net_connect_status_result: Err(Errno::ECONNREFUSED),
net_send_result: Err(Errno::ENOTCONN),
net_connect_calls: Vec::new(),
net_listen_calls: Vec::new(),
}
}

Expand Down Expand Up @@ -11427,22 +11463,23 @@ mod tests {
}
fn host_net_connect(
&mut self,
_handle: i32,
_addr: &[u8],
_port: u16,
handle: i32,
addr: &[u8],
port: u16,
) -> Result<(), Errno> {
Err(Errno::ECONNREFUSED)
self.net_connect_calls.push((handle, addr.to_vec(), port));
self.net_connect_result
}
fn host_net_connect_status(&mut self, _handle: i32) -> Result<(), Errno> {
Err(Errno::ECONNREFUSED)
self.net_connect_status_result
}
fn host_net_send(
&mut self,
_handle: i32,
_data: &[u8],
_flags: u32,
) -> Result<usize, Errno> {
Err(Errno::ENOTCONN)
self.net_send_result
}
fn host_net_recv(
&mut self,
Expand All @@ -11456,7 +11493,8 @@ mod tests {
fn host_net_close(&mut self, _handle: i32) -> Result<(), Errno> {
Ok(())
}
fn host_net_listen(&mut self, _fd: i32, _port: u16, _addr: &[u8; 4]) -> Result<(), Errno> {
fn host_net_listen(&mut self, fd: i32, port: u16, addr: &[u8; 4]) -> Result<(), Errno> {
self.net_listen_calls.push((fd, port, *addr));
Ok(())
}
fn host_getaddrinfo(&mut self, _name: &[u8], _result: &mut [u8]) -> Result<usize, Errno> {
Expand Down Expand Up @@ -13412,6 +13450,25 @@ mod tests {
assert!(proc.signals.is_pending(wasm_posix_shared::signal::SIGPIPE));
}

#[test]
fn test_write_external_tcp_epipe_raises_sigpipe() {
let mut proc = Process::new(1);
let mut host = MockHostIO::new();
use wasm_posix_shared::socket::*;

host.net_connect_result = Ok(());
host.net_connect_status_result = Ok(());
host.net_send_result = Err(Errno::EPIPE);

let fd = sys_socket(&mut proc, &mut host, AF_INET, SOCK_STREAM, 0).unwrap();
let addr = [2, 0, 0, 80, 93, 184, 216, 34, 0, 0, 0, 0, 0, 0, 0, 0];
sys_connect(&mut proc, &mut host, fd, &addr).unwrap();

let result = sys_write(&mut proc, &mut host, fd, b"test");
assert_eq!(result, Err(Errno::EPIPE));
assert!(proc.signals.is_pending(wasm_posix_shared::signal::SIGPIPE));
}

#[test]
fn test_socketpair_close_both_frees_pipes() {
let mut proc = Process::new(1);
Expand Down Expand Up @@ -13620,6 +13677,25 @@ mod tests {
assert!(!proc.signals.is_pending(wasm_posix_shared::signal::SIGPIPE));
}

#[test]
fn test_send_external_tcp_msg_nosignal_suppresses_sigpipe() {
let mut proc = Process::new(1);
let mut host = MockHostIO::new();
use wasm_posix_shared::socket::*;

host.net_connect_result = Ok(());
host.net_connect_status_result = Ok(());
host.net_send_result = Err(Errno::EPIPE);

let fd = sys_socket(&mut proc, &mut host, AF_INET, SOCK_STREAM, 0).unwrap();
let addr = [2, 0, 0, 80, 93, 184, 216, 34, 0, 0, 0, 0, 0, 0, 0, 0];
sys_connect(&mut proc, &mut host, fd, &addr).unwrap();

let result = sys_send(&mut proc, &mut host, fd, b"test", MSG_NOSIGNAL);
assert_eq!(result, Err(Errno::EPIPE));
assert!(!proc.signals.is_pending(wasm_posix_shared::signal::SIGPIPE));
}

#[test]
fn test_send_not_connected() {
let mut proc = Process::new(1);
Expand Down
Loading