diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2022-11-21 17:26:56 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-11-21 17:26:56 +0000 |
commit | 9ea1493a813bf49a493a4528640f036b9dbcc7d8 (patch) | |
tree | 04cd3f870d4efda024756e8e772937d6fd1cca53 | |
parent | 8d279853dc3c306e8d0d14d5476cdf787c297db1 (diff) | |
parent | fa62534a238c86334d41bc1da68267388cda9b36 (diff) | |
download | nix-9ea1493a813bf49a493a4528640f036b9dbcc7d8.zip |
Merge #1871
1871: Fix using SockaddrStorage to store Unix domain addresses on Linux r=rtzoeller a=asomers
Since it has variable length, the user of a sockaddr_un must keep track of its true length. On the BSDs, this is handled by the builtin sun_len field. But on Linux-like operating systems it isn't. Fix this bug by explicitly tracking it for SockaddrStorage just like we already do for UnixAddr.
Fixes #1866
Co-authored-by: Alan Somers <asomers@gmail.com>
-rw-r--r-- | CHANGELOG.md | 6 | ||||
-rw-r--r-- | src/sys/socket/addr.rs | 122 |
2 files changed, 127 insertions, 1 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 917094aa..fba51823 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,9 @@ This project adheres to [Semantic Versioning](https://semver.org/). ## [Unreleased] - ReleaseDate ### Added -- Add `MntFlags` and `unmount` on all of the BSDs. +- Added `SockaddrStorage::{as_unix_addr, as_unix_addr_mut}` + ([#1871](https://github.com/nix-rust/nix/pull/1871)) +- Added `MntFlags` and `unmount` on all of the BSDs. ([#1849](https://github.com/nix-rust/nix/pull/1849)) - Added a 'Statfs::flags' method. ([#1849](https://github.com/nix-rust/nix/pull/1849)) @@ -44,6 +46,8 @@ This project adheres to [Semantic Versioning](https://semver.org/). ### Fixed +- Fixed using `SockaddrStorage` to store a Unix-domain socket address on Linux. + ([#1871](https://github.com/nix-rust/nix/pull/1871)) - Fix microsecond calculation for `TimeSpec`. ([#1801](https://github.com/nix-rust/nix/pull/1801)) - Fix `User::from_name` and `Group::from_name` panicking diff --git a/src/sys/socket/addr.rs b/src/sys/socket/addr.rs index c7b5f29e..b80d78c1 100644 --- a/src/sys/socket/addr.rs +++ b/src/sys/socket/addr.rs @@ -1536,6 +1536,17 @@ impl SockaddrLike for SockaddrStorage { let mut ss: libc::sockaddr_storage = mem::zeroed(); let ssp = &mut ss as *mut libc::sockaddr_storage as *mut u8; ptr::copy(addr as *const u8, ssp, len as usize); + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux" + ))] + if i32::from(ss.ss_family) == libc::AF_UNIX { + // Safe because we UnixAddr is strictly smaller than + // SockaddrStorage, and we just initialized the structure. + (*(&mut ss as *mut libc::sockaddr_storage as *mut UnixAddr)).sun_len = len as u8; + } Some(Self { ss }) } } else { @@ -1597,6 +1608,21 @@ impl SockaddrLike for SockaddrStorage { } } } + + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux" + ))] + fn len(&self) -> libc::socklen_t { + match self.as_unix_addr() { + // The UnixAddr type knows its own length + Some(ua) => ua.len(), + // For all else, we're just a boring SockaddrStorage + None => mem::size_of_val(self) as libc::socklen_t + } + } } macro_rules! accessors { @@ -1634,6 +1660,64 @@ macro_rules! accessors { } impl SockaddrStorage { + /// Downcast to an immutable `[UnixAddr]` reference. + pub fn as_unix_addr(&self) -> Option<&UnixAddr> { + cfg_if! { + if #[cfg(any(target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux" + ))] + { + let p = unsafe{ &self.ss as *const libc::sockaddr_storage }; + // Safe because UnixAddr is strictly smaller than + // sockaddr_storage, and we're fully initialized + let len = unsafe { + (*(p as *const UnixAddr )).sun_len as usize + }; + } else { + let len = self.len() as usize; + } + } + // Sanity checks + if self.family() != Some(AddressFamily::Unix) || + len < offset_of!(libc::sockaddr_un, sun_path) || + len > mem::size_of::<libc::sockaddr_un>() { + None + } else { + Some(unsafe{&self.su}) + } + } + + /// Downcast to a mutable `[UnixAddr]` reference. + pub fn as_unix_addr_mut(&mut self) -> Option<&mut UnixAddr> { + cfg_if! { + if #[cfg(any(target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux" + ))] + { + let p = unsafe{ &self.ss as *const libc::sockaddr_storage }; + // Safe because UnixAddr is strictly smaller than + // sockaddr_storage, and we're fully initialized + let len = unsafe { + (*(p as *const UnixAddr )).sun_len as usize + }; + } else { + let len = self.len() as usize; + } + } + // Sanity checks + if self.family() != Some(AddressFamily::Unix) || + len < offset_of!(libc::sockaddr_un, sun_path) || + len > mem::size_of::<libc::sockaddr_un>() { + None + } else { + Some(unsafe{&mut self.su}) + } + } + #[cfg(any(target_os = "android", target_os = "linux"))] accessors! {as_alg_addr, as_alg_addr_mut, AlgAddr, AddressFamily::Alg, libc::sockaddr_alg, alg} @@ -3063,6 +3147,44 @@ mod tests { } } + mod sockaddr_storage { + use super::*; + + #[test] + fn from_sockaddr_un_named() { + let ua = UnixAddr::new("/var/run/mysock").unwrap(); + let ptr = ua.as_ptr() as *const libc::sockaddr; + let ss = unsafe { + SockaddrStorage::from_raw(ptr, Some(ua.len())) + }.unwrap(); + assert_eq!(ss.len(), ua.len()); + } + + #[cfg(any(target_os = "android", target_os = "linux"))] + #[test] + fn from_sockaddr_un_abstract_named() { + let name = String::from("nix\0abstract\0test"); + let ua = UnixAddr::new_abstract(name.as_bytes()).unwrap(); + let ptr = ua.as_ptr() as *const libc::sockaddr; + let ss = unsafe { + SockaddrStorage::from_raw(ptr, Some(ua.len())) + }.unwrap(); + assert_eq!(ss.len(), ua.len()); + } + + #[cfg(any(target_os = "android", target_os = "linux"))] + #[test] + fn from_sockaddr_un_abstract_unnamed() { + let empty = String::new(); + let ua = UnixAddr::new_abstract(empty.as_bytes()).unwrap(); + let ptr = ua.as_ptr() as *const libc::sockaddr; + let ss = unsafe { + SockaddrStorage::from_raw(ptr, Some(ua.len())) + }.unwrap(); + assert_eq!(ss.len(), ua.len()); + } + } + mod unixaddr { use super::*; |