Skip to content

Commit 5db5581

Browse files
bors[bot]roblablaasomers
authored
Merge #1788
1788: Workaround XNU bug in getifaddrs netmasks r=asomers a=roblabla Fixes #1709 Co-authored-by: roblabla <[email protected]> Co-authored-by: Alan Somers <[email protected]>
2 parents be578c6 + 9e6951f commit 5db5581

File tree

2 files changed

+68
-1
lines changed

2 files changed

+68
-1
lines changed

CHANGELOG.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,9 @@ This project adheres to [Semantic Versioning](https://semver.org/).
6464
([#1815](https://github.com/nix-rust/nix/pull/1815))
6565
- Fix `User::from_uid` and `User::from_name` crash on Android platform.
6666
([#1824](https://github.com/nix-rust/nix/pull/1824))
67-
67+
- Workaround XNU bug causing netmasks returned by `getifaddrs` to misbehave.
68+
([#1788](https://github.com/nix-rust/nix/pull/1788))
69+
6870
### Removed
6971

7072
- Removed deprecated error constants and conversions.

src/ifaddrs.rs

+65
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
//! of interfaces and their associated addresses.
55
66
use cfg_if::cfg_if;
7+
#[cfg(any(target_os = "ios", target_os = "macos"))]
8+
use std::convert::TryFrom;
79
use std::ffi;
810
use std::iter::Iterator;
911
use std::mem;
@@ -42,11 +44,50 @@ cfg_if! {
4244
}
4345
}
4446

47+
/// Workaround a bug in XNU where netmasks will always have the wrong size in
48+
/// the sa_len field due to the kernel ignoring trailing zeroes in the structure
49+
/// when setting the field. See https://github.com/nix-rust/nix/issues/1709#issuecomment-1199304470
50+
///
51+
/// To fix this, we stack-allocate a new sockaddr_storage, zero it out, and
52+
/// memcpy sa_len of the netmask to that new storage. Finally, we reset the
53+
/// ss_len field to sizeof(sockaddr_storage). This is supposedly valid as all
54+
/// members of the sockaddr_storage are "ok" with being zeroed out (there are
55+
/// no pointers).
56+
#[cfg(any(target_os = "ios", target_os = "macos"))]
57+
unsafe fn workaround_xnu_bug(info: &libc::ifaddrs) -> Option<SockaddrStorage> {
58+
let src_sock = info.ifa_netmask;
59+
if src_sock.is_null() {
60+
return None;
61+
}
62+
63+
let mut dst_sock = mem::MaybeUninit::<libc::sockaddr_storage>::zeroed();
64+
65+
// memcpy only sa_len bytes, assume the rest is zero
66+
std::ptr::copy_nonoverlapping(
67+
src_sock as *const u8,
68+
dst_sock.as_mut_ptr() as *mut u8,
69+
(*src_sock).sa_len.into(),
70+
);
71+
72+
// Initialize ss_len to sizeof(libc::sockaddr_storage).
73+
(*dst_sock.as_mut_ptr()).ss_len =
74+
u8::try_from(mem::size_of::<libc::sockaddr_storage>()).unwrap();
75+
let dst_sock = dst_sock.assume_init();
76+
77+
let dst_sock_ptr =
78+
&dst_sock as *const libc::sockaddr_storage as *const libc::sockaddr;
79+
80+
SockaddrStorage::from_raw(dst_sock_ptr, None)
81+
}
82+
4583
impl InterfaceAddress {
4684
/// Create an `InterfaceAddress` from the libc struct.
4785
fn from_libc_ifaddrs(info: &libc::ifaddrs) -> InterfaceAddress {
4886
let ifname = unsafe { ffi::CStr::from_ptr(info.ifa_name) };
4987
let address = unsafe { SockaddrStorage::from_raw(info.ifa_addr, None) };
88+
#[cfg(any(target_os = "ios", target_os = "macos"))]
89+
let netmask = unsafe { workaround_xnu_bug(info) };
90+
#[cfg(not(any(target_os = "ios", target_os = "macos")))]
5091
let netmask =
5192
unsafe { SockaddrStorage::from_raw(info.ifa_netmask, None) };
5293
let mut addr = InterfaceAddress {
@@ -145,4 +186,28 @@ mod tests {
145186
fn test_getifaddrs() {
146187
let _ = getifaddrs();
147188
}
189+
190+
// Ensures getting the netmask works, and in particular that
191+
// `workaround_xnu_bug` works properly.
192+
#[test]
193+
fn test_getifaddrs_netmask_correct() {
194+
let addrs = getifaddrs().unwrap();
195+
for iface in addrs {
196+
let sock = if let Some(sock) = iface.netmask {
197+
sock
198+
} else {
199+
continue;
200+
};
201+
if sock.family() == Some(crate::sys::socket::AddressFamily::Inet) {
202+
let _ = sock.as_sockaddr_in().unwrap();
203+
return;
204+
} else if sock.family()
205+
== Some(crate::sys::socket::AddressFamily::Inet6)
206+
{
207+
let _ = sock.as_sockaddr_in6().unwrap();
208+
return;
209+
}
210+
}
211+
panic!("No address?");
212+
}
148213
}

0 commit comments

Comments
 (0)