Skip to content

Commit

Permalink
Merge #1871
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
bors[bot] and asomers committed Nov 21, 2022
2 parents 8d27985 + fa62534 commit 9ea1493
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 1 deletion.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
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 @@ -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
Expand Down
122 changes: 122 additions & 0 deletions src/sys/socket/addr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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}
Expand Down Expand Up @@ -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::*;

Expand Down

0 comments on commit 9ea1493

Please sign in to comment.