From fa62534a238c86334d41bc1da68267388cda9b36 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Sat, 19 Nov 2022 13:39:31 -0700 Subject: [PATCH] Fix using SockaddrStorage to store Unix domain addresses on Linux 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 --- CHANGELOG.md | 6 +- src/sys/socket/addr.rs | 122 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10a95ad934..6551078852 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)) @@ -38,6 +40,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 4a16eea94e..74420399dc 100644 --- a/src/sys/socket/addr.rs +++ b/src/sys/socket/addr.rs @@ -1537,6 +1537,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 { @@ -1598,6 +1609,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 { @@ -1635,6 +1661,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::() { + 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::() { + 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} @@ -3064,6 +3148,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::*;