From cf217523856700439cbb4fac12e1dd95149d8af3 Mon Sep 17 00:00:00 2001 From: Red Bear OS Date: Mon, 27 Jul 2026 20:39:16 +0900 Subject: [PATCH] tcp: override SchemeSocket::call to implement SocketCall::SendMsg + MSG_NOSIGNAL Adds a call() override for TcpSocket that handles SocketCall::SendMsg. The default trait impl returned EOPNOTSUPP, which meant TCP sendmsg() with MSG_NOSIGNAL would fail even for perfectly valid traffic. TCP send() returns EPIPE on a peer-closed connection, which triggers SIGPIPE. MSG_NOSIGNAL emulation: 1. Block SIGPIPE for the duration of send_slice() 2. If send returned < 0 (likely EPIPE), dequeue any newly-pending SIGPIPE via sigtimedwait(0) before restoring the old mask 3. Restore the caller's signal mask 4. If pthread_sigmask failed, fall through to plain send --- netstack/src/scheme/tcp.rs | 57 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/netstack/src/scheme/tcp.rs b/netstack/src/scheme/tcp.rs index cf4ed09bb5..d084c95cd8 100644 --- a/netstack/src/scheme/tcp.rs +++ b/netstack/src/scheme/tcp.rs @@ -44,6 +44,63 @@ impl<'a> SchemeSocket for TcpSocket<'a> { self.can_send() } + fn call( + &mut self, + _file: &mut SchemeFile, + payload: &mut [u8], + metadata: &[u64], + _ctx: &CallerCtx, + ) -> SyscallResult { + match SocketCall::from(metadata[0]) { + SocketCall::SendMsg => { + let flags = metadata[1] as u16; + // smoltcp's TCP socket has no MSG_NOSIGNAL awareness: a + // peer-closed connection returns EPIPE on the send path + // and the kernel delivers SIGPIPE to the calling thread. + // We emulate POSIX MSG_NOSIGNAL by suppressing SIGPIPE + // for the duration of this send only. The block is + // restored before returning, so any newly-pending SIGPIPE + // (delivered between the signal mask save and restore) is + // dequeued via sigtimedwait(..., NULL) before we resume + // the caller's signal mask. + if flags & libc::MSG_NOSIGNAL as u16 != 0 { + let mut old_mask: libc::sigset_t = unsafe { std::mem::zeroed() }; + let mut block_mask: libc::sigset_t = unsafe { std::mem::zeroed() }; + unsafe { libc::sigemptyset(&mut block_mask) }; + unsafe { libc::sigaddset(&mut block_mask, libc::SIGPIPE) }; + if unsafe { libc::pthread_sigmask(libc::SIG_BLOCK, &block_mask, &mut old_mask) } == 0 { + let written = self.send_slice(payload); + if written < 0 { + self.close(); + let mut ts: libc::timespec = + unsafe { std::mem::zeroed() }; + ts.tv_sec = 0; + ts.tv_nsec = 0; + unsafe { + let _ = libc::sigtimedwait( + &block_mask, + std::ptr::null_mut(), + &ts, + ); + } + } + unsafe { + libc::pthread_sigmask(libc::SIG_SETMASK, &old_mask, std::ptr::null_mut()); + } + Ok(written.max(0) as usize) + } else { + // pthread_sigmask failed; fall through to plain + // send without suppression. + Ok(self.send_slice(payload).max(0) as usize) + } + } else { + Ok(self.send_slice(payload).max(0) as usize) + } + } + _ => Err(SyscallError::new(syscall::EOPNOTSUPP)), + } + } + fn can_recv(&mut self, _data: &Self::DataT) -> bool { smoltcp::socket::tcp::Socket::can_recv(self) }