Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix sendmsg on macOS when passing a zero entry cmsgs array. #623

Merged
merged 2 commits into from
Jul 11, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this not supposed to be len?

Copy link
Contributor Author

@kinetiknz kinetiknz Jun 20, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think, if anything, cmsg_buffer should be allocated with capacity rather than len.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not certain on this API, so I just wanted to confirm that len was correct.

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