Skip to content

Commit fa62534

Browse files
committed
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
1 parent fbebb21 commit fa62534

File tree

2 files changed

+127
-1
lines changed

2 files changed

+127
-1
lines changed

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ This project adheres to [Semantic Versioning](https://semver.org/).
66
## [Unreleased] - ReleaseDate
77
### Added
88

9-
- Add `MntFlags` and `unmount` on all of the BSDs.
9+
- Added `SockaddrStorage::{as_unix_addr, as_unix_addr_mut}`
10+
([#1871](https://github.com/nix-rust/nix/pull/1871))
11+
- Added `MntFlags` and `unmount` on all of the BSDs.
1012
([#1849](https://github.com/nix-rust/nix/pull/1849))
1113
- Added a 'Statfs::flags' method.
1214
([#1849](https://github.com/nix-rust/nix/pull/1849))
@@ -38,6 +40,8 @@ This project adheres to [Semantic Versioning](https://semver.org/).
3840

3941
### Fixed
4042

43+
- Fixed using `SockaddrStorage` to store a Unix-domain socket address on Linux.
44+
([#1871](https://github.com/nix-rust/nix/pull/1871))
4145
- Fix microsecond calculation for `TimeSpec`.
4246
([#1801](https://github.com/nix-rust/nix/pull/1801))
4347
- Fix `User::from_name` and `Group::from_name` panicking

src/sys/socket/addr.rs

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1537,6 +1537,17 @@ impl SockaddrLike for SockaddrStorage {
15371537
let mut ss: libc::sockaddr_storage = mem::zeroed();
15381538
let ssp = &mut ss as *mut libc::sockaddr_storage as *mut u8;
15391539
ptr::copy(addr as *const u8, ssp, len as usize);
1540+
#[cfg(any(
1541+
target_os = "android",
1542+
target_os = "fuchsia",
1543+
target_os = "illumos",
1544+
target_os = "linux"
1545+
))]
1546+
if i32::from(ss.ss_family) == libc::AF_UNIX {
1547+
// Safe because we UnixAddr is strictly smaller than
1548+
// SockaddrStorage, and we just initialized the structure.
1549+
(*(&mut ss as *mut libc::sockaddr_storage as *mut UnixAddr)).sun_len = len as u8;
1550+
}
15401551
Some(Self { ss })
15411552
}
15421553
} else {
@@ -1598,6 +1609,21 @@ impl SockaddrLike for SockaddrStorage {
15981609
}
15991610
}
16001611
}
1612+
1613+
#[cfg(any(
1614+
target_os = "android",
1615+
target_os = "fuchsia",
1616+
target_os = "illumos",
1617+
target_os = "linux"
1618+
))]
1619+
fn len(&self) -> libc::socklen_t {
1620+
match self.as_unix_addr() {
1621+
// The UnixAddr type knows its own length
1622+
Some(ua) => ua.len(),
1623+
// For all else, we're just a boring SockaddrStorage
1624+
None => mem::size_of_val(self) as libc::socklen_t
1625+
}
1626+
}
16011627
}
16021628

16031629
macro_rules! accessors {
@@ -1635,6 +1661,64 @@ macro_rules! accessors {
16351661
}
16361662

16371663
impl SockaddrStorage {
1664+
/// Downcast to an immutable `[UnixAddr]` reference.
1665+
pub fn as_unix_addr(&self) -> Option<&UnixAddr> {
1666+
cfg_if! {
1667+
if #[cfg(any(target_os = "android",
1668+
target_os = "fuchsia",
1669+
target_os = "illumos",
1670+
target_os = "linux"
1671+
))]
1672+
{
1673+
let p = unsafe{ &self.ss as *const libc::sockaddr_storage };
1674+
// Safe because UnixAddr is strictly smaller than
1675+
// sockaddr_storage, and we're fully initialized
1676+
let len = unsafe {
1677+
(*(p as *const UnixAddr )).sun_len as usize
1678+
};
1679+
} else {
1680+
let len = self.len() as usize;
1681+
}
1682+
}
1683+
// Sanity checks
1684+
if self.family() != Some(AddressFamily::Unix) ||
1685+
len < offset_of!(libc::sockaddr_un, sun_path) ||
1686+
len > mem::size_of::<libc::sockaddr_un>() {
1687+
None
1688+
} else {
1689+
Some(unsafe{&self.su})
1690+
}
1691+
}
1692+
1693+
/// Downcast to a mutable `[UnixAddr]` reference.
1694+
pub fn as_unix_addr_mut(&mut self) -> Option<&mut UnixAddr> {
1695+
cfg_if! {
1696+
if #[cfg(any(target_os = "android",
1697+
target_os = "fuchsia",
1698+
target_os = "illumos",
1699+
target_os = "linux"
1700+
))]
1701+
{
1702+
let p = unsafe{ &self.ss as *const libc::sockaddr_storage };
1703+
// Safe because UnixAddr is strictly smaller than
1704+
// sockaddr_storage, and we're fully initialized
1705+
let len = unsafe {
1706+
(*(p as *const UnixAddr )).sun_len as usize
1707+
};
1708+
} else {
1709+
let len = self.len() as usize;
1710+
}
1711+
}
1712+
// Sanity checks
1713+
if self.family() != Some(AddressFamily::Unix) ||
1714+
len < offset_of!(libc::sockaddr_un, sun_path) ||
1715+
len > mem::size_of::<libc::sockaddr_un>() {
1716+
None
1717+
} else {
1718+
Some(unsafe{&mut self.su})
1719+
}
1720+
}
1721+
16381722
#[cfg(any(target_os = "android", target_os = "linux"))]
16391723
accessors! {as_alg_addr, as_alg_addr_mut, AlgAddr,
16401724
AddressFamily::Alg, libc::sockaddr_alg, alg}
@@ -3064,6 +3148,44 @@ mod tests {
30643148
}
30653149
}
30663150

