Skip to content

Commit

Permalink
Merge #1788
Browse files Browse the repository at this point in the history
1788: Workaround XNU bug in getifaddrs netmasks r=asomers a=roblabla

Fixes #1709 

Co-authored-by: roblabla <unfiltered@roblab.la>
Co-authored-by: Alan Somers <asomers@gmail.com>
  • Loading branch information
3 people committed Nov 29, 2022
2 parents be578c6 + 9e6951f commit 5db5581
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 1 deletion.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ This project adheres to [Semantic Versioning](https://semver.org/).
([#1815](https://github.com/nix-rust/nix/pull/1815))
- Fix `User::from_uid` and `User::from_name` crash on Android platform.
([#1824](https://github.com/nix-rust/nix/pull/1824))

- Workaround XNU bug causing netmasks returned by `getifaddrs` to misbehave.
([#1788](https://github.com/nix-rust/nix/pull/1788))

### Removed

- Removed deprecated error constants and conversions.
Expand Down
65 changes: 65 additions & 0 deletions src/ifaddrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
//! of interfaces and their associated addresses.

use cfg_if::cfg_if;
#[cfg(any(target_os = "ios", target_os = "macos"))]
use std::convert::TryFrom;
use std::ffi;
use std::iter::Iterator;
use std::mem;
Expand Down Expand Up @@ -42,11 +44,50 @@ cfg_if! {
}
}

/// Workaround a bug in XNU where netmasks will always have the wrong size in
/// the sa_len field due to the kernel ignoring trailing zeroes in the structure
/// when setting the field. See https://github.com/nix-rust/nix/issues/1709#issuecomment-1199304470
///
/// To fix this, we stack-allocate a new sockaddr_storage, zero it out, and
/// memcpy sa_len of the netmask to that new storage. Finally, we reset the
/// ss_len field to sizeof(sockaddr_storage). This is supposedly valid as all
/// members of the sockaddr_storage are "ok" with being zeroed out (there are
/// no pointers).
#[cfg(any(target_os = "ios", target_os = "macos"))]
unsafe fn workaround_xnu_bug(info: &libc::ifaddrs) -> Option<SockaddrStorage> {
let src_sock = info.ifa_netmask;
if src_sock.is_null() {
return None;
}

let mut dst_sock = mem::MaybeUninit::<libc::sockaddr_storage>::zeroed();

// memcpy only sa_len bytes, assume the rest is zero
std::ptr::copy_nonoverlapping(
src_sock as *const u8,
dst_sock.as_mut_ptr() as *mut u8,
(*src_sock).sa_len.into(),
);

// Initialize ss_len to sizeof(libc::sockaddr_storage).
(*dst_sock.as_mut_ptr()).ss_len =
u8::try_from(mem::size_of::<libc::sockaddr_storage>()).unwrap();
let dst_sock = dst_sock.assume_init();

let dst_sock_ptr =
&dst_sock as *const libc::sockaddr_storage as *const libc::sockaddr;

SockaddrStorage::from_raw(dst_sock_ptr, None)
}

impl InterfaceAddress {
/// Create an `InterfaceAddress` from the libc struct.
fn from_libc_ifaddrs(info: &libc::ifaddrs) -> InterfaceAddress {
let ifname = unsafe { ffi::CStr::from_ptr(info.ifa_name) };
let address = unsafe { SockaddrStorage::from_raw(info.ifa_addr, None) };
#[cfg(any(target_os = "ios", target_os = "macos"))]
let netmask = unsafe { workaround_xnu_bug(info) };
#[cfg(not(any(target_os = "ios", target_os = "macos")))]
let netmask =
unsafe { SockaddrStorage::from_raw(info.ifa_netmask, None) };
let mut addr = InterfaceAddress {
Expand Down Expand Up @@ -145,4 +186,28 @@ mod tests {
fn test_getifaddrs() {
let _ = getifaddrs();
}

// Ensures getting the netmask works, and in particular that
// `workaround_xnu_bug` works properly.
#[test]
fn test_getifaddrs_netmask_correct() {
let addrs = getifaddrs().unwrap();
for iface in addrs {
let sock = if let Some(sock) = iface.netmask {
sock
} else {
continue;
};
if sock.family() == Some(crate::sys::socket::AddressFamily::Inet) {
let _ = sock.as_sockaddr_in().unwrap();
return;
} else if sock.family()
== Some(crate::sys::socket::AddressFamily::Inet6)
{
let _ = sock.as_sockaddr_in6().unwrap();
return;
}
}
panic!("No address?");
}
}

0 comments on commit 5db5581

Please sign in to comment.