relibc: implement MSG_NOSIGNAL with sigprocmask SIGPIPE blocking
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).
This commit is contained in:
@@ -1074,15 +1074,47 @@ unsafe {
|
|||||||
unsafe { serialize_ancillary_data_to_stream(msg, mhdr, socket, &mut msg_stream) })?;
|
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 metadata = [SocketCall::SendMsg as u64, flags as u64];
|
||||||
let call_flags = CallFlags::empty();
|
let call_flags = CallFlags::empty();
|
||||||
let written = redox_rt::sys::sys_call_rw(
|
let no_signals = (flags & MSG_NOSIGNAL) != 0;
|
||||||
socket as usize,
|
if no_signals {
|
||||||
msg_stream.as_mut_slice(),
|
// SAFETY: pthread_sigmask is async-signal-safe per POSIX.1-2017
|
||||||
call_flags,
|
// (XSH 2.4.3). The mask is restored before returning.
|
||||||
&metadata,
|
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)
|
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_addr: *const sockaddr,
|
||||||
dest_len: socklen_t,
|
dest_len: socklen_t,
|
||||||
) -> Result<usize> {
|
) -> Result<usize> {
|
||||||
// TODO: Actually support MSG_NOSIGNAL
|
// MSG_NOSIGNAL handling lives in Self::sendmsg (see sigprocmask
|
||||||
// TODO: TCP lacks `SocketCall::SendMsg` handling
|
// block there). The previous "strip the flag" workaround made
|
||||||
let flags = flags & !MSG_NOSIGNAL;
|
// 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 {
|
if flags != 0 {
|
||||||
// Convert to sendmsg
|
// Convert to sendmsg
|
||||||
let mut iov = iovec {
|
let mut iov = iovec {
|
||||||
|
|||||||
Reference in New Issue
Block a user