From bae5a373391eced579c7574fda942b9db4a55c3f Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Mon, 25 Jul 2016 02:41:24 +0000 Subject: [PATCH 1/3] Fix the sethostname binding on FreeBSD and DragonflyBSD --- src/unistd.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/unistd.rs b/src/unistd.rs index 6ab1d920e4..d4da60da68 100644 --- a/src/unistd.rs +++ b/src/unistd.rs @@ -176,7 +176,10 @@ pub fn daemon(nochdir: bool, noclose: bool) -> Result<()> { pub fn sethostname(name: &[u8]) -> Result<()> { // Handle some differences in type of the len arg across platforms. cfg_if! { - if #[cfg(any(target_os = "macos", target_os = "ios"))] { + if #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", ))] { type sethostname_len_t = c_int; } else { type sethostname_len_t = size_t; From 36789c78a726d9ccf2f3b2fa2eccdfb78b28c664 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Mon, 25 Jul 2016 02:42:07 +0000 Subject: [PATCH 2/3] Fix the sockopt_impl matcher rule order. Rules for generic types were located above rules for specific types, so the rules for specific types never got matched. This caused the sys::socket::sockopt::test::can_get_listen_on_tcp_socket test to fail on FreeBSD. The solution is to put all of the generic rules at the bottom. --- src/sys/socket/sockopt.rs | 58 +++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/src/sys/socket/sockopt.rs b/src/sys/socket/sockopt.rs index 8f30d628c3..bf17347cf3 100644 --- a/src/sys/socket/sockopt.rs +++ b/src/sys/socket/sockopt.rs @@ -49,10 +49,6 @@ macro_rules! getsockopt_impl { // Helper to generate the sockopt accessors macro_rules! sockopt_impl { - (GetOnly, $name:ident, $level:path, $flag:path, $ty:ty) => { - sockopt_impl!(GetOnly, $name, $level, $flag, $ty, GetStruct<$ty>); - }; - (GetOnly, $name:ident, $level:path, $flag:path, bool) => { sockopt_impl!(GetOnly, $name, $level, $flag, bool, GetBool); }; @@ -65,17 +61,6 @@ macro_rules! sockopt_impl { sockopt_impl!(GetOnly, $name, $level, $flag, usize, GetUsize); }; - (GetOnly, $name:ident, $level:path, $flag:path, $ty:ty, $getter:ty) => { - #[derive(Copy, Clone, Debug)] - pub struct $name; - - getsockopt_impl!($name, $level, $flag, $ty, $getter); - }; - - (SetOnly, $name:ident, $level:path, $flag:path, $ty:ty) => { - sockopt_impl!(SetOnly, $name, $level, $flag, $ty, SetStruct<$ty>); - }; - (SetOnly, $name:ident, $level:path, $flag:path, bool) => { sockopt_impl!(SetOnly, $name, $level, $flag, bool, SetBool); }; @@ -88,31 +73,50 @@ macro_rules! sockopt_impl { sockopt_impl!(SetOnly, $name, $level, $flag, usize, SetUsize); }; - (SetOnly, $name:ident, $level:path, $flag:path, $ty:ty, $setter:ty) => { - #[derive(Copy, Clone, Debug)] - pub struct $name; + (Both, $name:ident, $level:path, $flag:path, bool) => { + sockopt_impl!(Both, $name, $level, $flag, bool, GetBool, SetBool); + }; - setsockopt_impl!($name, $level, $flag, $ty, $setter); + (Both, $name:ident, $level:path, $flag:path, u8) => { + sockopt_impl!(Both, $name, $level, $flag, u8, GetU8, SetU8); }; - (Both, $name:ident, $level:path, $flag:path, $ty:ty, $getter:ty, $setter:ty) => { + (Both, $name:ident, $level:path, $flag:path, usize) => { + sockopt_impl!(Both, $name, $level, $flag, usize, GetUsize, SetUsize); + }; + + /* + * Matchers with generic getter types must be placed at the end, so + * they'll only match _after_ specialized matchers fail + */ + (GetOnly, $name:ident, $level:path, $flag:path, $ty:ty) => { + sockopt_impl!(GetOnly, $name, $level, $flag, $ty, GetStruct<$ty>); + }; + + (GetOnly, $name:ident, $level:path, $flag:path, $ty:ty, $getter:ty) => { #[derive(Copy, Clone, Debug)] pub struct $name; - setsockopt_impl!($name, $level, $flag, $ty, $setter); getsockopt_impl!($name, $level, $flag, $ty, $getter); }; - (Both, $name:ident, $level:path, $flag:path, bool) => { - sockopt_impl!(Both, $name, $level, $flag, bool, GetBool, SetBool); + (SetOnly, $name:ident, $level:path, $flag:path, $ty:ty) => { + sockopt_impl!(SetOnly, $name, $level, $flag, $ty, SetStruct<$ty>); }; - (Both, $name:ident, $level:path, $flag:path, u8) => { - sockopt_impl!(Both, $name, $level, $flag, u8, GetU8, SetU8); + (SetOnly, $name:ident, $level:path, $flag:path, $ty:ty, $setter:ty) => { + #[derive(Copy, Clone, Debug)] + pub struct $name; + + setsockopt_impl!($name, $level, $flag, $ty, $setter); }; - (Both, $name:ident, $level:path, $flag:path, usize) => { - sockopt_impl!(Both, $name, $level, $flag, usize, GetUsize, SetUsize); + (Both, $name:ident, $level:path, $flag:path, $ty:ty, $getter:ty, $setter:ty) => { + #[derive(Copy, Clone, Debug)] + pub struct $name; + + setsockopt_impl!($name, $level, $flag, $ty, $setter); + getsockopt_impl!($name, $level, $flag, $ty, $getter); }; (Both, $name:ident, $level:path, $flag:path, $ty:ty) => { From 077d979acb57ecbd1dbe4c1a4b7b1449aa5a14d2 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Wed, 3 Aug 2016 22:11:05 -0600 Subject: [PATCH 3/3] 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()) };