summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlan Somers <asomers@gmail.com>2016-08-03 22:11:05 -0600
committerPhilipp Matthias Schaefer <philipp.matthias.schaefer@posteo.de>2016-08-10 21:12:53 +0200
commit077d979acb57ecbd1dbe4c1a4b7b1449aa5a14d2 (patch)
tree078511f3b8dd0e83e0442f6e206fe910b9216659 /src
parent36789c78a726d9ccf2f3b2fa2eccdfb78b28c664 (diff)
downloadnix-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.rs14
-rw-r--r--src/sys/socket/mod.rs27
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()) };