From 077d979acb57ecbd1dbe4c1a4b7b1449aa5a14d2 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Wed, 3 Aug 2016 22:11:05 -0600 Subject: [PATCH] Fix nix on FreeBSD amd64 On Linux, the cmsg_len field of struct cmsghdr has type size_t, but it has size socklen_t on POSIX-compliant operating systems. So on POSIX-compliant 64-bit operating systems, struct cmsghdr has padding gaps that aren't present on Linux. Most of the issues fixed by this commit related to those gaps. src/sys/socket/ffi.rs Fix the type of the cmsg_data field so the struct layout will be correct. src/sys/socket/mod.rs In CmsgIterator.next, only return a single file descriptor. sendmsg(2) can only stuff a single file descriptor into each cmsg. In cmsg_align, fix the rounding calculation, and eliminate a division instruction. Add a missing cmsg_align call in ControlMessage.len In ControlMessage.encode_into, add any necessary padding bytes between the cmsghdr and the data. In sendmsg, fix some len<->capacity confusion. --- src/sys/socket/ffi.rs | 14 ++++++++++++-- src/sys/socket/mod.rs | 27 +++++++++++++-------------- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/sys/socket/ffi.rs b/src/sys/socket/ffi.rs index 1cbf766cde..55a47eb642 100644 --- a/src/sys/socket/ffi.rs +++ b/src/sys/socket/ffi.rs @@ -4,8 +4,11 @@ pub use libc::{socket, listen, bind, accept, connect, setsockopt, sendto, recvfrom, getsockname, getpeername, recv, send}; use libc::{c_int, c_void, socklen_t, size_t, ssize_t}; -use sys::uio::IoVec; +#[cfg(target_os = "macos")] +use libc::c_uint; + +use sys::uio::IoVec; #[cfg(target_os = "linux")] pub type type_of_cmsg_len = size_t; @@ -13,6 +16,13 @@ pub type type_of_cmsg_len = size_t; #[cfg(not(target_os = "linux"))] pub type type_of_cmsg_len = socklen_t; +// OSX always aligns struct cmsghdr as if it were a 32-bit OS +#[cfg(target_os = "macos")] +pub type type_of_cmsg_data = c_uint; + +#[cfg(not(target_os = "macos"))] +pub type type_of_cmsg_data = size_t; + // Private because we don't expose any external functions that operate // directly on this type; we just use it internally at FFI boundaries. // Note that in some cases we store pointers in *const fields that the @@ -37,7 +47,7 @@ pub struct cmsghdr { pub cmsg_len: type_of_cmsg_len, pub cmsg_level: c_int, pub cmsg_type: c_int, - pub cmsg_data: [type_of_cmsg_len; 0] + pub cmsg_data: [type_of_cmsg_data; 0] } extern { diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs index 0846eaf52a..69f26aa0a5 100644 --- a/src/sys/socket/mod.rs +++ b/src/sys/socket/mod.rs @@ -94,7 +94,7 @@ unsafe fn copy_bytes<'a, 'b, T: ?Sized>(src: &T, dst: &'a mut &'b mut [u8]) { } -use self::ffi::{cmsghdr, msghdr, type_of_cmsg_len}; +use self::ffi::{cmsghdr, msghdr, type_of_cmsg_len, type_of_cmsg_data}; /// A structure used to make room in a cmsghdr passed to recvmsg. The /// size and alignment match that of a cmsghdr followed by a T, but the @@ -169,8 +169,7 @@ impl<'a> Iterator for CmsgIterator<'a> { (SOL_SOCKET, SCM_RIGHTS) => unsafe { Some(ControlMessage::ScmRights( slice::from_raw_parts( - &cmsg.cmsg_data as *const _ as *const _, - len / mem::size_of::()))) + &cmsg.cmsg_data as *const _ as *const _, 1))) }, (_, _) => unsafe { Some(ControlMessage::Unknown(UnknownCmsg( @@ -201,12 +200,8 @@ pub enum ControlMessage<'a> { pub struct UnknownCmsg<'a>(&'a cmsghdr, &'a [u8]); fn cmsg_align(len: usize) -> usize { - let round_to = mem::size_of::(); - if len % round_to == 0 { - len - } else { - len + round_to - (len % round_to) - } + let align_bytes = mem::size_of::() - 1; + (len + align_bytes) & !align_bytes } impl<'a> ControlMessage<'a> { @@ -217,7 +212,7 @@ impl<'a> ControlMessage<'a> { /// The value of CMSG_LEN on this message. fn len(&self) -> usize { - mem::size_of::() + match *self { + cmsg_align(mem::size_of::()) + match *self { ControlMessage::ScmRights(fds) => { mem::size_of_val(fds) }, @@ -240,7 +235,11 @@ impl<'a> ControlMessage<'a> { cmsg_data: [], }; copy_bytes(&cmsg, buf); - copy_bytes(fds, buf); + + let padlen = cmsg_align(mem::size_of_val(&cmsg)) - + mem::size_of_val(&cmsg); + let buf2 = &mut &mut buf[padlen..]; + copy_bytes(fds, buf2); }, ControlMessage::Unknown(UnknownCmsg(orig_cmsg, bytes)) => { copy_bytes(orig_cmsg, buf); @@ -267,10 +266,10 @@ pub fn sendmsg<'a>(fd: RawFd, iov: &[IoVec<&'a [u8]>], cmsgs: &[ControlMessage<' // multiple of size_t. Note also that the resulting vector claims // to have length == capacity, so it's presently uninitialized. let mut cmsg_buffer = unsafe { - let mut vec = Vec::::with_capacity(capacity / mem::size_of::()); + let mut vec = Vec::::with_capacity(len); let ptr = vec.as_mut_ptr(); mem::forget(vec); - Vec::::from_raw_parts(ptr as *mut _, capacity, capacity) + Vec::::from_raw_parts(ptr as *mut _, len, len) }; { let mut ptr = &mut cmsg_buffer[..]; @@ -290,7 +289,7 @@ pub fn sendmsg<'a>(fd: RawFd, iov: &[IoVec<&'a [u8]>], cmsgs: &[ControlMessage<' msg_iov: iov.as_ptr(), msg_iovlen: iov.len() as size_t, msg_control: cmsg_buffer.as_ptr() as *const c_void, - msg_controllen: len as size_t, + msg_controllen: capacity as size_t, msg_flags: 0, }; let ret = unsafe { ffi::sendmsg(fd, &mhdr, flags.bits()) };