Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions library/std/src/os/unix/net/addr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))

@Mark-Simulacrum Mark-Simulacrum Aug 8, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this also adjust

// on QNX7.1 and QNX8 the `len` value returned by the SUN_LEN
// macro in its libc does not include the null byte in the count so
// don't add it here to match what a C program passes to bind(2) and
// similar functions
if cfg!(not(any(target_os = "qnx", target_env = "nto71"))) {
len += 1
}
? Haven't traced the code but went looking for the QNX modifications which were in #158697.

View changes since the review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no because only the inbound one was a parsing bug, "oddly" enough QNX has it right using len (because this platform excludes the NULL terminator). As for the 'cfg' part, it is still needed as len is fed to bind where SUN_LEN for QNX is a real kernel-facing difference.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rustbot ready

let end = core::slice::memchr::memchr(0, &path[..len]).unwrap_or(len);
AddressKind::Pathname(OsStr::from_bytes(&path[..end]).as_ref())
}
}
Expand Down
23 changes: 23 additions & 0 deletions library/std/src/os/unix/net/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
Loading