Skip to content

Commit 84901b0

Browse files
committed
Merge #623
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.
2 parents 386c50c + 64815c6 commit 84901b0

File tree

3 files changed

+51
-8
lines changed

3 files changed

+51
-8
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
6060
- Added `nix::ptrace` on all Linux-kernel-based platforms
6161
[#624](https://github.com/nix-rust/nix/pull/624). Previously it was
6262
only available on x86, x86-64, and ARM, and also not on Android.
63+
- Fixed `sys::socket::sendmsg` with zero entry `cmsgs` parameter.
64+
([#623](https://github.com/nix-rust/nix/pull/623))
6365

6466
## [0.8.1] 2017-04-16
6567

src/sys/socket/mod.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -258,14 +258,12 @@ pub fn sendmsg<'a>(fd: RawFd, iov: &[IoVec<&'a [u8]>], cmsgs: &[ControlMessage<'
258258
len += cmsg.len();
259259
capacity += cmsg.space();
260260
}
261-
// Alignment hackery. Note that capacity is guaranteed to be a
262-
// multiple of size_t. Note also that the resulting vector claims
263-
// to have length == capacity, so it's presently uninitialized.
261+
// Note that the resulting vector claims to have length == capacity,
262+
// so it's presently uninitialized.
264263
let mut cmsg_buffer = unsafe {
265264
let mut vec = Vec::<u8>::with_capacity(len);
266-
let ptr = vec.as_mut_ptr();
267-
mem::forget(vec);
268-
Vec::<u8>::from_raw_parts(ptr as *mut _, len, len)
265+
vec.set_len(len);
266+
vec
269267
};
270268
{
271269
let mut ptr = &mut cmsg_buffer[..];
@@ -279,12 +277,18 @@ pub fn sendmsg<'a>(fd: RawFd, iov: &[IoVec<&'a [u8]>], cmsgs: &[ControlMessage<'
279277
None => (0 as *const _, 0),
280278
};
281279

280+
let cmsg_ptr = if capacity > 0 {
281+
cmsg_buffer.as_ptr() as *const c_void
282+
} else {
283+
ptr::null()
284+
};
285+
282286
let mhdr = msghdr {
283287
msg_name: name as *const c_void,
284288
msg_namelen: namelen,
285289
msg_iov: iov.as_ptr(),
286290
msg_iovlen: iov.len() as size_t,
287-
msg_control: cmsg_buffer.as_ptr() as *const c_void,
291+
msg_control: cmsg_ptr,
288292
msg_controllen: capacity as size_t,
289293
msg_flags: 0,
290294
};

test/sys/test_socket.rs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ pub fn test_scm_rights() {
131131
panic!("unexpected cmsg");
132132
}
133133
}
134-
assert_eq!(msg.flags & (MSG_TRUNC | MSG_CTRUNC), MsgFlags::empty());
134+
assert!(!msg.flags.intersects(MSG_TRUNC | MSG_CTRUNC));
135135
close(fd2).unwrap();
136136
}
137137

@@ -145,6 +145,43 @@ pub fn test_scm_rights() {
145145
close(w).unwrap();
146146
}
147147

148+
// Verify `sendmsg` builds a valid `msghdr` when passing an empty
149+
// `cmsgs` argument. This should result in a msghdr with a nullptr
150+
// msg_control field and a msg_controllen of 0 when calling into the
151+
// raw `sendmsg`.
152+
#[test]
153+
pub fn test_sendmsg_empty_cmsgs() {
154+
use nix::sys::uio::IoVec;
155+
use nix::unistd::close;
156+
use nix::sys::socket::{socketpair, sendmsg, recvmsg,
157+
AddressFamily, SockType, SockFlag,
158+
CmsgSpace, MsgFlags,
159+
MSG_TRUNC, MSG_CTRUNC};
160+
161+
let (fd1, fd2) = socketpair(AddressFamily::Unix, SockType::Stream, 0,
162+
SockFlag::empty())
163+
.unwrap();
164+
165+
{
166+
let iov = [IoVec::from_slice(b"hello")];
167+
assert_eq!(sendmsg(fd1, &iov, &[], MsgFlags::empty(), None).unwrap(), 5);
168+
close(fd1).unwrap();
169+
}
170+
171+
{
172+
let mut buf = [0u8; 5];
173+
let iov = [IoVec::from_mut_slice(&mut buf[..])];
174+
let mut cmsgspace: CmsgSpace<[RawFd; 1]> = CmsgSpace::new();
175+
let msg = recvmsg(fd2, &iov, Some(&mut cmsgspace), MsgFlags::empty()).unwrap();
176+
177+
for _ in msg.cmsgs() {
178+
panic!("unexpected cmsg");
179+
}
180+
assert!(!msg.flags.intersects(MSG_TRUNC | MSG_CTRUNC));
181+
close(fd2).unwrap();
182+
}
183+
}
184+
148185
// Test creating and using named unix domain sockets
149186
#[test]
150187
pub fn test_unixdomain() {

0 commit comments

Comments
 (0)