Skip to content

Commit 6ea77da

Browse files
committed
Correct access of cmsg ancillary data on first header
Also updates cmsg types to match structs on non-Linux OSes. The implementation of CmsgInterator did not correctly mirror the way the CMSG_FIRSTHDR and CMSG_NEXTHDR macros work in C. CMSG_FIRSTHDR does not attempt to align access since the pointer is already aligned due to being part of the msghdr struct. CmsgInterator was always aligning access which happened to work on all platforms except OpenBSD where the use of alignment was adding unexpected bytes to the expected size and causing the `cmsg_align(cmsg_len) > self.buf.len()` guard clause to return early.
1 parent 000366c commit 6ea77da

File tree

2 files changed

+40
-16
lines changed

2 files changed

+40
-16
lines changed

src/sys/socket/ffi.rs

+15-4
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@
33

44
pub use libc::{socket, listen, bind, accept, connect, setsockopt, sendto, recvfrom, getsockname, getpeername, recv, send};
55

6-
use libc::{c_int, c_void, socklen_t, size_t, ssize_t};
6+
use libc::{c_int, c_void, socklen_t, ssize_t};
77

8-
#[cfg(target_os = "macos")]
8+
#[cfg(not(target_os = "macos"))]
9+
use libc::size_t;
10+
11+
#[cfg(not(target_os = "linux"))]
912
use libc::c_uint;
1013

1114
use sys::uio::IoVec;
@@ -23,19 +26,27 @@ pub type type_of_cmsg_data = c_uint;
2326
#[cfg(not(target_os = "macos"))]
2427
pub type type_of_cmsg_data = size_t;
2528

29+
#[cfg(target_os = "linux")]
30+
pub type type_of_msg_iovlen = size_t;
31+
32+
#[cfg(not(target_os = "linux"))]
33+
pub type type_of_msg_iovlen = c_uint;
34+
2635
// Private because we don't expose any external functions that operate
2736
// directly on this type; we just use it internally at FFI boundaries.
2837
// Note that in some cases we store pointers in *const fields that the
2938
// kernel will proceed to mutate, so users should be careful about the
3039
// actual mutability of data pointed to by this structure.
40+
//
41+
// FIXME: Replace these structs with the ones defined in libc
3142
#[repr(C)]
3243
pub struct msghdr<'a> {
3344
pub msg_name: *const c_void,
3445
pub msg_namelen: socklen_t,
3546
pub msg_iov: *const IoVec<&'a [u8]>,
36-
pub msg_iovlen: size_t,
47+
pub msg_iovlen: type_of_msg_iovlen,
3748
pub msg_control: *const c_void,
38-
pub msg_controllen: size_t,
49+
pub msg_controllen: type_of_cmsg_len,
3950
pub msg_flags: c_int,
4051
}
4152

src/sys/socket/mod.rs

+25-12
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ unsafe fn copy_bytes<'a, 'b, T: ?Sized>(src: &T, dst: &'a mut &'b mut [u8]) {
168168
}
169169

170170

171-
use self::ffi::{cmsghdr, msghdr, type_of_cmsg_len, type_of_cmsg_data};
171+
use self::ffi::{cmsghdr, msghdr, type_of_cmsg_data, type_of_msg_iovlen, type_of_cmsg_len};
172172

173173
/// A structure used to make room in a cmsghdr passed to recvmsg. The
174174
/// size and alignment match that of a cmsghdr followed by a T, but the
@@ -204,11 +204,17 @@ impl<'a> RecvMsg<'a> {
204204
/// Iterate over the valid control messages pointed to by this
205205
/// msghdr.
206206
pub fn cmsgs(&self) -> CmsgIterator {
207-
CmsgIterator(self.cmsg_buffer)
207+
CmsgIterator {
208+
buf: self.cmsg_buffer,
209+
next: 0
210+
}
208211
}
209212
}
210213

