diff options
author | Alan Somers <asomers@gmail.com> | 2016-08-03 22:11:05 -0600 |
---|---|---|
committer | Philipp Matthias Schaefer <philipp.matthias.schaefer@posteo.de> | 2016-08-10 21:12:53 +0200 |
commit | 077d979acb57ecbd1dbe4c1a4b7b1449aa5a14d2 (patch) | |
tree | 078511f3b8dd0e83e0442f6e206fe910b9216659 /src | |
parent | 36789c78a726d9ccf2f3b2fa2eccdfb78b28c664 (diff) | |
download | nix-077d979acb57ecbd1dbe4c1a4b7b1449aa5a14d2.zip |
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.
Diffstat (limited to 'src')
-rw-r--r-- | src/sys/socket/ffi.rs | 14 | ||||
-rw-r--r-- | 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 1cbf766c..55a47eb6 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 0846eaf5..69f26aa0 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::<RawFd>()))) + &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::<type_of_cmsg_len>(); - if len % round_to == 0 { - len - } else { - len + round_to - (len % round_to) - } + let align_bytes = mem::size_of::<type_of_cmsg_data>() - 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::<cmsghdr>() + match *self { + cmsg_align(mem::size_of::<cmsghdr>()) + 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::<size_t>::with_capacity(capacity / mem::size_of::<size_t>()); + let mut vec = Vec::<u8>::with_capacity(len); let ptr = vec.as_mut_ptr(); mem::forget(vec); - Vec::<u8>::from_raw_parts(ptr as *mut _, capacity, capacity) + Vec::<u8>::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()) }; |