Skip to content

Commit

Permalink
Fix nix on FreeBSD amd64
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
asomers authored and fiveop committed Aug 10, 2016
1 parent 36789c7 commit 077d979
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 16 deletions.
14 changes: 12 additions & 2 deletions src/sys/socket/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,25 @@
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;

#[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
Expand All @@ -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 {
Expand Down
27 changes: 13 additions & 14 deletions src/sys/socket/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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> {
Expand All @@ -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)
},
Expand All @@ -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);
Expand All @@ -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[..];
Expand All @@ -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()) };
Expand Down

0 comments on commit 077d979

Please sign in to comment.