diff --git a/CHANGELOG.md b/CHANGELOG.md index 15f42a45d3..4ceae9c275 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs index a126f8b438..c11b536778 100644 --- a/src/sys/socket/mod.rs +++ b/src/sys/socket/mod.rs @@ -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::::with_capacity(len); - let ptr = vec.as_mut_ptr(); - mem::forget(vec); - Vec::::from_raw_parts(ptr as *mut _, len, len) + vec.set_len(len); + vec }; { let mut ptr = &mut cmsg_buffer[..]; @@ -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, }; diff --git a/test/sys/test_socket.rs b/test/sys/test_socket.rs index b5465aa0b6..9f9c9bf0ff 100644 --- a/test/sys/test_socket.rs +++ b/test/sys/test_socket.rs @@ -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(); } @@ -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() {