From b45d492c2fad45f5ac3381c1ec5008050db07372 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Thu, 23 Jan 2025 17:45:38 -0800 Subject: [PATCH 01/10] Add a new `ReturnFlags` type for flags returned from `recvmsg` `RecvMsgReturn`'s `flags` field was previously `RecvFlags`, however `recvmsg` returns a different set of flags than that. To address that, add a new type, `ReturnFlags`, which contains the flags that are returned from `recvmsg`. Fixes #1287. --- src/backend/libc/net/send_recv.rs | 32 +++++++- src/backend/libc/net/syscalls.rs | 4 +- src/backend/linux_raw/c.rs | 12 +-- src/backend/linux_raw/net/send_recv.rs | 27 ++++++ src/backend/linux_raw/net/syscalls.rs | 4 +- src/net/send_recv/mod.rs | 2 +- src/net/send_recv/msg.rs | 9 +- tests/net/recv_trunc.rs | 109 ++++++++++++++++++++++++- tests/net/unix.rs | 73 ++++++++++------- tests/net/unix_alloc.rs | 61 ++++++++------ tests/net/v4.rs | 4 +- tests/net/v6.rs | 4 +- 12 files changed, 269 insertions(+), 72 deletions(-) diff --git a/src/backend/libc/net/send_recv.rs b/src/backend/libc/net/send_recv.rs index fb0740c21..6a3d5ad05 100644 --- a/src/backend/libc/net/send_recv.rs +++ b/src/backend/libc/net/send_recv.rs @@ -63,6 +63,7 @@ bitflags! { #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct RecvFlags: u32 { + /// `MSG_CMSG_CLOEXEC` #[cfg(not(any( apple, solarish, @@ -73,7 +74,6 @@ bitflags! { target_os = "nto", target_os = "vita", )))] - /// `MSG_CMSG_CLOEXEC` const CMSG_CLOEXEC = bitcast!(c::MSG_CMSG_CLOEXEC); /// `MSG_DONTWAIT` #[cfg(not(windows))] @@ -104,3 +104,33 @@ bitflags! { const _ = !0; } } + +bitflags! { + /// `MSG_*` flags returned from [`recvmsg`], in the `flags` field of + /// [`RecvMsgReturn`] + /// + /// [`recvmsg`]: crate::net::recvmsg + /// [`RecvMsgReturn`]: crate::net::RecvMsgReturn + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct ReturnFlags: u32 { + /// `MSG_OOB` + const OOB = bitcast!(c::MSG_OOB); + /// `MSG_EOR` + const EOR = bitcast!(c::MSG_EOR); + /// `MSG_TRUNC` + const TRUNC = bitcast!(c::MSG_TRUNC); + /// `MSG_CTRUNC` + const CTRUNC = bitcast!(c::MSG_CTRUNC); + + /// `MSG_CMSG_CLOEXEC` + #[cfg(linux_kernel)] + const CMSG_CLOEXEC = bitcast!(c::MSG_CMSG_CLOEXEC); + /// `MSG_ERRQUEUE` + #[cfg(linux_kernel)] + const ERRQUEUE = bitcast!(c::MSG_ERRQUEUE); + + /// + const _ = !0; + } +} diff --git a/src/backend/libc/net/syscalls.rs b/src/backend/libc/net/syscalls.rs index 3013f9922..deb12c366 100644 --- a/src/backend/libc/net/syscalls.rs +++ b/src/backend/libc/net/syscalls.rs @@ -30,7 +30,7 @@ use { #[cfg(not(any(target_os = "redox", target_os = "wasi")))] use { super::read_sockaddr::{initialize_family_to_unspec, maybe_read_sockaddr_os, read_sockaddr_os}, - super::send_recv::{RecvFlags, SendFlags}, + super::send_recv::{RecvFlags, ReturnFlags, SendFlags}, super::write_sockaddr::{encode_sockaddr_v4, encode_sockaddr_v6}, crate::net::{AddressFamily, Protocol, Shutdown, SocketFlags, SocketType}, core::ptr::null_mut, @@ -344,7 +344,7 @@ pub(crate) fn recvmsg( RecvMsgReturn { bytes, address: addr, - flags: RecvFlags::from_bits_retain(bitcast!(msghdr.msg_flags)), + flags: ReturnFlags::from_bits_retain(bitcast!(msghdr.msg_flags)), } }) }) diff --git a/src/backend/linux_raw/c.rs b/src/backend/linux_raw/c.rs index 92709c17a..d1aa6e0f0 100644 --- a/src/backend/linux_raw/c.rs +++ b/src/backend/linux_raw/c.rs @@ -71,12 +71,12 @@ pub(crate) use linux_raw_sys::{ IPV6_MULTICAST_LOOP, IPV6_RECVTCLASS, IPV6_TCLASS, IPV6_UNICAST_HOPS, IPV6_V6ONLY, IP_ADD_MEMBERSHIP, IP_ADD_SOURCE_MEMBERSHIP, IP_DROP_MEMBERSHIP, IP_DROP_SOURCE_MEMBERSHIP, IP_FREEBIND, IP_MULTICAST_LOOP, IP_MULTICAST_TTL, IP_RECVTOS, IP_TOS, IP_TTL, - MSG_CMSG_CLOEXEC, MSG_CONFIRM, MSG_DONTROUTE, MSG_DONTWAIT, MSG_EOR, MSG_ERRQUEUE, - MSG_MORE, MSG_NOSIGNAL, MSG_OOB, MSG_PEEK, MSG_TRUNC, MSG_WAITALL, SCM_CREDENTIALS, - SCM_RIGHTS, SHUT_RD, SHUT_RDWR, SHUT_WR, SOCK_DGRAM, SOCK_RAW, SOCK_RDM, SOCK_SEQPACKET, - SOCK_STREAM, SOL_SOCKET, SOL_XDP, SO_ACCEPTCONN, SO_BROADCAST, SO_COOKIE, SO_DOMAIN, - SO_ERROR, SO_INCOMING_CPU, SO_KEEPALIVE, SO_LINGER, SO_OOBINLINE, SO_ORIGINAL_DST, - SO_PASSCRED, SO_PROTOCOL, SO_RCVBUF, SO_RCVBUFFORCE, SO_RCVTIMEO_NEW, + MSG_CMSG_CLOEXEC, MSG_CONFIRM, MSG_CTRUNC, MSG_DONTROUTE, MSG_DONTWAIT, MSG_EOR, + MSG_ERRQUEUE, MSG_MORE, MSG_NOSIGNAL, MSG_OOB, MSG_PEEK, MSG_TRUNC, MSG_WAITALL, + SCM_CREDENTIALS, SCM_RIGHTS, SHUT_RD, SHUT_RDWR, SHUT_WR, SOCK_DGRAM, SOCK_RAW, SOCK_RDM, + SOCK_SEQPACKET, SOCK_STREAM, SOL_SOCKET, SOL_XDP, SO_ACCEPTCONN, SO_BROADCAST, SO_COOKIE, + SO_DOMAIN, SO_ERROR, SO_INCOMING_CPU, SO_KEEPALIVE, SO_LINGER, SO_OOBINLINE, + SO_ORIGINAL_DST, SO_PASSCRED, SO_PROTOCOL, SO_RCVBUF, SO_RCVBUFFORCE, SO_RCVTIMEO_NEW, SO_RCVTIMEO_NEW as SO_RCVTIMEO, SO_RCVTIMEO_OLD, SO_REUSEADDR, SO_REUSEPORT, SO_SNDBUF, SO_SNDTIMEO_NEW, SO_SNDTIMEO_NEW as SO_SNDTIMEO, SO_SNDTIMEO_OLD, SO_TYPE, TCP_CONGESTION, TCP_CORK, TCP_KEEPCNT, TCP_KEEPIDLE, TCP_KEEPINTVL, TCP_NODELAY, TCP_QUICKACK, diff --git a/src/backend/linux_raw/net/send_recv.rs b/src/backend/linux_raw/net/send_recv.rs index aa175536a..12a1cfef0 100644 --- a/src/backend/linux_raw/net/send_recv.rs +++ b/src/backend/linux_raw/net/send_recv.rs @@ -58,3 +58,30 @@ bitflags! { const _ = !0; } } + +bitflags! { + /// `MSG_*` flags returned from [`recvmsg`], in the `flags` field of + /// [`RecvMsgReturn`] + /// + /// [`recvmsg`]: crate::net::recvmsg + /// [`RecvMsgReturn`]: crate::net::RecvMsgReturn + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct ReturnFlags: u32 { + /// `MSG_OOB` + const OOB = c::MSG_OOB; + /// `MSG_EOR` + const EOR = c::MSG_EOR; + /// `MSG_TRUNC` + const TRUNC = c::MSG_TRUNC; + /// `MSG_CTRUNC` + const CTRUNC = c::MSG_CTRUNC; + /// `MSG_ERRQUEUE` + const ERRQUEUE = c::MSG_ERRQUEUE; + /// `MSG_CMSG_CLOEXEC` + const CMSG_CLOEXEC = c::MSG_CMSG_CLOEXEC; + + /// + const _ = !0; + } +} diff --git a/src/backend/linux_raw/net/syscalls.rs b/src/backend/linux_raw/net/syscalls.rs index 213ac9c4a..10737643a 100644 --- a/src/backend/linux_raw/net/syscalls.rs +++ b/src/backend/linux_raw/net/syscalls.rs @@ -11,7 +11,7 @@ use super::msghdr::{ with_noaddr_msghdr, with_recv_msghdr, with_unix_msghdr, with_v4_msghdr, with_v6_msghdr, }; use super::read_sockaddr::{initialize_family_to_unspec, maybe_read_sockaddr_os, read_sockaddr_os}; -use super::send_recv::{RecvFlags, SendFlags}; +use super::send_recv::{RecvFlags, ReturnFlags, SendFlags}; #[cfg(target_os = "linux")] use super::write_sockaddr::encode_sockaddr_xdp; use super::write_sockaddr::{encode_sockaddr_v4, encode_sockaddr_v6}; @@ -293,7 +293,7 @@ pub(crate) fn recvmsg( RecvMsgReturn { bytes, address: addr, - flags: RecvFlags::from_bits_retain(msghdr.msg_flags), + flags: ReturnFlags::from_bits_retain(msghdr.msg_flags), } }) }) diff --git a/src/net/send_recv/mod.rs b/src/net/send_recv/mod.rs index e10b81db8..2a4d1db35 100644 --- a/src/net/send_recv/mod.rs +++ b/src/net/send_recv/mod.rs @@ -13,7 +13,7 @@ use backend::fd::{AsFd, BorrowedFd}; use core::cmp::min; use core::mem::MaybeUninit; -pub use backend::net::send_recv::{RecvFlags, SendFlags}; +pub use backend::net::send_recv::{RecvFlags, ReturnFlags, SendFlags}; #[cfg(not(any( windows, diff --git a/src/net/send_recv/msg.rs b/src/net/send_recv/msg.rs index 7456e7e86..5d8785e83 100644 --- a/src/net/send_recv/msg.rs +++ b/src/net/send_recv/msg.rs @@ -15,7 +15,7 @@ use core::mem::{align_of, size_of, size_of_val, take}; use core::ptr::addr_of; use core::{ptr, slice}; -use super::{RecvFlags, SendFlags, SocketAddrAny, SocketAddrV4, SocketAddrV6}; +use super::{RecvFlags, ReturnFlags, SendFlags, SocketAddrAny, SocketAddrV4, SocketAddrV6}; /// Macro for defining the amount of space to allocate in a buffer for use with /// [`RecvAncillaryBuffer::new`] and [`SendAncillaryBuffer::new`]. @@ -817,12 +817,17 @@ pub fn recvmsg( } /// The result of a successful [`recvmsg`] call. +#[derive(Debug)] pub struct RecvMsgReturn { /// The number of bytes received. + /// + /// When `RecvFlags::TRUNC` is in use, this may be greater than the + /// length of the buffer, as it reflects the number of bytes received + /// before truncation into the buffer. pub bytes: usize, /// The flags received. - pub flags: RecvFlags, + pub flags: ReturnFlags, /// The address of the socket we received from, if any. pub address: Option, diff --git a/tests/net/recv_trunc.rs b/tests/net/recv_trunc.rs index c5b10468e..814883c48 100644 --- a/tests/net/recv_trunc.rs +++ b/tests/net/recv_trunc.rs @@ -1,4 +1,5 @@ -use rustix::net::{AddressFamily, RecvFlags, SendFlags, SocketAddrUnix, SocketType}; +use rustix::io::IoSliceMut; +use rustix::net::{AddressFamily, RecvFlags, ReturnFlags, SendFlags, SocketAddrUnix, SocketType}; use std::mem::MaybeUninit; /// Test `recv_uninit` with the `RecvFlags::Trunc` flag. @@ -17,7 +18,6 @@ fn net_recv_uninit_trunc() { let request = b"Hello, World!!!"; let n = rustix::net::sendto_unix(&sender, request, SendFlags::empty(), &name).expect("send"); assert_eq!(n, request.len()); - drop(sender); let mut response = [MaybeUninit::::zeroed(); 5]; let (init, uninit) = @@ -26,4 +26,109 @@ fn net_recv_uninit_trunc() { // We used the `TRUNC` flag, so we should have only gotten 5 bytes. assert_eq!(init, b"Hello"); assert!(uninit.is_empty()); + + // Send the message again. + let n = rustix::net::sendto_unix(&sender, request, SendFlags::empty(), &name).expect("send"); + assert_eq!(n, request.len()); + + // This time receive it without `TRUNC`. This should fail. + let mut response = [MaybeUninit::::zeroed(); 5]; + let (init, uninit) = rustix::net::recv_uninit(&receiver, &mut response, RecvFlags::empty()) + .expect("recv_uninit"); + + // We didn't use the `TRUNC` flag, so we should have received 15 bytes, + // truncated to 5 bytes. + assert_eq!(init, b"Hello"); + assert!(uninit.is_empty()); +} + +/// Test `recvmsg` with the `RecvFlags::Trunc` flag. +#[test] +fn net_recvmsg_trunc() { + crate::init(); + + let tmpdir = tempfile::tempdir().unwrap(); + let path = tmpdir.path().join("recv_uninit_trunc"); + let name = SocketAddrUnix::new(&path).unwrap(); + + let receiver = rustix::net::socket(AddressFamily::UNIX, SocketType::DGRAM, None).unwrap(); + rustix::net::bind_unix(&receiver, &name).expect("bind"); + + let sender = rustix::net::socket(AddressFamily::UNIX, SocketType::DGRAM, None).unwrap(); + let request = b"Hello, World!!!"; + let n = rustix::net::sendto_unix(&sender, request, SendFlags::empty(), &name).expect("send"); + assert_eq!(n, request.len()); + + let mut response = [0_u8; 5]; + let result = rustix::net::recvmsg( + &receiver, + &mut [IoSliceMut::new(&mut response)], + &mut Default::default(), + RecvFlags::TRUNC, + ) + .expect("recvmsg"); + + // We used the `TRUNC` flag, so we should have received 15 bytes, + // truncated to 5 bytes, and the `TRUNC` flag should have been returned. + assert_eq!(&response, b"Hello"); + assert_eq!(result.bytes, 15); + assert_eq!(result.flags, ReturnFlags::TRUNC); + + // Send the message again. + let n = rustix::net::sendto_unix(&sender, request, SendFlags::empty(), &name).expect("send"); + assert_eq!(n, request.len()); + + // This time receive it with `TRUNC` and a big enough buffer. + let mut response = [0_u8; 30]; + let result = rustix::net::recvmsg( + &receiver, + &mut [IoSliceMut::new(&mut response)], + &mut Default::default(), + RecvFlags::TRUNC, + ) + .expect("recvmsg"); + + // We used the `TRUNC` flag, so we should have received 15 bytes + // and the buffer was big enough so the `TRUNC` flag should not have + // been returned. + assert_eq!(&response[..result.bytes], request); + assert_eq!(result.flags, ReturnFlags::empty()); + + // Send the message again. + let n = rustix::net::sendto_unix(&sender, request, SendFlags::empty(), &name).expect("send"); + assert_eq!(n, request.len()); + + // This time receive it without `TRUNC` but a big enough buffer. + let mut response = [0_u8; 30]; + let result = rustix::net::recvmsg( + &receiver, + &mut [IoSliceMut::new(&mut response)], + &mut Default::default(), + RecvFlags::empty(), + ) + .expect("recvmsg"); + + // We used the `TRUNC` flag, so we should have received 15 bytes, + // truncated to 5 bytes, and the `TRUNC` flag should have been returned. + assert_eq!(&response[..result.bytes], request); + assert_eq!(result.flags, ReturnFlags::empty()); + + // Send the message again. + let n = rustix::net::sendto_unix(&sender, request, SendFlags::empty(), &name).expect("send"); + assert_eq!(n, request.len()); + + // This time receive it without `TRUNC` and a small buffer. + let mut response = [0_u8; 5]; + let result = rustix::net::recvmsg( + &receiver, + &mut [IoSliceMut::new(&mut response)], + &mut Default::default(), + RecvFlags::empty(), + ) + .expect("recvmsg"); + + // We didn't use the `TRUNC` flag, so we should have received 15 bytes, + // truncated to 5 bytes, and the `TRUNC` flag should have been returned. + assert_eq!(&response[..result.bytes], b"Hello"); + assert_eq!(result.flags, ReturnFlags::TRUNC); } diff --git a/tests/net/unix.rs b/tests/net/unix.rs index a84b79d26..cdf389599 100644 --- a/tests/net/unix.rs +++ b/tests/net/unix.rs @@ -129,7 +129,7 @@ fn test_unix() { #[cfg(not(any(target_os = "espidf", target_os = "redox", target_os = "wasi")))] fn do_test_unix_msg(addr: SocketAddrUnix) { use rustix::io::{IoSlice, IoSliceMut}; - use rustix::net::{recvmsg, sendmsg, RecvFlags, SendFlags}; + use rustix::net::{recvmsg, sendmsg, RecvFlags, ReturnFlags, SendFlags}; let server = { let connection_socket = socket(AddressFamily::UNIX, SocketType::SEQPACKET, None).unwrap(); @@ -142,14 +142,16 @@ fn do_test_unix_msg(addr: SocketAddrUnix) { let data_socket = accept(&connection_socket).unwrap(); let mut sum = 0; loop { - let nread = recvmsg( + let result = recvmsg( &data_socket, &mut [IoSliceMut::new(&mut buffer)], &mut Default::default(), RecvFlags::empty(), ) - .unwrap() - .bytes; + .unwrap(); + let nread = result.bytes; + + assert_eq!(result.flags, ReturnFlags::empty()); if &buffer[..nread] == b"exit" { break 'exit; @@ -210,6 +212,7 @@ fn do_test_unix_msg(addr: SocketAddrUnix) { RecvFlags::empty(), ) .unwrap(); + assert_eq!(result.flags, ReturnFlags::empty()); let nread = result.bytes; assert_eq!( i32::from_str(&String::from_utf8_lossy(&buffer[..nread])).unwrap(), @@ -259,7 +262,7 @@ fn do_test_unix_msg(addr: SocketAddrUnix) { #[cfg(not(any(target_os = "espidf", target_os = "redox", target_os = "wasi")))] fn do_test_unix_msg_unconnected(addr: SocketAddrUnix) { use rustix::io::{IoSlice, IoSliceMut}; - use rustix::net::{recvmsg, sendmsg_unix, RecvFlags, SendFlags}; + use rustix::net::{recvmsg, sendmsg_unix, RecvFlags, ReturnFlags, SendFlags}; let server = { let runs: &[i32] = &[3, 184, 187, 0]; @@ -271,16 +274,17 @@ fn do_test_unix_msg_unconnected(addr: SocketAddrUnix) { for expected_sum in runs { let mut sum = 0; loop { - let nread = recvmsg( + let result = recvmsg( &data_socket, &mut [IoSliceMut::new(&mut buffer)], &mut Default::default(), RecvFlags::empty(), ) - .unwrap() - .bytes; + .unwrap(); + let nread = result.bytes; assert_ne!(&buffer[..nread], b"exit"); + assert_eq!(result.flags, ReturnFlags::empty()); if &buffer[..nread] == b"sum" { break; } @@ -290,16 +294,17 @@ fn do_test_unix_msg_unconnected(addr: SocketAddrUnix) { assert_eq!(sum, *expected_sum); } - let nread = recvmsg( + let result = recvmsg( &data_socket, &mut [IoSliceMut::new(&mut buffer)], &mut Default::default(), RecvFlags::empty(), ) - .unwrap() - .bytes; + .unwrap(); + let nread = result.bytes; assert_eq!(&buffer[..nread], b"exit"); + assert_eq!(result.flags, ReturnFlags::empty()); } }; @@ -425,7 +430,7 @@ fn test_unix_msg_with_scm_rights() { use rustix::fd::AsFd; use rustix::io::{IoSlice, IoSliceMut}; use rustix::net::{ - recvmsg, sendmsg, RecvAncillaryBuffer, RecvAncillaryMessage, RecvFlags, + recvmsg, sendmsg, RecvAncillaryBuffer, RecvAncillaryMessage, RecvFlags, ReturnFlags, SendAncillaryBuffer, SendAncillaryMessage, SendFlags, }; use rustix::pipe::pipe; @@ -454,14 +459,16 @@ fn test_unix_msg_with_scm_rights() { let mut sum = 0; loop { let mut cmsg_buffer = RecvAncillaryBuffer::new(&mut cmsg_space); - let nread = recvmsg( + let result = recvmsg( &data_socket, &mut [IoSliceMut::new(&mut buffer)], &mut cmsg_buffer, RecvFlags::empty(), ) - .unwrap() - .bytes; + .unwrap(); + let nread = result.bytes; + + assert_eq!(result.flags, ReturnFlags::empty()); // Read out the pipe if we got it. if let Some(end) = cmsg_buffer @@ -536,18 +543,19 @@ fn test_unix_msg_with_scm_rights() { ) .unwrap(); - let nread = recvmsg( + let result = recvmsg( &data_socket, &mut [IoSliceMut::new(&mut buffer)], &mut Default::default(), RecvFlags::empty(), ) - .unwrap() - .bytes; + .unwrap(); + let nread = result.bytes; assert_eq!( i32::from_str(&String::from_utf8_lossy(&buffer[..nread])).unwrap(), *sum ); + assert_eq!(result.flags, ReturnFlags::empty()); } let data_socket = socket(AddressFamily::UNIX, SocketType::SEQPACKET, None).unwrap(); @@ -600,7 +608,7 @@ fn test_unix_peercred_explicit() { use rustix::io::{IoSlice, IoSliceMut}; use rustix::net::{ recvmsg, sendmsg, sockopt, RecvAncillaryBuffer, RecvAncillaryMessage, RecvFlags, - SendAncillaryBuffer, SendAncillaryMessage, SendFlags, SocketFlags, + ReturnFlags, SendAncillaryBuffer, SendAncillaryMessage, SendFlags, SocketFlags, }; let (send_sock, recv_sock) = rustix::net::socketpair( @@ -631,7 +639,7 @@ fn test_unix_peercred_explicit() { let mut cmsg_buffer = RecvAncillaryBuffer::new(&mut cmsg_space); let mut buffer = [0; BUFFER_SIZE]; - recvmsg( + let result = recvmsg( &recv_sock, &mut [IoSliceMut::new(&mut buffer)], &mut cmsg_buffer, @@ -639,6 +647,8 @@ fn test_unix_peercred_explicit() { ) .unwrap(); + assert_eq!(result.flags, ReturnFlags::empty()); + match cmsg_buffer.drain().next().unwrap() { RecvAncillaryMessage::ScmCredentials(ucred2) => assert_eq!(ucred2, ucred), _ => panic!("Unexpected ancillary message"), @@ -656,7 +666,7 @@ fn test_unix_peercred_implicit() { use rustix::io::{IoSlice, IoSliceMut}; use rustix::net::{ recvmsg, sendmsg, sockopt, RecvAncillaryBuffer, RecvAncillaryMessage, RecvFlags, - SendAncillaryBuffer, SendFlags, SocketFlags, + ReturnFlags, SendAncillaryBuffer, SendFlags, SocketFlags, }; use rustix::process::{getgid, getpid, getuid}; @@ -689,7 +699,7 @@ fn test_unix_peercred_implicit() { let mut cmsg_buffer = RecvAncillaryBuffer::new(&mut cmsg_space); let mut buffer = [0; BUFFER_SIZE]; - recvmsg( + let result = recvmsg( &recv_sock, &mut [IoSliceMut::new(&mut buffer)], &mut cmsg_buffer, @@ -697,6 +707,8 @@ fn test_unix_peercred_implicit() { ) .unwrap(); + assert_eq!(result.flags, ReturnFlags::empty()); + match cmsg_buffer.drain().next().unwrap() { RecvAncillaryMessage::ScmCredentials(ucred2) => assert_eq!(ucred2, ucred), _ => panic!("Unexpected ancillary message"), @@ -714,7 +726,7 @@ fn test_unix_msg_with_combo() { use rustix::fd::AsFd; use rustix::io::{IoSlice, IoSliceMut}; use rustix::net::{ - recvmsg, sendmsg, RecvAncillaryBuffer, RecvAncillaryMessage, RecvFlags, + recvmsg, sendmsg, RecvAncillaryBuffer, RecvAncillaryMessage, RecvFlags, ReturnFlags, SendAncillaryBuffer, SendAncillaryMessage, SendFlags, }; use rustix::pipe::pipe; @@ -745,14 +757,16 @@ fn test_unix_msg_with_combo() { let mut sum = 0; loop { let mut cmsg_buffer = RecvAncillaryBuffer::new(&mut cmsg_space); - let nread = recvmsg( + let result = recvmsg( &data_socket, &mut [IoSliceMut::new(&mut buffer)], &mut cmsg_buffer, RecvFlags::empty(), ) - .unwrap() - .bytes; + .unwrap(); + let nread = result.bytes; + + assert_eq!(result.flags, ReturnFlags::empty()); // Read out the pipe if we got it. for cmsg in cmsg_buffer.drain() { @@ -844,18 +858,19 @@ fn test_unix_msg_with_combo() { ) .unwrap(); - let nread = recvmsg( + let result = recvmsg( &data_socket, &mut [IoSliceMut::new(&mut buffer)], &mut Default::default(), RecvFlags::empty(), ) - .unwrap() - .bytes; + .unwrap(); + let nread = result.bytes; assert_eq!( i32::from_str(&String::from_utf8_lossy(&buffer[..nread])).unwrap(), *sum ); + assert_eq!(result.flags, ReturnFlags::empty()); } let data_socket = socket(AddressFamily::UNIX, SocketType::SEQPACKET, None).unwrap(); diff --git a/tests/net/unix_alloc.rs b/tests/net/unix_alloc.rs index 964b031cd..5a5278215 100644 --- a/tests/net/unix_alloc.rs +++ b/tests/net/unix_alloc.rs @@ -127,7 +127,7 @@ fn test_unix() { #[cfg(not(any(target_os = "espidf", target_os = "redox", target_os = "wasi")))] fn do_test_unix_msg(addr: SocketAddrUnix) { use rustix::io::{IoSlice, IoSliceMut}; - use rustix::net::{recvmsg, sendmsg, RecvFlags, SendFlags}; + use rustix::net::{recvmsg, sendmsg, RecvFlags, ReturnFlags, SendFlags}; let server = { let connection_socket = socket(AddressFamily::UNIX, SocketType::SEQPACKET, None).unwrap(); @@ -140,14 +140,16 @@ fn do_test_unix_msg(addr: SocketAddrUnix) { let data_socket = accept(&connection_socket).unwrap(); let mut sum = 0; loop { - let nread = recvmsg( + let result = recvmsg( &data_socket, &mut [IoSliceMut::new(&mut buffer)], &mut Default::default(), RecvFlags::empty(), ) - .unwrap() - .bytes; + .unwrap(); + let nread = result.bytes; + + assert_eq!(result.flags, ReturnFlags::empty()); if &buffer[..nread] == b"exit" { break 'exit; @@ -257,7 +259,7 @@ fn do_test_unix_msg(addr: SocketAddrUnix) { #[cfg(not(any(target_os = "espidf", target_os = "redox", target_os = "wasi")))] fn do_test_unix_msg_unconnected(addr: SocketAddrUnix) { use rustix::io::{IoSlice, IoSliceMut}; - use rustix::net::{recvmsg, sendmsg_unix, RecvFlags, SendFlags}; + use rustix::net::{recvmsg, sendmsg_unix, RecvFlags, ReturnFlags, SendFlags}; let server = { let runs: &[i32] = &[3, 184, 187, 0]; @@ -269,14 +271,16 @@ fn do_test_unix_msg_unconnected(addr: SocketAddrUnix) { for expected_sum in runs { let mut sum = 0; loop { - let nread = recvmsg( + let result = recvmsg( &data_socket, &mut [IoSliceMut::new(&mut buffer)], &mut Default::default(), RecvFlags::empty(), ) - .unwrap() - .bytes; + .unwrap(); + let nread = result.bytes; + + assert_eq!(result.flags, ReturnFlags::empty()); assert_ne!(&buffer[..nread], b"exit"); if &buffer[..nread] == b"sum" { @@ -288,16 +292,17 @@ fn do_test_unix_msg_unconnected(addr: SocketAddrUnix) { assert_eq!(sum, *expected_sum); } - let nread = recvmsg( + let result = recvmsg( &data_socket, &mut [IoSliceMut::new(&mut buffer)], &mut Default::default(), RecvFlags::empty(), ) - .unwrap() - .bytes; + .unwrap(); + let nread = result.bytes; assert_eq!(&buffer[..nread], b"exit"); + assert_eq!(result.flags, ReturnFlags::empty()); } }; @@ -423,7 +428,7 @@ fn test_unix_msg_with_scm_rights() { use rustix::fd::AsFd; use rustix::io::{IoSlice, IoSliceMut}; use rustix::net::{ - recvmsg, sendmsg, RecvAncillaryBuffer, RecvAncillaryMessage, RecvFlags, + recvmsg, sendmsg, RecvAncillaryBuffer, RecvAncillaryMessage, RecvFlags, ReturnFlags, SendAncillaryBuffer, SendAncillaryMessage, SendFlags, }; use rustix::pipe::pipe; @@ -452,14 +457,16 @@ fn test_unix_msg_with_scm_rights() { let mut sum = 0; loop { let mut cmsg_buffer = RecvAncillaryBuffer::new(&mut cmsg_space); - let nread = recvmsg( + let result = recvmsg( &data_socket, &mut [IoSliceMut::new(&mut buffer)], &mut cmsg_buffer, RecvFlags::empty(), ) - .unwrap() - .bytes; + .unwrap(); + let nread = result.bytes; + + assert_eq!(result.flags, ReturnFlags::empty()); // Read out the pipe if we got it. if let Some(end) = cmsg_buffer @@ -534,18 +541,19 @@ fn test_unix_msg_with_scm_rights() { ) .unwrap(); - let nread = recvmsg( + let result = recvmsg( &data_socket, &mut [IoSliceMut::new(&mut buffer)], &mut Default::default(), RecvFlags::empty(), ) - .unwrap() - .bytes; + .unwrap(); + let nread = result.bytes; assert_eq!( i32::from_str(&String::from_utf8_lossy(&buffer[..nread])).unwrap(), *sum ); + assert_eq!(result.flags, ReturnFlags::empty()); } let data_socket = socket(AddressFamily::UNIX, SocketType::SEQPACKET, None).unwrap(); @@ -659,7 +667,7 @@ fn test_unix_msg_with_combo() { use rustix::fd::AsFd; use rustix::io::{IoSlice, IoSliceMut}; use rustix::net::{ - recvmsg, sendmsg, RecvAncillaryBuffer, RecvAncillaryMessage, RecvFlags, + recvmsg, sendmsg, RecvAncillaryBuffer, RecvAncillaryMessage, RecvFlags, ReturnFlags, SendAncillaryBuffer, SendAncillaryMessage, SendFlags, }; use rustix::pipe::pipe; @@ -690,14 +698,16 @@ fn test_unix_msg_with_combo() { let mut sum = 0; loop { let mut cmsg_buffer = RecvAncillaryBuffer::new(&mut cmsg_space); - let nread = recvmsg( + let result = recvmsg( &data_socket, &mut [IoSliceMut::new(&mut buffer)], &mut cmsg_buffer, RecvFlags::empty(), ) - .unwrap() - .bytes; + .unwrap(); + let nread = result.bytes; + + assert_eq!(result.flags, ReturnFlags::empty()); // Read out the pipe if we got it. for cmsg in cmsg_buffer.drain() { @@ -789,18 +799,19 @@ fn test_unix_msg_with_combo() { ) .unwrap(); - let nread = recvmsg( + let result = recvmsg( &data_socket, &mut [IoSliceMut::new(&mut buffer)], &mut Default::default(), RecvFlags::empty(), ) - .unwrap() - .bytes; + .unwrap(); + let nread = result.bytes; assert_eq!( i32::from_str(&String::from_utf8_lossy(&buffer[..nread])).unwrap(), *sum ); + assert_eq!(result.flags, ReturnFlags::empty()); } let data_socket = socket(AddressFamily::UNIX, SocketType::SEQPACKET, None).unwrap(); diff --git a/tests/net/v4.rs b/tests/net/v4.rs index d770b657b..aedeffdc4 100644 --- a/tests/net/v4.rs +++ b/tests/net/v4.rs @@ -6,7 +6,7 @@ use rustix::net::{ accept, bind_v4, connect_v4, getsockname, listen, recv, send, socket, AddressFamily, Ipv4Addr, - RecvFlags, SendFlags, SocketAddrAny, SocketAddrV4, SocketType, + RecvFlags, ReturnFlags, SendFlags, SocketAddrAny, SocketAddrV4, SocketType, }; use std::sync::{Arc, Condvar, Mutex}; use std::thread; @@ -127,6 +127,7 @@ fn test_v4_msg() { String::from_utf8_lossy(&buffer[..res.bytes]), "hello, world" ); + assert_eq!(res.flags, ReturnFlags::empty()); sendmsg( &data_socket, @@ -172,6 +173,7 @@ fn test_v4_msg() { String::from_utf8_lossy(&buffer[..res.bytes]), "goodnight, moon" ); + assert_eq!(res.flags, ReturnFlags::empty()); } let ready = Arc::new((Mutex::new(0_u16), Condvar::new())); diff --git a/tests/net/v6.rs b/tests/net/v6.rs index 0d0a596c9..ca63abf6e 100644 --- a/tests/net/v6.rs +++ b/tests/net/v6.rs @@ -6,7 +6,7 @@ use rustix::net::{ accept, bind_v6, connect_v6, getsockname, listen, recv, send, socket, AddressFamily, Ipv6Addr, - RecvFlags, SendFlags, SocketAddrAny, SocketAddrV6, SocketType, + RecvFlags, ReturnFlags, SendFlags, SocketAddrAny, SocketAddrV6, SocketType, }; use std::sync::{Arc, Condvar, Mutex}; use std::thread; @@ -127,6 +127,7 @@ fn test_v6_msg() { String::from_utf8_lossy(&buffer[..result.bytes]), "hello, world" ); + assert_eq!(result.flags, ReturnFlags::empty()); sendmsg( &data_socket, @@ -172,6 +173,7 @@ fn test_v6_msg() { String::from_utf8_lossy(&buffer[..nread.bytes]), "goodnight, moon" ); + assert_eq!(nread.flags, ReturnFlags::empty()); } let ready = Arc::new((Mutex::new(0_u16), Condvar::new())); From 5e9577fb5ff56caf92bbfcaacf6d6447b17124cc Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Thu, 23 Jan 2025 19:05:32 -0800 Subject: [PATCH 02/10] Make `RecvMsgReturn`'s `Debug` impl not break no_std. --- src/net/send_recv/msg.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/net/send_recv/msg.rs b/src/net/send_recv/msg.rs index 5d8785e83..b67511e21 100644 --- a/src/net/send_recv/msg.rs +++ b/src/net/send_recv/msg.rs @@ -817,7 +817,6 @@ pub fn recvmsg( } /// The result of a successful [`recvmsg`] call. -#[derive(Debug)] pub struct RecvMsgReturn { /// The number of bytes received. /// @@ -833,6 +832,17 @@ pub struct RecvMsgReturn { pub address: Option, } +#[cfg(feature = "std")] +impl fmt::Debug for RecvMsgReturn { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RecvMsgReturn") + .field("bytes", &self.bytes) + .field("flags", &self.flags) + .field("address", &self.address) + .finish() + } +} + /// An iterator over data in an ancillary buffer. pub struct AncillaryIter<'data, T> { /// The data we're iterating over. From 0e3962fc6e858a9f2a083489d34ddfbc9e12046c Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Thu, 23 Jan 2025 19:16:54 -0800 Subject: [PATCH 03/10] Fix compilation. --- src/net/send_recv/msg.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/net/send_recv/msg.rs b/src/net/send_recv/msg.rs index b67511e21..3e5e6e6c5 100644 --- a/src/net/send_recv/msg.rs +++ b/src/net/send_recv/msg.rs @@ -7,7 +7,8 @@ use crate::fd::{AsFd, BorrowedFd, OwnedFd}; use crate::io::{self, IoSlice, IoSliceMut}; #[cfg(linux_kernel)] use crate::net::UCred; - +#[cfg(feature = "std")] +use core::fmt; use core::iter::FusedIterator; use core::marker::PhantomData; use core::mem::{align_of, size_of, size_of_val, take}; From b8600273c7cbfe8d61f7fa04db96867962084876 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Thu, 23 Jan 2025 19:22:34 -0800 Subject: [PATCH 04/10] Fix copypasta to avoid a unix domain socket collision. --- tests/net/recv_trunc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/net/recv_trunc.rs b/tests/net/recv_trunc.rs index 814883c48..a03ea7fbf 100644 --- a/tests/net/recv_trunc.rs +++ b/tests/net/recv_trunc.rs @@ -48,7 +48,7 @@ fn net_recvmsg_trunc() { crate::init(); let tmpdir = tempfile::tempdir().unwrap(); - let path = tmpdir.path().join("recv_uninit_trunc"); + let path = tmpdir.path().join("recvmsg_trunc"); let name = SocketAddrUnix::new(&path).unwrap(); let receiver = rustix::net::socket(AddressFamily::UNIX, SocketType::DGRAM, None).unwrap(); From d17c49564f41f31e49e5873f094e534a076fa042 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Thu, 23 Jan 2025 19:30:28 -0800 Subject: [PATCH 05/10] Windows lacks `MSG_EOR`. --- src/backend/libc/net/send_recv.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/backend/libc/net/send_recv.rs b/src/backend/libc/net/send_recv.rs index 6a3d5ad05..bfe9162a6 100644 --- a/src/backend/libc/net/send_recv.rs +++ b/src/backend/libc/net/send_recv.rs @@ -117,6 +117,7 @@ bitflags! { /// `MSG_OOB` const OOB = bitcast!(c::MSG_OOB); /// `MSG_EOR` + #[cfg(not(windows))] const EOR = bitcast!(c::MSG_EOR); /// `MSG_TRUNC` const TRUNC = bitcast!(c::MSG_TRUNC); From e1e73ff4db8140a57d3a3fa6d4107f7d92c4e3d9 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Thu, 23 Jan 2025 19:56:22 -0800 Subject: [PATCH 06/10] Fix VMIN/VEOF on AIX. --- src/termios/types.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/termios/types.rs b/src/termios/types.rs index ec1d26aee..394c930b4 100644 --- a/src/termios/types.rs +++ b/src/termios/types.rs @@ -1211,24 +1211,28 @@ impl core::fmt::Debug for SpecialCodeIndex { Self::VKILL => write!(f, "VKILL"), #[cfg(not(any( solarish, + target_os = "aix", all(linux_kernel, any(target_arch = "sparc", target_arch = "sparc64")) )))] Self::VEOF => write!(f, "VEOF"), #[cfg(not(any( solarish, + target_os = "aix", all(linux_kernel, any(target_arch = "sparc", target_arch = "sparc64")) )))] Self::VTIME => write!(f, "VTIME"), #[cfg(not(any( solarish, + target_os = "aix", all(linux_kernel, any(target_arch = "sparc", target_arch = "sparc64")) )))] Self::VMIN => write!(f, "VMIN"), - // On Solarish platforms, and Linux on SPARC, `VMIN` and `VTIME` + // On Solarish platforms, Linux on SPARC, and AIX, `VMIN` and `VTIME` // have the same value as `VEOF` and `VEOL`. #[cfg(any( solarish, + target_os = "aix", all(linux_kernel, any(target_arch = "sparc", target_arch = "sparc64")) ))] Self::VMIN => write!(f, "VMIN/VEOF"), From 0e61434aebde6269d4079e13fb0b6089db5106fa Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Thu, 23 Jan 2025 19:42:59 -0800 Subject: [PATCH 07/10] Workaround limitations on Apple. - Disable `RecvFlags::TRUNC` on Apple. - Check for `AF_UNIX` socket addresses with no paths on Apple. --- src/backend/libc/net/read_sockaddr.rs | 25 +++++- src/backend/libc/net/send_recv.rs | 3 + tests/net/recv_trunc.rs | 111 ++++++++++++++------------ 3 files changed, 87 insertions(+), 52 deletions(-) diff --git a/src/backend/libc/net/read_sockaddr.rs b/src/backend/libc/net/read_sockaddr.rs index d2c9b69e6..eaaec897e 100644 --- a/src/backend/libc/net/read_sockaddr.rs +++ b/src/backend/libc/net/read_sockaddr.rs @@ -93,6 +93,18 @@ unsafe fn read_ss_family(storage: *const c::sockaddr_storage) -> u16 { (*storage.cast::()).ss_family.into() } +/// Read the first byte of the `sun_path` field, assuming we have an `AF_UNIX` +/// socket address. +#[cfg(apple)] +#[inline] +unsafe fn read_sun_path0(storage: *const c::sockaddr_storage) -> u8 { + // In `read_ss_family` we assert that we know the layout of `sockaddr`. + storage + .cast::() + .add(super::addr::offsetof_sun_path()) + .read() +} + /// Set the `ss_family` field of a socket address to `AF_UNSPEC`, so that we /// can test for `AF_UNSPEC` to test whether it was stored to. pub(crate) unsafe fn initialize_family_to_unspec(storage: *mut c::sockaddr_storage) { @@ -233,10 +245,17 @@ pub(crate) unsafe fn maybe_read_sockaddr_os( assert!(len >= size_of::()); let family = read_ss_family(storage).into(); if family == c::AF_UNSPEC { - None - } else { - Some(inner_read_sockaddr_os(family, storage, len)) + return None; } + + // On macOS, if we get an `AF_UNIX` with an empty path, treat it as + // an absent address. + #[cfg(apple)] + if family == c::AF_UNIX && read_sun_path0(storage) == 0 { + return None; + } + + Some(inner_read_sockaddr_os(family, storage, len)) } /// Read a socket address returned from the OS. diff --git a/src/backend/libc/net/send_recv.rs b/src/backend/libc/net/send_recv.rs index bfe9162a6..9783a249d 100644 --- a/src/backend/libc/net/send_recv.rs +++ b/src/backend/libc/net/send_recv.rs @@ -96,6 +96,9 @@ bitflags! { /// `MSG_PEEK` const PEEK = bitcast!(c::MSG_PEEK); /// `MSG_TRUNC` + // Apple has `MSG_TRUNC` but it's not documented for use with + // `recv` and friends, and in practice appears to be ignored. + #[cfg(not(apple))] const TRUNC = bitcast!(c::MSG_TRUNC); /// `MSG_WAITALL` const WAITALL = bitcast!(c::MSG_WAITALL); diff --git a/tests/net/recv_trunc.rs b/tests/net/recv_trunc.rs index a03ea7fbf..fcf1cf609 100644 --- a/tests/net/recv_trunc.rs +++ b/tests/net/recv_trunc.rs @@ -19,17 +19,22 @@ fn net_recv_uninit_trunc() { let n = rustix::net::sendto_unix(&sender, request, SendFlags::empty(), &name).expect("send"); assert_eq!(n, request.len()); - let mut response = [MaybeUninit::::zeroed(); 5]; - let (init, uninit) = - rustix::net::recv_uninit(&receiver, &mut response, RecvFlags::TRUNC).expect("recv_uninit"); - - // We used the `TRUNC` flag, so we should have only gotten 5 bytes. - assert_eq!(init, b"Hello"); - assert!(uninit.is_empty()); - - // Send the message again. - let n = rustix::net::sendto_unix(&sender, request, SendFlags::empty(), &name).expect("send"); - assert_eq!(n, request.len()); + // Test with `RecvFlags::TRUNC`, which is not supported on Apple. + #[cfg(not(apple))] + { + let mut response = [MaybeUninit::::zeroed(); 5]; + let (init, uninit) = rustix::net::recv_uninit(&receiver, &mut response, RecvFlags::TRUNC) + .expect("recv_uninit"); + + // We used the `TRUNC` flag, so we should have only gotten 5 bytes. + assert_eq!(init, b"Hello"); + assert!(uninit.is_empty()); + + // Send the message again. + let n = + rustix::net::sendto_unix(&sender, request, SendFlags::empty(), &name).expect("send"); + assert_eq!(n, request.len()); + } // This time receive it without `TRUNC`. This should fail. let mut response = [MaybeUninit::::zeroed(); 5]; @@ -56,43 +61,50 @@ fn net_recvmsg_trunc() { let sender = rustix::net::socket(AddressFamily::UNIX, SocketType::DGRAM, None).unwrap(); let request = b"Hello, World!!!"; - let n = rustix::net::sendto_unix(&sender, request, SendFlags::empty(), &name).expect("send"); - assert_eq!(n, request.len()); - - let mut response = [0_u8; 5]; - let result = rustix::net::recvmsg( - &receiver, - &mut [IoSliceMut::new(&mut response)], - &mut Default::default(), - RecvFlags::TRUNC, - ) - .expect("recvmsg"); - // We used the `TRUNC` flag, so we should have received 15 bytes, - // truncated to 5 bytes, and the `TRUNC` flag should have been returned. - assert_eq!(&response, b"Hello"); - assert_eq!(result.bytes, 15); - assert_eq!(result.flags, ReturnFlags::TRUNC); - - // Send the message again. - let n = rustix::net::sendto_unix(&sender, request, SendFlags::empty(), &name).expect("send"); - assert_eq!(n, request.len()); - - // This time receive it with `TRUNC` and a big enough buffer. - let mut response = [0_u8; 30]; - let result = rustix::net::recvmsg( - &receiver, - &mut [IoSliceMut::new(&mut response)], - &mut Default::default(), - RecvFlags::TRUNC, - ) - .expect("recvmsg"); - - // We used the `TRUNC` flag, so we should have received 15 bytes - // and the buffer was big enough so the `TRUNC` flag should not have - // been returned. - assert_eq!(&response[..result.bytes], request); - assert_eq!(result.flags, ReturnFlags::empty()); + // Test with `RecvFlags::TRUNC`, which is not supported on Apple. + #[cfg(not(apple))] + { + let n = + rustix::net::sendto_unix(&sender, request, SendFlags::empty(), &name).expect("send"); + assert_eq!(n, request.len()); + + let mut response = [0_u8; 5]; + let result = rustix::net::recvmsg( + &receiver, + &mut [IoSliceMut::new(&mut response)], + &mut Default::default(), + RecvFlags::TRUNC, + ) + .expect("recvmsg"); + + // We used the `TRUNC` flag, so we should have received 15 bytes, + // truncated to 5 bytes, and the `TRUNC` flag should have been returned. + assert_eq!(&response, b"Hello"); + assert_eq!(result.bytes, 15); + assert_eq!(result.flags, ReturnFlags::TRUNC); + + // Send the message again. + let n = + rustix::net::sendto_unix(&sender, request, SendFlags::empty(), &name).expect("send"); + assert_eq!(n, request.len()); + + // This time receive it with `TRUNC` and a big enough buffer. + let mut response = [0_u8; 30]; + let result = rustix::net::recvmsg( + &receiver, + &mut [IoSliceMut::new(&mut response)], + &mut Default::default(), + RecvFlags::TRUNC, + ) + .expect("recvmsg"); + + // We used the `TRUNC` flag, so we should have received 15 bytes + // and the buffer was big enough so the `TRUNC` flag should not have + // been returned. + assert_eq!(&response[..result.bytes], request); + assert_eq!(result.flags, ReturnFlags::empty()); + } // Send the message again. let n = rustix::net::sendto_unix(&sender, request, SendFlags::empty(), &name).expect("send"); @@ -108,8 +120,9 @@ fn net_recvmsg_trunc() { ) .expect("recvmsg"); - // We used the `TRUNC` flag, so we should have received 15 bytes, - // truncated to 5 bytes, and the `TRUNC` flag should have been returned. + // We didn't use the `TRUNC` flag, but the buffer was big enough, + // so we should have received 15 bytes, and the `TRUNC` flag should + // have been returned. assert_eq!(&response[..result.bytes], request); assert_eq!(result.flags, ReturnFlags::empty()); From 18753ea23df3cd9fd1cdbc14519c3335464d6ae9 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Wed, 29 Jan 2025 11:56:50 -0800 Subject: [PATCH 08/10] Disable `RecvFlags::TRUNC` on illumos too. --- src/backend/libc/net/send_recv.rs | 6 +++--- src/backend/linux_raw/net/addr.rs | 1 + tests/net/recv_trunc.rs | 8 ++++---- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/backend/libc/net/send_recv.rs b/src/backend/libc/net/send_recv.rs index 9783a249d..c4ceb9bbf 100644 --- a/src/backend/libc/net/send_recv.rs +++ b/src/backend/libc/net/send_recv.rs @@ -96,9 +96,9 @@ bitflags! { /// `MSG_PEEK` const PEEK = bitcast!(c::MSG_PEEK); /// `MSG_TRUNC` - // Apple has `MSG_TRUNC` but it's not documented for use with - // `recv` and friends, and in practice appears to be ignored. - #[cfg(not(apple))] + // Apple and illumos have `MSG_TRUNC` but it's not documented for use + // with `recv` and friends, and in practice appears to be ignored. + #[cfg(not(any(apple, solarish)))] const TRUNC = bitcast!(c::MSG_TRUNC); /// `MSG_WAITALL` const WAITALL = bitcast!(c::MSG_WAITALL); diff --git a/src/backend/linux_raw/net/addr.rs b/src/backend/linux_raw/net/addr.rs index 5bc84ed69..eddfae280 100644 --- a/src/backend/linux_raw/net/addr.rs +++ b/src/backend/linux_raw/net/addr.rs @@ -11,6 +11,7 @@ use crate::ffi::CStr; use crate::{io, path}; use core::cmp::Ordering; use core::hash::{Hash, Hasher}; +use core::mem::size_of; use core::{fmt, slice}; /// `struct sockaddr_un` diff --git a/tests/net/recv_trunc.rs b/tests/net/recv_trunc.rs index fcf1cf609..d2f0b983d 100644 --- a/tests/net/recv_trunc.rs +++ b/tests/net/recv_trunc.rs @@ -19,8 +19,8 @@ fn net_recv_uninit_trunc() { let n = rustix::net::sendto_unix(&sender, request, SendFlags::empty(), &name).expect("send"); assert_eq!(n, request.len()); - // Test with `RecvFlags::TRUNC`, which is not supported on Apple. - #[cfg(not(apple))] + // Test with `RecvFlags::TRUNC`, which is not supported on Apple or illumos. + #[cfg(not(any(apple, solarish)))] { let mut response = [MaybeUninit::::zeroed(); 5]; let (init, uninit) = rustix::net::recv_uninit(&receiver, &mut response, RecvFlags::TRUNC) @@ -62,8 +62,8 @@ fn net_recvmsg_trunc() { let sender = rustix::net::socket(AddressFamily::UNIX, SocketType::DGRAM, None).unwrap(); let request = b"Hello, World!!!"; - // Test with `RecvFlags::TRUNC`, which is not supported on Apple. - #[cfg(not(apple))] + // Test with `RecvFlags::TRUNC`, which is not supported on Apple or illumos. + #[cfg(not(any(apple, solarish)))] { let n = rustix::net::sendto_unix(&sender, request, SendFlags::empty(), &name).expect("send"); From ba31d5986998c8f857706fe8dd7a8422dbe2c9f0 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Wed, 29 Jan 2025 12:05:35 -0800 Subject: [PATCH 09/10] Fix an unused import. --- src/backend/linux_raw/net/addr.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/backend/linux_raw/net/addr.rs b/src/backend/linux_raw/net/addr.rs index eddfae280..5bc84ed69 100644 --- a/src/backend/linux_raw/net/addr.rs +++ b/src/backend/linux_raw/net/addr.rs @@ -11,7 +11,6 @@ use crate::ffi::CStr; use crate::{io, path}; use core::cmp::Ordering; use core::hash::{Hash, Hasher}; -use core::mem::size_of; use core::{fmt, slice}; /// `struct sockaddr_un` From 2c0d43919c3963a1a16a2ea70c52df2455561436 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Wed, 29 Jan 2025 15:16:19 -0800 Subject: [PATCH 10/10] Disable `RecvFlags::TRUNC` on NetBSD too. --- src/backend/libc/net/send_recv.rs | 7 ++++--- tests/net/recv_trunc.rs | 8 ++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/backend/libc/net/send_recv.rs b/src/backend/libc/net/send_recv.rs index c4ceb9bbf..510afd622 100644 --- a/src/backend/libc/net/send_recv.rs +++ b/src/backend/libc/net/send_recv.rs @@ -96,9 +96,10 @@ bitflags! { /// `MSG_PEEK` const PEEK = bitcast!(c::MSG_PEEK); /// `MSG_TRUNC` - // Apple and illumos have `MSG_TRUNC` but it's not documented for use - // with `recv` and friends, and in practice appears to be ignored. - #[cfg(not(any(apple, solarish)))] + // Apple, illumos, and NetBSD have `MSG_TRUNC` but it's not documented + // for use with `recv` and friends, and in practice appears to be + // ignored. + #[cfg(not(any(apple, solarish, target_os = "netbsd")))] const TRUNC = bitcast!(c::MSG_TRUNC); /// `MSG_WAITALL` const WAITALL = bitcast!(c::MSG_WAITALL); diff --git a/tests/net/recv_trunc.rs b/tests/net/recv_trunc.rs index d2f0b983d..3e17c9c93 100644 --- a/tests/net/recv_trunc.rs +++ b/tests/net/recv_trunc.rs @@ -19,8 +19,8 @@ fn net_recv_uninit_trunc() { let n = rustix::net::sendto_unix(&sender, request, SendFlags::empty(), &name).expect("send"); assert_eq!(n, request.len()); - // Test with `RecvFlags::TRUNC`, which is not supported on Apple or illumos. - #[cfg(not(any(apple, solarish)))] + // Test with `RecvFlags::TRUNC`, which is not supported on Apple, illumos, or NetBSD. + #[cfg(not(any(apple, solarish, target_os = "netbsd")))] { let mut response = [MaybeUninit::::zeroed(); 5]; let (init, uninit) = rustix::net::recv_uninit(&receiver, &mut response, RecvFlags::TRUNC) @@ -62,8 +62,8 @@ fn net_recvmsg_trunc() { let sender = rustix::net::socket(AddressFamily::UNIX, SocketType::DGRAM, None).unwrap(); let request = b"Hello, World!!!"; - // Test with `RecvFlags::TRUNC`, which is not supported on Apple or illumos. - #[cfg(not(any(apple, solarish)))] + // Test with `RecvFlags::TRUNC`, which is not supported on Apple, illumos, or NetBSD. + #[cfg(not(any(apple, solarish, target_os = "netbsd")))] { let n = rustix::net::sendto_unix(&sender, request, SendFlags::empty(), &name).expect("send");