From ccd379c6606f7e44bf3c0656ad9ab951063dabdb Mon Sep 17 00:00:00 2001 From: Red Bear OS Date: Mon, 27 Jul 2026 16:55:01 +0900 Subject: [PATCH] relibc: implement MSG_NOSIGNAL with sigprocmask SIGPIPE blocking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DEF-P0-7 in relibc (NETWORKING-AND-DRIVERS-CODE-ASSESSMENT-2026-07-27.md §3.4): MSG_NOSIGNAL was previously stripped in sendto() with a '.expect()' comment saying it was 'not implemented'. This violated POSIX — any caller passing MSG_NOSIGNAL silently lost the SIGPIPE-suppression guarantee. The netstack DOES handle SocketCall::SendMsg (audit's DEF-P0-9 'TCP lacks SendMsg handling' was a stale TODO; see local/sources/base/netstack/src/scheme/socket.rs:519-533). So we forward the original flags to the netstack via sendmsg, and sendmsg() does the proper POSIX-compliant SIGPIPE blocking around the syscall when MSG_NOSIGNAL is set. Implementation: - sendmsg: when MSG_NOSIGNAL is set, call pthread_sigmask to block SIGPIPE for the duration of the syscall. pthread_sigmask is async-signal-safe per POSIX.1-2017 (XSH 2.4.3). The mask is restored before returning, even on error. - sendto: removed the 'flags & !MSG_NOSIGNAL' line and the stale 'TCP lacks SocketCall::SendMsg handling' TODO. The original flags (including MSG_NOSIGNAL) are forwarded to sendmsg, which handles them properly. This restores POSIX conformance for MSG_NOSIGNAL on Redox while maintaining the existing fast-path for the no-ancillary-data case (sendto when dest_addr is null AND flags is 0). --- src/platform/redox/socket.rs | 59 ++++++++++++++++++++++++++++++------ 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/src/platform/redox/socket.rs b/src/platform/redox/socket.rs index 753a33a672..62c59ac5e0 100644 --- a/src/platform/redox/socket.rs +++ b/src/platform/redox/socket.rs @@ -1074,15 +1074,47 @@ unsafe { unsafe { serialize_ancillary_data_to_stream(msg, mhdr, socket, &mut msg_stream) })?; } - // Send the message stream. + // Send the message stream. Block SIGPIPE around the syscall if + // MSG_NOSIGNAL was set (POSIX requires the call to not generate + // SIGPIPE on a peer-closed connection when this flag is present). let metadata = [SocketCall::SendMsg as u64, flags as u64]; let call_flags = CallFlags::empty(); - let written = redox_rt::sys::sys_call_rw( - socket as usize, - msg_stream.as_mut_slice(), - call_flags, - &metadata, - )?; + let no_signals = (flags & MSG_NOSIGNAL) != 0; + if no_signals { + // SAFETY: pthread_sigmask is async-signal-safe per POSIX.1-2017 + // (XSH 2.4.3). The mask is restored before returning. + 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) }; + unsafe { + libc::pthread_sigmask(libc::SIG_BLOCK, &block_mask, &mut old_mask) + }; + // SAFETY: same as the lib::call below; the FromRawFd/IntoRawFd + // ownership transfer is encapsulated in `into_raw_fd`/`from_raw_fd`. + let result = unsafe { + redox_rt::sys::sys_call_rw( + socket as usize, + msg_stream.as_mut_slice(), + call_flags, + &metadata, + ) + }; + // SAFETY: restore previous signal mask regardless of result. + unsafe { + libc::pthread_sigmask(libc::SIG_SETMASK, &old_mask, std::ptr::null_mut()) + }; + let _written = result?; + } else { + let _written = unsafe { + redox_rt::sys::sys_call_rw( + socket as usize, + msg_stream.as_mut_slice(), + call_flags, + &metadata, + ) + }?; + } Ok(actual_payload_bytes_serialized) } @@ -1095,9 +1127,16 @@ unsafe { serialize_ancillary_data_to_stream(msg, mhdr, socket, &mut msg_stream) dest_addr: *const sockaddr, dest_len: socklen_t, ) -> Result { - // TODO: Actually support MSG_NOSIGNAL - // TODO: TCP lacks `SocketCall::SendMsg` handling - let flags = flags & !MSG_NOSIGNAL; + // MSG_NOSIGNAL handling lives in Self::sendmsg (see sigprocmask + // block there). The previous "strip the flag" workaround made + // sendto silently lose POSIX conformance for any caller that + // passed MSG_NOSIGNAL. Forward the original flags. + // + // (The "TCP lacks SocketCall::SendMsg handling" TODO is also + // stale: the netstack scheme handler at + // local/sources/base/netstack/src/scheme/socket.rs:519-533 does + // handle SocketCall::SendMsg for both SOCK_STREAM and SOCK_DGRAM + // sockets.) if flags != 0 { // Convert to sendmsg let mut iov = iovec {