211-
pub struct CmsgIterator<'a>(&'a [u8]);
214+
pub struct CmsgIterator<'a> {
215+
buf: &'a [u8],
216+
next: usize,
217+
}
212218

213219
impl<'a> Iterator for CmsgIterator<'a> {
214220
type Item = ControlMessage<'a>;
@@ -217,12 +223,11 @@ impl<'a> Iterator for CmsgIterator<'a> {
217223
// although we handle the invariants in slightly different places to
218224
// get a better iterator interface.
219225
fn next(&mut self) -> Option<ControlMessage<'a>> {
220-
let buf = self.0;
221226
let sizeof_cmsghdr = mem::size_of::<cmsghdr>();
222-
if buf.len() < sizeof_cmsghdr {
227+
if self.buf.len() < sizeof_cmsghdr {
223228
return None;
224229
}
225-
let cmsg: &cmsghdr = unsafe { mem::transmute(buf.as_ptr()) };
230+
let cmsg: &cmsghdr = unsafe { mem::transmute(self.buf.as_ptr()) };
226231

227232
// This check is only in the glibc implementation of CMSG_NXTHDR
228233
// (although it claims the kernel header checks this), but such
@@ -232,12 +237,20 @@ impl<'a> Iterator for CmsgIterator<'a> {
232237
return None;
233238
}
234239
let len = cmsg_len - sizeof_cmsghdr;
240+
let aligned_cmsg_len = if self.next == 0 {
241+
// CMSG_FIRSTHDR
242+
cmsg_len
243+
} else {
244+
// CMSG_NXTHDR
245+
cmsg_align(cmsg_len)
246+
};
235247

236248
// Advance our internal pointer.
237-
if cmsg_align(cmsg_len) > buf.len() {
249+
if aligned_cmsg_len > self.buf.len() {
238250
return None;
239251
}
240-
self.0 = &buf[cmsg_align(cmsg_len)..];
252+
self.buf = &self.buf[aligned_cmsg_len..];
253+
self.next += 1;
241254

242255
match (cmsg.cmsg_level, cmsg.cmsg_type) {
243256
(libc::SOL_SOCKET, libc::SCM_RIGHTS) => unsafe {
@@ -370,9 +383,9 @@ pub fn sendmsg<'a>(fd: RawFd, iov: &[IoVec<&'a [u8]>], cmsgs: &[ControlMessage<'
370383
msg_name: name as *const c_void,
371384
msg_namelen: namelen,
372385
msg_iov: iov.as_ptr(),
373-
msg_iovlen: iov.len() as size_t,
386+
msg_iovlen: iov.len() as type_of_msg_iovlen,
374387
msg_control: cmsg_ptr,
375-
msg_controllen: capacity as size_t,
388+
msg_controllen: capacity as type_of_cmsg_len,
376389
msg_flags: 0,
377390
};
378391
let ret = unsafe { ffi::sendmsg(fd, &mhdr, flags.bits()) };
@@ -393,9 +406,9 @@ pub fn recvmsg<'a, T>(fd: RawFd, iov: &[IoVec<&mut [u8]>], cmsg_buffer: Option<&
393406
msg_name: &mut address as *const _ as *const c_void,
394407
msg_namelen: mem::size_of::<sockaddr_storage>() as socklen_t,
395408
msg_iov: iov.as_ptr() as *const IoVec<&[u8]>, // safe cast to add const-ness
396-
msg_iovlen: iov.len() as size_t,
409+
msg_iovlen: iov.len() as type_of_msg_iovlen,
397410
msg_control: msg_control as *const c_void,
398-
msg_controllen: msg_controllen as size_t,
411+
msg_controllen: msg_controllen as type_of_cmsg_len,
399412
msg_flags: 0,
400413
};
401414
let ret = unsafe { ffi::recvmsg(fd, &mut mhdr, flags.bits()) };

0 commit comments

Comments
 (0)