Skip to content

Commit

Permalink
Merge #623
Browse files Browse the repository at this point in the history
623: Fix sendmsg on macOS when passing a zero entry cmsgs array. r=Susurrus

On macOS, trying to call `sendmsg` with a zero entry cmsgs buffer, e.g.:

    sendmsg(fd, [IoVec::from_slice(buf)], &[], MsgFlags::empty(), None)

...fails with `EINVAL`.  This occurs due to the pointer value for zero capacity `Vec`s being 0x1 rather than 0x0, to distinguish allocated-but-zero-size from nullptr.  The [kernel validates](https://github.com/opensource-apple/xnu/blob/dc0628e187c3148723505cf1f1d35bb948d3195b/bsd/kern/uipc_syscalls.c#L1304) both the `msghdr.msg_control` and `msghdr.msg_controllen` fields and rejects `msghdr.msg_control == 0x1` as invalid.

This doesn't show up on Linux because the [kernel validates](https://github.com/torvalds/linux/blob/9705596d08ac87c18aee32cc97f2783b7d14624e/net/core/scm.c#L139) `msghdr.msg_controllen` first and ignores the value of `msghdr.msg_control` if the length was 0.
  • Loading branch information
bors[bot] committed Jul 10, 2017
2 parents 386c50c + 64815c6 commit 8a7320a
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 8 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Added `nix::ptrace` on all Linux-kernel-based platforms
[#624](https://github.com/nix-rust/nix/pull/624). Previously it was
only available on x86, x86-64, and ARM, and also not on Android.
- Fixed `sys::socket::sendmsg` with zero entry `cmsgs` parameter.
([#623](https://github.com/nix-rust/nix/pull/623))

## [0.8.1] 2017-04-16

Expand Down
18 changes: 11 additions & 7 deletions src/sys/socket/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,14 +258,12 @@ pub fn sendmsg<'a>(fd: RawFd, iov: &[IoVec<&'a [u8]>], cmsgs: &[ControlMessage<'
len += cmsg.len();
capacity += cmsg.space();
}
// Alignment hackery. Note that capacity is guaranteed to be a
// multiple of size_t. Note also that the resulting vector claims
// to have length == capacity, so it's presently uninitialized.
// Note that the resulting vector claims to have length == capacity,
// so it's presently uninitialized.
let mut cmsg_buffer = unsafe {
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 _, len, len)
vec.set_len(len);
vec
};
{
let mut ptr = &mut cmsg_buffer[..];
Expand All @@ -279,12 +277,18 @@ pub fn sendmsg<'a>(fd: RawFd, iov: &[IoVec<&'a [u8]>], cmsgs: &[ControlMessage<'
None => (0 as *const _, 0),
};

let cmsg_ptr = if capacity > 0 {
cmsg_buffer.as_ptr() as *const c_void
} else {
ptr::null()
};

let mhdr = msghdr {
msg_name: name as *const c_void,
msg_namelen: namelen,
msg_iov: iov.as_ptr(),
msg_iovlen: iov.len() as size_t,
msg_control: cmsg_buffer.as_ptr() as *const c_void,
msg_control: cmsg_ptr,
msg_controllen: capacity as size_t,
msg_flags: 0,
};
Expand Down
39 changes: 38 additions & 1 deletion test/sys/test_socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ pub fn test_scm_rights() {
panic!("unexpected cmsg");
}
}
assert_eq!(msg.flags & (MSG_TRUNC | MSG_CTRUNC), MsgFlags::empty());
assert!(!msg.flags.intersects(MSG_TRUNC | MSG_CTRUNC));
close(fd2).unwrap();
}

Expand All @@ -145,6 +145,43 @@ pub fn test_scm_rights() {
close(w).unwrap();
}

// Verify `sendmsg` builds a valid `msghdr` when passing an empty
// `cmsgs` argument. This should result in a msghdr with a nullptr
// msg_control field and a msg_controllen of 0 when calling into the
// raw `sendmsg`.
#[test]
pub fn test_sendmsg_empty_cmsgs() {
use nix::sys::uio::IoVec;
use nix::unistd::close;
use nix::sys::socket::{socketpair, sendmsg, recvmsg,
AddressFamily, SockType, SockFlag,
CmsgSpace, MsgFlags,
MSG_TRUNC, MSG_CTRUNC};

let (fd1, fd2) = socketpair(AddressFamily::Unix, SockType::Stream, 0,
SockFlag::empty())
.unwrap();

{
let iov = [IoVec::from_slice(b"hello")];
assert_eq!(sendmsg(fd1, &iov, &[], MsgFlags::empty(), None).unwrap(), 5);
close(fd1).unwrap();
}

{
let mut buf = [0u8; 5];
let iov = [IoVec::from_mut_slice(&mut buf[..])];
let mut cmsgspace: CmsgSpace<[RawFd; 1]> = CmsgSpace::new();
let msg = recvmsg(fd2, &iov, Some(&mut cmsgspace), MsgFlags::empty()).unwrap();

for _ in msg.cmsgs() {
panic!("unexpected cmsg");
}
assert!(!msg.flags.intersects(MSG_TRUNC | MSG_CTRUNC));
close(fd2).unwrap();
}
}

// Test creating and using named unix domain sockets
#[test]
pub fn test_unixdomain() {
Expand Down

0 comments on commit 8a7320a

Please sign in to comment.