Skip to content

Commit 7597c26

Browse files
committed
Fixed null pointer dereference and cleaned up all safety arguments.
1 parent fa60ce6 commit 7597c26

File tree

5 files changed

+86
-16
lines changed

5 files changed

+86
-16
lines changed

src/control_message.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ pub(crate) enum MessageQueue {
1313
Error,
1414
}
1515

16+
// Invariants:
17+
// self.mhdr points to a valid libc::msghdr with a valid control
18+
// message region.
19+
// self.next_msg points to one of the control messages
20+
// in the region described by self.mhdr or is NULL
21+
//
22+
// These invariants are guaranteed from the safety conditions on
23+
// calling ControlMessageIterator::new, the fact that next preserves
1624
// these invariants and that the fields of ControlMessageIterator
1725
// are not modified outside these two functions.
1826
pub(crate) struct ControlMessageIterator<'a> {

src/interface.rs

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,27 @@ impl InterfaceData {
3838
}
3939
}
4040

41+
// Invariants:
42+
// self.base always contains a pointer received from libc::getifaddrs that is not NULL. The region pointed to is never modified in rust code.
43+
// self.next always contains either a pointer pointing to a valid ifaddr received from libc::getifaddrs or null.
44+
//
45+
// These invariants are setup by InterfaceIterator::new and guaranteed by drop and next, which are the only places these pointers are used.
4146
struct InterfaceIterator {
4247
base: *mut libc::ifaddrs,
43-
next: *mut libc::ifaddrs,
48+
next: *const libc::ifaddrs,
4449
}
4550

