-
Notifications
You must be signed in to change notification settings - Fork 13.9k
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(