-
Notifications
You must be signed in to change notification settings - Fork 13.6k
Description
UnixDatagram
sets MSG_NOSIGNAL
when sending data (https://doc.rust-lang.org/src/std/os/unix/net/datagram.rs.html#516)
UnixStream
doesn't have it.
Why it's important?
https://pubs.opengroup.org/onlinepubs/9699919799/functions/send.html:
[EPIPE]
The socket is shut down for writing, or the socket is connection-mode and is no longer connected. In the latter case, and if the socket is of type SOCK_STREAM or SOCK_SEQPACKET and the MSG_NOSIGNAL flag is not set, the SIGPIPE signal is generated to the calling thread.
So without MSG_NOSIGNAL
writing to e.g. closed socket leads to SIGPIPE
.
https://pkg.go.dev/os/signal#hdr-Go_programs_that_use_cgo_or_SWIG
If a SIGPIPE signal is received, the Go program will invoke the special handling described above if the SIGPIPE is received on a Go thread. If the SIGPIPE is received on a non-Go thread the signal will be forwarded to the non-Go handler, if any; if there is none the default system handler will cause the program to terminate.
It means Golang programs using Rust library terminate when underlying Rust library doesn't handle disconnected sockets properly (e.g. keep using closed socket).
Discovered problem with 1.83.0 but code for UnixStream
doesn't seem to ever pass MSG_NOSIGNAL
(so rather not a regression or so).
Apparently old behaviour from #62569 doesn't help neither as Golang runtime doesn't find non-Go signal handler when Rust lib is being used.
MSG_NOSIGNAL
doesn't exist everywhere (e.g. not on macOS) so there we could use SO_NOSIGPIPE
on a socket but it seems it's already done inside:
rust/library/std/src/sys/net/connection/socket/unix.rs
Lines 73 to 112 in 15c4cce
pub fn new_raw(fam: c_int, ty: c_int) -> io::Result<Socket> { | |
unsafe { | |
cfg_if::cfg_if! { | |
if #[cfg(any( | |
target_os = "android", | |
target_os = "dragonfly", | |
target_os = "freebsd", | |
target_os = "illumos", | |
target_os = "hurd", | |
target_os = "linux", | |
target_os = "netbsd", | |
target_os = "openbsd", | |
target_os = "cygwin", | |
target_os = "nto", | |
target_os = "solaris", | |
))] { | |
// On platforms that support it we pass the SOCK_CLOEXEC | |
// flag to atomically create the socket and set it as | |
// CLOEXEC. On Linux this was added in 2.6.27. | |
let fd = cvt(libc::socket(fam, ty | libc::SOCK_CLOEXEC, 0))?; | |
let socket = Socket(FileDesc::from_raw_fd(fd)); | |
// DragonFlyBSD, FreeBSD and NetBSD use `SO_NOSIGPIPE` as a `setsockopt` | |
// flag to disable `SIGPIPE` emission on socket. | |
#[cfg(any(target_os = "freebsd", target_os = "netbsd", target_os = "dragonfly"))] | |
setsockopt(&socket, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1)?; | |
Ok(socket) | |
} else { | |
let fd = cvt(libc::socket(fam, ty, 0))?; | |
let fd = FileDesc::from_raw_fd(fd); | |
fd.set_cloexec()?; | |
let socket = Socket(fd); | |
// macOS and iOS use `SO_NOSIGPIPE` as a `setsockopt` | |
// flag to disable `SIGPIPE` emission on socket. | |
#[cfg(target_vendor = "apple")] | |
setsockopt(&socket, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1)?; | |
Ok(socket) |
Passing MSG_NOSIGNAL
is handled for other types:
- TcpStream -
rust/library/std/src/sys/net/connection/socket.rs
Lines 399 to 405 in 1f76d21
pub fn write(&self, buf: &[u8]) -> io::Result<usize> { let len = cmp::min(buf.len(), <wrlen_t>::MAX as usize) as wrlen_t; let ret = cvt(unsafe { c::send(self.inner.as_raw(), buf.as_ptr() as *const c_void, len, MSG_NOSIGNAL) })?; Ok(ret as usize) } - UdpSocket -
MSG_NOSIGNAL, - UnixDatagram
rust/library/std/src/os/unix/net/datagram.rs
Line 518 in 1f76d21
MSG_NOSIGNAL,
For UnixStream
it's pure write
on underlying file descriptor:
rust/library/std/src/os/unix/net/stream.rs
Line 636 in 1f76d21
self.0.write(buf) pub struct Socket(FileDesc); rust/library/std/src/sys/fd/unix.rs
Line 326 in 1f76d21
libc::write(