3151+
mod sockaddr_storage {
3152+
use super::*;
3153+
3154+
#[test]
3155+
fn from_sockaddr_un_named() {
3156+
let ua = UnixAddr::new("/var/run/mysock").unwrap();
3157+
let ptr = ua.as_ptr() as *const libc::sockaddr;
3158+
let ss = unsafe {
3159+
SockaddrStorage::from_raw(ptr, Some(ua.len()))
3160+
}.unwrap();
3161+
assert_eq!(ss.len(), ua.len());
3162+
}
3163+
3164+
#[cfg(any(target_os = "android", target_os = "linux"))]
3165+
#[test]
3166+
fn from_sockaddr_un_abstract_named() {
3167+
let name = String::from("nix\0abstract\0test");
3168+
let ua = UnixAddr::new_abstract(name.as_bytes()).unwrap();
3169+
let ptr = ua.as_ptr() as *const libc::sockaddr;
3170+
let ss = unsafe {
3171+
SockaddrStorage::from_raw(ptr, Some(ua.len()))
3172+
}.unwrap();
3173+
assert_eq!(ss.len(), ua.len());
3174+
}
3175+
3176+
#[cfg(any(target_os = "android", target_os = "linux"))]
3177+
#[test]
3178+
fn from_sockaddr_un_abstract_unnamed() {
3179+
let empty = String::new();
3180+
let ua = UnixAddr::new_abstract(empty.as_bytes()).unwrap();
3181+
let ptr = ua.as_ptr() as *const libc::sockaddr;
3182+
let ss = unsafe {
3183+
SockaddrStorage::from_raw(ptr, Some(ua.len()))
3184+
}.unwrap();
3185+
assert_eq!(ss.len(), ua.len());
3186+
}
3187+
}
3188+
30673189
mod unixaddr {
30683190
use super::*;
30693191

0 commit comments

Comments
 (0)