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>
This commit is contained in:
bors[bot] 2022-11-21 17:26:56 +00:00 committed by GitHub
commit 9ea1493a81
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 127 additions and 1 deletions

View File

@ -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

View File

@ -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::*;