4651
impl InterfaceIterator {
4752
pub fn new() -> std::io::Result<Self> {
4853
let mut addrs: *mut libc::ifaddrs = std::ptr::null_mut();
4954

55+
// Safety:
56+
// addrs lives for the duration of the call to getifaddrs.
57+
//
58+
// Invariant preservation:
59+
// we validate that the received address is not null, and
60+
// by the guarantees from getifaddrs points to a valid
61+
// ifaddr returned from getifaddrs
5062
unsafe {
5163
cerr(libc::getifaddrs(&mut addrs))?;
5264

@@ -62,6 +74,8 @@ impl InterfaceIterator {
6274

6375
impl Drop for InterfaceIterator {
6476
fn drop(&mut self) {
77+
// Safety:
78+
// By the invariants, self.base is guaranteed to point to a memory region allocated by getifaddrs
6579
unsafe { libc::freeifaddrs(self.base) };
6680
}
6781
}
@@ -76,25 +90,35 @@ impl Iterator for InterfaceIterator {
7690
type Item = InterfaceDataInternal;
7791

7892
fn next(&mut self) -> Option<<Self as Iterator>::Item> {
93+
// Safety:
94+
// By the invariants, self.next is guaranteed to be a valid pointer to an ifaddrs struct or null.
7995
let ifaddr = unsafe { self.next.as_ref() }?;
8096

97+
// Invariant preservation
98+
// By the guarantees given by getifaddrs, ifaddr.ifa_next is either null or points to a valid
99+
// ifaddr.
81100
self.next = ifaddr.ifa_next;
82101

102+
// Safety:
103+
// getifaddrs guarantees that ifa_name is not null and points to a valid C string.
83104
let ifname = unsafe { std::ffi::CStr::from_ptr(ifaddr.ifa_name) };
84105
let name = match std::str::from_utf8(ifname.to_bytes()) {
85106
Err(_) => unreachable!("interface names must be ascii"),
86107
Ok(name) => InterfaceName::from_str(name).expect("name from os"),
87108
};
88109

89-
let family = unsafe { (*ifaddr.ifa_addr).sa_family };
110+
// Safety:
111+
// getifaddrs guarantees that ifa_addr either points to a valid address or is NULL.
112+
let family = unsafe { ifaddr.ifa_addr.as_ref() }.map(|a| a.sa_family);
90113

91114
#[allow(unused)]
92115
let mac: Option<[u8; 6]> = None;
93116

94117
#[cfg(target_os = "linux")]
95-
// Safety: getifaddrs ensures that all addresses are valid, and a valid address of type
96-
// AF_PACKET always is reinterpret castable to sockaddr_ll
97-
let mac = if family as i32 == libc::AF_PACKET {
118+
// Safety: getifaddrs ensures that, if an address is present, it is valid. A valid address
119+
// of type AF_PACKET is always reinterpret castable to sockaddr_ll, and we know an address
120+
// is present since family is not None
121+
let mac = if family == Some(libc::AF_PACKET as _) {
98122
let sockaddr_ll: libc::sockaddr_ll =
99123
unsafe { std::ptr::read_unaligned(ifaddr.ifa_addr as *const _) };
100124

@@ -111,9 +135,10 @@ impl Iterator for InterfaceIterator {
111135
};
112136

113137
#[cfg(any(target_os = "freebsd", target_os = "macos"))]
114-
let mac = if family as i32 == libc::AF_LINK {
115-
// Safety: getifaddrs ensures that all addresses are valid, and a valid address of type
116-
// AF_LINK always is reinterpret castable to sockaddr_dl
138+
let mac = if family == Some(libc::AF_LINK as _) {
139+
// Safety: getifaddrs ensures that, if an address is present, it is valid. A valid address
140+
// of type AF_LINK is always reinterpret castable to sockaddr_ll, and we know an address
141+
// is present since family is not None
117142
let sockaddr_dl: libc::sockaddr_dl =
118143
unsafe { std::ptr::read_unaligned(ifaddr.ifa_addr as *const _) };
119144

@@ -138,6 +163,7 @@ impl Iterator for InterfaceIterator {
138163
None
139164
};
140165

166+
// Safety: ifaddr.ifa_addr is always either NULL, or by the guarantees of getifaddrs, points to a valid address.
141167
let socket_addr = unsafe { sockaddr_to_socket_addr(ifaddr.ifa_addr) };
142168

143169
let data = InterfaceDataInternal {
@@ -206,7 +232,7 @@ impl InterfaceName {
206232
pub fn get_index(&self) -> Option<libc::c_uint> {
207233
// # SAFETY
208234
//
209-
// The pointer is valid and null-terminated
235+
// self lives for the duration of the call, and is null terminated.
210236
match unsafe { libc::if_nametoindex(self.as_cstr().as_ptr()) } {
211237
0 => None,
212238
n => Some(n),
@@ -266,22 +292,25 @@ impl<'de> serde::Deserialize<'de> for InterfaceName {
266292
///
267293
/// # Safety
268294
///
269-
/// According to the posix standard, `sockaddr` does not have a defined size:
270-
/// the size depends on the value of the `ss_family` field. We assume this to be
271-
/// correct.
272-
///
273-
/// In practice, types in rust/c need a statically-known stack size, so they
274-
/// pick some value. In practice it can be (and is) larger than the
275-
/// `sizeof<libc::sockaddr>` value.
295+
/// This function assumes that sockaddr is either NULL or points to a valid address.
276296
unsafe fn sockaddr_to_socket_addr(sockaddr: *const libc::sockaddr) -> Option<SocketAddr> {
277297
// Most (but not all) of the fields in a socket addr are in network byte
278298
// ordering. As such, when doing conversions here, we should start from the
279299
// NATIVE byte representation, as this will actualy be the big-endian
280300
// representation of the underlying value regardless of platform.
301+
302+
// Check for null pointers
303+
if sockaddr.is_null() {
304+
return None;
305+
}
306+
307+
// Safety: by the previous check, sockaddr is not NULL and hence points to a valid address
281308
match unsafe { (*sockaddr).sa_family as libc::c_int } {
282309
libc::AF_INET => {
283310
// SAFETY: we cast from a libc::sockaddr (alignment 2) to a libc::sockaddr_in (alignment 4)
284311
// that means that the pointer is now potentially unaligned. We must used read_unaligned!
312+
// However, the rest of the cast is safe as a valid AF_INET address is always reinterpret castable
313+
// as a sockaddr_in
285314
let inaddr: libc::sockaddr_in =
286315
unsafe { std::ptr::read_unaligned(sockaddr as *const libc::sockaddr_in) };
287316

@@ -295,9 +324,12 @@ unsafe fn sockaddr_to_socket_addr(sockaddr: *const libc::sockaddr) -> Option<Soc
295324
libc::AF_INET6 => {
296325
// SAFETY: we cast from a libc::sockaddr (alignment 2) to a libc::sockaddr_in6 (alignment 4)
297326
// that means that the pointer is now potentially unaligned. We must used read_unaligned!
327+
// However, the cast is safe as a valid AF_INET6 address is always reinterpret catable as a sockaddr_in6
298328
let inaddr: libc::sockaddr_in6 =
299329
unsafe { std::ptr::read_unaligned(sockaddr as *const libc::sockaddr_in6) };
300330

331+
// Safety:
332+
// sin_addr lives for the duration fo the call and matches type
301333
let sin_addr = inaddr.sin6_addr.s6_addr;
302334
let segment_bytes: [u8; 16] =
303335
unsafe { std::ptr::read_unaligned(&sin_addr as *const _ as *const _) };

src/raw_socket.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ impl RawSocket {
6565

6666
pub(crate) fn set_nonblocking(&self, nonblocking: bool) -> std::io::Result<()> {
6767
let nonblocking = nonblocking as libc::c_int;
68+
// Safety: nonblocking lives for the duration of the call, and is 4 bytes long as expected for FIONBIO
6869
cerr(unsafe { libc::ioctl(self.fd, libc::FIONBIO, &nonblocking) }).map(drop)
6970
}
7071

src/raw_socket/freebsd.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ impl RawSocket {
3131
}?;
3232
if options != 0 {
3333
let clock = libc::SO_TS_REALTIME as u32;
34+
// Safety:
35+
//
36+
// - The socket is proviided by (safe) rust, and will outlive the call
37+
// - method is guaranteed to be a valid "name" argument
38+
// - clock outlives the call
39+
// - option_len corresponds with the size of clock
3440
unsafe {
3541
cerr(libc::setsockopt(
3642
self.fd,

src/raw_socket/linux.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ impl RawSocket {
5252
},
5353
};
5454

55+
// Safety:
56+
// ifreq lives for the duration of the call, ioctl is safe to call otherwise
5557
cerr(unsafe { libc::ioctl(self.fd, libc::SIOCSHWTSTAMP as _, &mut ifreq) })?;
5658
Ok(())
5759
}
@@ -60,6 +62,9 @@ impl RawSocket {
6062
let value = interface_name.as_str().as_bytes();
6163
let len = value.len();
6264

65+
// Safety:
66+
// value lives for the duration of the call, and is of size len.
67+
// setsockopt is safe to call in all other regards
6368
unsafe {
6469
cerr(libc::setsockopt(
6570
self.fd,
@@ -86,6 +91,9 @@ impl RawSocket {
8691
.ok_or(std::io::ErrorKind::InvalidInput)? as _,
8792
};
8893

94+
// Safety:
95+
// request lives for the duration of the call, and we pass its size
96+
// as option_len. setsockopt is safe to call in all other regards
8997
cerr(unsafe {
9098
libc::setsockopt(
9199
self.fd,
@@ -103,6 +111,9 @@ impl RawSocket {
103111
.get_index()
104112
.ok_or(std::io::ErrorKind::InvalidInput)?;
105113

114+
// Safety:
115+
// index lives for the duration of the call, and we pass its size
116+
// as option_len. setsockopt is safe to call in all other regards
106117
cerr(unsafe {
107118
libc::setsockopt(
108119
self.fd,
@@ -117,6 +128,10 @@ impl RawSocket {
117128

118129
pub(crate) fn ip_multicast_loop(&self, enabled: bool) -> std::io::Result<()> {
119130
let state: i32 = if enabled { 1 } else { 0 };
131+
132+
// Safety:
133+
// state lives for the duration of the call, and we pass its size
134+
// as option_len. setsockopt is safe to call in all other regards.
120135
cerr(unsafe {
121136
libc::setsockopt(
122137
self.fd,
@@ -131,6 +146,10 @@ impl RawSocket {
131146

132147
pub(crate) fn ipv6_multicast_loop(&self, enabled: bool) -> std::io::Result<()> {
133148
let state: i32 = if enabled { 1 } else { 0 };
149+
150+
// Safety:
151+
// state lives for the duration of the call, and we pass its size
152+
// as option_len. setsockopt is safe to call in all other regards.
134153
cerr(unsafe {
135154
libc::setsockopt(
136155
self.fd,
@@ -145,6 +164,10 @@ impl RawSocket {
145164

146165
pub(crate) fn ipv6_v6only(&self, enabled: bool) -> std::io::Result<()> {
147166
let state: i32 = if enabled { 1 } else { 0 };
167+
168+
// Safety:
169+
// state lives for the duration of the call, and we pass its size
170+
// as option_len. setsockopt is safe to call in all other regards.
148171
cerr(unsafe {
149172
libc::setsockopt(
150173
self.fd,

0 commit comments

Comments
 (0)