Skip to content

Commit

Permalink
Fix using SockaddrStorage to store Unix domain addresses on Linux
Browse files Browse the repository at this point in the history
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
  • Loading branch information
asomers committed Nov 19, 2022
1 parent fbebb21 commit fa62534
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 1 deletion.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Expand Up @@ -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))
Expand Down Expand Up @@ -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
Expand Down
122 changes: 122 additions & 0 deletions src/sys/socket/addr.rs
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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::<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}
Expand Down Expand Up @@ -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::*;

Expand Down

0 comments on commit fa62534

Please sign in to comment.