From e96993c68f6f41c6fd9746e3c3cf2ec7f50ace7e Mon Sep 17 00:00:00 2001 From: David Carlier Date: Mon, 3 Aug 2026 06:51:24 +0100 Subject: [PATCH] std: fix unix socket address truncation without a trailing NUL getsockname(2) and friends do not count the trailing NUL in the length they report on freebsd, netbsd and qnx, and a caller may bind(2) without one anywhere, so shortening the path by one byte dropped its last character. scan for the NUL within the reported length instead, which is the rule unix(7) gives and subsumes the qnx case the old cfg handled. Fixes rust-lang/rust#118925 --- library/std/src/os/unix/net/addr.rs | 10 ++++------ library/std/src/os/unix/net/tests.rs | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/library/std/src/os/unix/net/addr.rs b/library/std/src/os/unix/net/addr.rs index e13f44d6fc9bd..92f30ae4605cb 100644 --- a/library/std/src/os/unix/net/addr.rs +++ b/library/std/src/os/unix/net/addr.rs @@ -255,12 +255,10 @@ impl SocketAddr { } else if self.addr.sun_path[0] == 0 { AddressKind::Abstract(ByteStr::from_bytes(&path[1..len])) } else { - // the value returned by getsockname(2) and similar on QNX7.1 and - // QNX8 does not count the NUL byte terminator of the path string, - // which matches the behavior of the SUN_LEN macro in libc, but - // other OSes do count the NUL byte so adjust accordingly - let end = - if cfg!(any(target_os = "qnx", target_env = "nto71")) { len } else { len - 1 }; + // linux adds a trailing NUL and counts it in the length, freebsd, netbsd + // and qnx do not, and a caller may bind(2) without one either. unix(7) + // gives the portable rule: strnlen(sun_path, len - offsetof(sun_path)) + let end = core::slice::memchr::memchr(0, &path[..len]).unwrap_or(len); AddressKind::Pathname(OsStr::from_bytes(&path[..end]).as_ref()) } } diff --git a/library/std/src/os/unix/net/tests.rs b/library/std/src/os/unix/net/tests.rs index 3ba4b44d2f1ef..9c3119e787b33 100644 --- a/library/std/src/os/unix/net/tests.rs +++ b/library/std/src/os/unix/net/tests.rs @@ -29,6 +29,29 @@ fn sock_addr_from_pathname() { assert_eq!(address.as_pathname(), Some(Path::new("/path/to/socket"))); } +// the trailing NUL is not counted in the reported length on freebsd, netbsd +// and qnx, and a caller may bind(2) without one anywhere +#[test] +fn sock_addr_without_trailing_nul() { + const PATH: &[u8] = b"/path/to/socket"; + + // SAFETY: all zeros is a valid representation for `sockaddr_un`. + let mut addr: libc::sockaddr_un = unsafe { crate::mem::zeroed() }; + addr.sun_family = libc::AF_UNIX as libc::sa_family_t; + for (dst, &src) in addr.sun_path.iter_mut().zip(PATH) { + *dst = src as _; + } + let offset = crate::mem::offset_of!(libc::sockaddr_un, sun_path); + + // length excluding the NUL, as reported by freebsd, netbsd and qnx + let address = or_panic!(SocketAddr::from_parts(addr, (offset + PATH.len()) as _)); + assert_eq!(address.as_pathname(), Some(Path::new("/path/to/socket"))); + + // length including the NUL, as reported by linux + let address = or_panic!(SocketAddr::from_parts(addr, (offset + PATH.len() + 1) as _)); + assert_eq!(address.as_pathname(), Some(Path::new("/path/to/socket"))); +} + #[test] #[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets #[cfg_attr(target_os = "vxworks", ignore = "Unix sockets are not implemented in VxWorks")]