@@ -38,15 +38,27 @@ impl InterfaceData {
38
38
}
39
39
}
40
40
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.
41
46
struct InterfaceIterator {
42
47
base : * mut libc:: ifaddrs ,
43
- next : * mut libc:: ifaddrs ,
48
+ next : * const libc:: ifaddrs ,
44
49
}
45
50
46
51
impl InterfaceIterator {
47
52
pub fn new ( ) -> std:: io:: Result < Self > {
48
53
let mut addrs: * mut libc:: ifaddrs = std:: ptr:: null_mut ( ) ;
49
54
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
50
62
unsafe {
51
63
cerr ( libc:: getifaddrs ( & mut addrs) ) ?;
52
64
@@ -62,6 +74,8 @@ impl InterfaceIterator {
62
74
63
75
impl Drop for InterfaceIterator {
64
76
fn drop ( & mut self ) {
77
+ // Safety:
78
+ // By the invariants, self.base is guaranteed to point to a memory region allocated by getifaddrs
65
79
unsafe { libc:: freeifaddrs ( self . base ) } ;
66
80
}
67
81
}
@@ -76,25 +90,35 @@ impl Iterator for InterfaceIterator {
76
90
type Item = InterfaceDataInternal ;
77
91
78
92
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.
79
95
let ifaddr = unsafe { self . next . as_ref ( ) } ?;
80
96
97
+ // Invariant preservation
98
+ // By the guarantees given by getifaddrs, ifaddr.ifa_next is either null or points to a valid
99
+ // ifaddr.
81
100
self . next = ifaddr. ifa_next ;
82
101
102
+ // Safety:
103
+ // getifaddrs guarantees that ifa_name is not null and points to a valid C string.
83
104
let ifname = unsafe { std:: ffi:: CStr :: from_ptr ( ifaddr. ifa_name ) } ;
84
105
let name = match std:: str:: from_utf8 ( ifname. to_bytes ( ) ) {
85
106
Err ( _) => unreachable ! ( "interface names must be ascii" ) ,
86
107
Ok ( name) => InterfaceName :: from_str ( name) . expect ( "name from os" ) ,
87
108
} ;
88
109
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 ) ;
90
113
91
114
#[ allow( unused) ]
92
115
let mac: Option < [ u8 ; 6 ] > = None ;
93
116
94
117
#[ 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 _ ) {
98
122
let sockaddr_ll: libc:: sockaddr_ll =
99
123
unsafe { std:: ptr:: read_unaligned ( ifaddr. ifa_addr as * const _ ) } ;
100
124
@@ -111,9 +135,10 @@ impl Iterator for InterfaceIterator {
111
135
} ;
112
136
113
137
#[ 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
117
142
let sockaddr_dl: libc:: sockaddr_dl =
118
143
unsafe { std:: ptr:: read_unaligned ( ifaddr. ifa_addr as * const _ ) } ;
119
144
@@ -138,6 +163,7 @@ impl Iterator for InterfaceIterator {
138
163
None
139
164
} ;
140
165
166
+ // Safety: ifaddr.ifa_addr is always either NULL, or by the guarantees of getifaddrs, points to a valid address.
141
167
let socket_addr = unsafe { sockaddr_to_socket_addr ( ifaddr. ifa_addr ) } ;
142
168
143
169
let data = InterfaceDataInternal {
@@ -206,7 +232,7 @@ impl InterfaceName {
206
232
pub fn get_index ( & self ) -> Option < libc:: c_uint > {
207
233
// # SAFETY
208
234
//
209
- // The pointer is valid and null- terminated
235
+ // self lives for the duration of the call, and is null terminated.
210
236
match unsafe { libc:: if_nametoindex ( self . as_cstr ( ) . as_ptr ( ) ) } {
211
237
0 => None ,
212
238
n => Some ( n) ,
@@ -266,22 +292,25 @@ impl<'de> serde::Deserialize<'de> for InterfaceName {
266
292
///
267
293
/// # Safety
268
294
///
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.
276
296
unsafe fn sockaddr_to_socket_addr ( sockaddr : * const libc:: sockaddr ) -> Option < SocketAddr > {
277
297
// Most (but not all) of the fields in a socket addr are in network byte
278
298
// ordering. As such, when doing conversions here, we should start from the
279
299
// NATIVE byte representation, as this will actualy be the big-endian
280
300
// 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
281
308
match unsafe { ( * sockaddr) . sa_family as libc:: c_int } {
282
309
libc:: AF_INET => {
283
310
// SAFETY: we cast from a libc::sockaddr (alignment 2) to a libc::sockaddr_in (alignment 4)
284
311
// 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
285
314
let inaddr: libc:: sockaddr_in =
286
315
unsafe { std:: ptr:: read_unaligned ( sockaddr as * const libc:: sockaddr_in ) } ;
287
316
@@ -295,9 +324,12 @@ unsafe fn sockaddr_to_socket_addr(sockaddr: *const libc::sockaddr) -> Option<Soc
295
324
libc:: AF_INET6 => {
296
325
// SAFETY: we cast from a libc::sockaddr (alignment 2) to a libc::sockaddr_in6 (alignment 4)
297
326
// 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
298
328
let inaddr: libc:: sockaddr_in6 =
299
329
unsafe { std:: ptr:: read_unaligned ( sockaddr as * const libc:: sockaddr_in6 ) } ;
300
330
331
+ // Safety:
332
+ // sin_addr lives for the duration fo the call and matches type
301
333
let sin_addr = inaddr. sin6_addr . s6_addr ;
302
334
let segment_bytes: [ u8 ; 16 ] =
303
335
unsafe { std:: ptr:: read_unaligned ( & sin_addr as * const _ as * const _ ) } ;
0 commit comments