diff --git a/src/sockaddr.rs b/src/sockaddr.rs index 370b3856..b38953b6 100644 --- a/src/sockaddr.rs +++ b/src/sockaddr.rs @@ -214,7 +214,8 @@ impl SockAddr { /// Constructs a `SockAddr` with the family `AF_UNIX` and the provided path. /// - /// Returns an error if the path is longer than `SUN_LEN`. + /// Returns an error if a pathname contains a null byte or the path is longer + /// than `SUN_LEN`. #[cfg(not(target_os = "wasi"))] pub fn unix

(path: P) -> io::Result where diff --git a/src/sys/unix.rs b/src/sys/unix.rs index 8027d325..a9e876af 100644 --- a/src/sys/unix.rs +++ b/src/sys/unix.rs @@ -758,6 +758,12 @@ pub(crate) fn unix_sockaddr(path: &Path) -> io::Result { "path must be shorter than SUN_LEN", )); } + if bytes.first() != Some(&0) && bytes.contains(&0) { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "path must not contain null bytes", + )); + } storage.sun_family = libc::AF_UNIX as sa_family_t; // SAFETY: `bytes` and `addr.sun_path` are not overlapping and @@ -961,12 +967,11 @@ impl SockAddr { } /// Returns this address as Unix `SocketAddr` if it is an `AF_UNIX` pathname - /// address, otherwise returns `None`. + /// address that can be represented by the standard library, otherwise returns + /// `None`. pub fn as_unix(&self) -> Option { let path = self.as_pathname()?; - // SAFETY: we can represent this as a valid pathname, then so can the - // standard library. - Some(std::os::unix::net::SocketAddr::from_pathname(path).unwrap()) + std::os::unix::net::SocketAddr::from_pathname(path).ok() } /// Returns this address as a `Path` reference if it is an `AF_UNIX` diff --git a/src/sys/windows.rs b/src/sys/windows.rs index b1fa20fc..33007296 100644 --- a/src/sys/windows.rs +++ b/src/sys/windows.rs @@ -1047,6 +1047,12 @@ pub(crate) fn unix_sockaddr(path: &Path) -> io::Result { "path must be shorter than SUN_LEN", )); } + if bytes.first() != Some(&0) && bytes.contains(&0) { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "path must not contain null bytes", + )); + } storage.sun_family = crate::sys::AF_UNIX as sa_family_t; // SAFETY: casting `[u8]` to `[i8]` is safe. diff --git a/tests/socket.rs b/tests/socket.rs index 0f390e3a..766f79ca 100644 --- a/tests/socket.rs +++ b/tests/socket.rs @@ -173,6 +173,44 @@ fn socket_address_unix() { } } +#[test] +#[cfg(not(target_os = "wasi"))] +fn socket_address_unix_invalid_pathname() { + for path in ["a\0b", "a\0", "a\0b\0c"] { + let err = SockAddr::unix(path).unwrap_err(); + assert_eq!(err.kind(), io::ErrorKind::InvalidInput); + } +} + +#[test] +#[cfg(all(unix, not(target_os = "wasi")))] +fn socket_address_unix_padded_raw_pathname() { + let original = SockAddr::unix("socket").unwrap(); + // SAFETY: a null-terminated pathname and zero padding form a valid + // sockaddr_un. Raw callers can supply the full structure length. + let (_, addr) = unsafe { + SockAddr::try_init(|storage, len| { + std::ptr::copy_nonoverlapping(original.as_ptr(), storage, 1); + *len = mem::size_of::() as _; + Ok(()) + }) + } + .unwrap(); + assert!(addr.as_pathname().unwrap().as_os_str().len() > "socket".len()); + assert!(addr.as_unix().is_none()); +} + +#[test] +#[cfg(all(unix, not(target_os = "wasi")))] +fn socket_address_unix_non_utf8_pathname() { + use std::ffi::OsStr; + use std::os::unix::ffi::OsStrExt; + + let path = Path::new(OsStr::from_bytes(b"socket-\xff")); + let addr = SockAddr::unix(path).unwrap(); + assert_eq!(addr.as_unix().unwrap().as_pathname(), Some(path)); +} + #[test] #[cfg(not(target_os = "wasi"))] fn socket_address_unix_unnamed() { @@ -208,9 +246,22 @@ fn socket_address_unix_abstract_namespace() { // The first byte is the opening null bytes of an abstract address, should not be included. assert_eq!(addr.as_abstract_namespace(), Some(&path.as_bytes()[1..])); assert!(addr.as_pathname().is_none()); + assert!(addr.as_unix().is_none()); assert!(!addr.is_unnamed()); } +#[test] +#[cfg(any(target_os = "linux", target_os = "android", target_os = "cygwin"))] +fn socket_address_unix_abstract_null_bytes() { + for path in ["\0", "\0\0", "\0abstract\0payload"] { + let addr = SockAddr::unix(path).unwrap(); + assert_eq!(addr.as_abstract_namespace(), Some(&path.as_bytes()[1..])); + assert!(addr.as_pathname().is_none()); + assert!(addr.as_unix().is_none()); + assert!(!addr.is_unnamed()); + } +} + #[test] #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))] fn socket_address_vsock() {