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() {