Skip to content

Commit 5d50aca

Browse files
committed
Merge #648
648: Fix `struct msghdr` types on non-Linux platforms. r=Susurrus a=kinetiknz Type `struct msghdr` and `struct cmsghdr` types defined in `sys/socket/ffi.rs` match the Linux headers, but differ from the standard header (and other OSes): http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_socket.h.html This PR fixes a memory corruption/unsafety issue on non-Linux machines when using `recvmsg` with certain parameters. While fixing this, I wondered what the reason was for nix not using libc's definition of these structures. Closes #748.
2 parents b68db41 + 19ef148 commit 5d50aca

File tree

3 files changed

+63
-118
lines changed

3 files changed

+63
-118
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ This project adheres to [Semantic Versioning](http://semver.org/).
9696
- Fixed error handling in `AioCb::fsync`, `AioCb::read`, and `AioCb::write`.
9797
It is no longer an error to drop an `AioCb` that failed to enqueue in the OS.
9898
([#715](https://github.com/nix-rust/nix/pull/715))
99+
- Fix potential memory corruption on non-Linux platforms when using
100+
`sendmsg`/`recvmsg`, caused by mismatched `msghdr` definition.
101+
([#648](https://github.com/nix-rust/nix/pull/648))
99102

100103
### Removed
101104
- The syscall module has been removed. This only exposed enough functionality for

src/sys/socket/ffi.rs

Lines changed: 0 additions & 81 deletions
This file was deleted.

src/sys/socket/mod.rs

Lines changed: 60 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ use sys::time::TimeVal;
1111
use sys::uio::IoVec;
1212

1313
mod addr;
14-
mod ffi;
1514
pub mod sockopt;
1615

1716
/*
@@ -33,6 +32,8 @@ pub use self::addr::{
3332
pub use ::sys::socket::addr::netlink::NetlinkAddr;
3433

3534
pub use libc::{
35+
cmsghdr,
36+
msghdr,
3637
sa_family_t,
3738
sockaddr,
3839
sockaddr_in,
@@ -295,19 +296,32 @@ unsafe fn copy_bytes<'a, 'b, T: ?Sized>(src: &T, dst: &'a mut &'b mut [u8]) {
295296
mem::swap(dst, &mut remainder);
296297
}
297298

298-
299-
use self::ffi::{cmsghdr, msghdr, type_of_cmsg_data, type_of_msg_iovlen, type_of_cmsg_len};
299+
cfg_if! {
300+
// Darwin and DragonFly BSD always align struct cmsghdr to 32-bit only.
301+
if #[cfg(any(target_os = "dragonfly", target_os = "ios", target_os = "macos"))] {
302+
type align_of_cmsg_data = u32;
303+
} else {
304+
type align_of_cmsg_data = size_t;
305+
}
306+
}
300307

301308
/// A structure used to make room in a cmsghdr passed to recvmsg. The
302309
/// size and alignment match that of a cmsghdr followed by a T, but the
303310
/// fields are not accessible, as the actual types will change on a call
304311
/// to recvmsg.
305312
///
306313
/// To make room for multiple messages, nest the type parameter with
307-
/// tuples, e.g.
308-
/// `let cmsg: CmsgSpace<([RawFd; 3], CmsgSpace<[RawFd; 2]>)> = CmsgSpace::new();`
314+
/// tuples:
315+
///
316+
/// ```
317+
/// use std::os::unix::io::RawFd;
318+
/// use nix::sys::socket::CmsgSpace;
319+
/// let cmsg: CmsgSpace<([RawFd; 3], CmsgSpace<[RawFd; 2]>)> = CmsgSpace::new();
320+
/// ```
321+
#[repr(C)]
309322
pub struct CmsgSpace<T> {
310323
_hdr: cmsghdr,
324+
_pad: [align_of_cmsg_data; 0],
311325
_data: T,
312326
}
313327

@@ -377,24 +391,25 @@ impl<'a> Iterator for CmsgIterator<'a> {
377391
if aligned_cmsg_len > self.buf.len() {
378392
return None;
379393
}
394+
let cmsg_data = &self.buf[cmsg_align(sizeof_cmsghdr)..cmsg_len];
380395
self.buf = &self.buf[aligned_cmsg_len..];
381396
self.next += 1;
382397

383398
match (cmsg.cmsg_level, cmsg.cmsg_type) {
384399
(libc::SOL_SOCKET, libc::SCM_RIGHTS) => unsafe {
385400
Some(ControlMessage::ScmRights(
386-
slice::from_raw_parts(
387-
&cmsg.cmsg_data as *const _ as *const _, 1)))
401+
slice::from_raw_parts(cmsg_data.as_ptr() as *const _,
402+
cmsg_data.len() / mem::size_of::<RawFd>())))
388403
},
389404
(libc::SOL_SOCKET, libc::SCM_TIMESTAMP) => unsafe {
390405
Some(ControlMessage::ScmTimestamp(
391-
&*(&cmsg.cmsg_data as *const _ as *const _)))
406+
&*(cmsg_data.as_ptr() as *const _)))
392407
},
393408
(_, _) => unsafe {
394409
Some(ControlMessage::Unknown(UnknownCmsg(
395410
&cmsg,
396411
slice::from_raw_parts(
397-
&cmsg.cmsg_data as *const _ as *const _,
412+
cmsg_data.as_ptr() as *const _,
398413
len))))
399414
}
400415
}
@@ -487,8 +502,13 @@ pub enum ControlMessage<'a> {
487502
#[doc(hidden)]
488503
pub struct UnknownCmsg<'a>(&'a cmsghdr, &'a [u8]);
489504

505+
// Round `len` up to meet the platform's required alignment for
506+
// `cmsghdr`s and trailing `cmsghdr` data. This should match the
507+
// behaviour of CMSG_ALIGN from the Linux headers and do the correct
508+
// thing on other platforms that don't usually provide CMSG_ALIGN.
509+
#[inline]
490510
fn cmsg_align(len: usize) -> usize {
491-
let align_bytes = mem::size_of::<type_of_cmsg_data>() - 1;
511+
let align_bytes = mem::size_of::<align_of_cmsg_data>() - 1;
492512
(len + align_bytes) & !align_bytes
493513
}
494514

@@ -513,17 +533,16 @@ impl<'a> ControlMessage<'a> {
513533
}
514534
}
515535

516-
// Unsafe: start and end of buffer must be size_t-aligned (that is,
517-
// cmsg_align'd). Updates the provided slice; panics if the buffer
518-
// is too small.
536+
// Unsafe: start and end of buffer must be cmsg_align'd. Updates
537+
// the provided slice; panics if the buffer is too small.
519538
unsafe fn encode_into<'b>(&self, buf: &mut &'b mut [u8]) {
520539
match *self {
521540
ControlMessage::ScmRights(fds) => {
522541
let cmsg = cmsghdr {
523-
cmsg_len: self.len() as type_of_cmsg_len,
542+
cmsg_len: self.len() as _,
524543
cmsg_level: libc::SOL_SOCKET,
525544
cmsg_type: libc::SCM_RIGHTS,
526-
cmsg_data: [],
545+
..mem::uninitialized()
527546
};
528547
copy_bytes(&cmsg, buf);
529548

@@ -539,10 +558,10 @@ impl<'a> ControlMessage<'a> {
539558
},
540559
ControlMessage::ScmTimestamp(t) => {
541560
let cmsg = cmsghdr {
542-
cmsg_len: self.len() as type_of_cmsg_len,
561+
cmsg_len: self.len() as _,
543562
cmsg_level: libc::SOL_SOCKET,
544563
cmsg_type: libc::SCM_TIMESTAMP,
545-
cmsg_data: [],
564+
..mem::uninitialized()
546565
};
547566
copy_bytes(&cmsg, buf);
548567

@@ -602,16 +621,18 @@ pub fn sendmsg<'a>(fd: RawFd, iov: &[IoVec<&'a [u8]>], cmsgs: &[ControlMessage<'
602621
ptr::null()
603622
};
604623

605-
let mhdr = msghdr {
606-
msg_name: name as *const c_void,
607-
msg_namelen: namelen,
608-
msg_iov: iov.as_ptr(),
609-
msg_iovlen: iov.len() as type_of_msg_iovlen,
610-
msg_control: cmsg_ptr,
611-
msg_controllen: capacity as type_of_cmsg_len,
612-
msg_flags: 0,
624+
let mhdr = unsafe {
625+
let mut mhdr: msghdr = mem::uninitialized();
626+
mhdr.msg_name = name as *mut _;
627+
mhdr.msg_namelen = namelen;
628+
mhdr.msg_iov = iov.as_ptr() as *mut _;
629+
mhdr.msg_iovlen = iov.len() as _;
630+
mhdr.msg_control = cmsg_ptr as *mut _;
631+
mhdr.msg_controllen = capacity as _;
632+
mhdr.msg_flags = 0;
633+
mhdr
613634
};
614-
let ret = unsafe { ffi::sendmsg(fd, &mhdr, flags.bits()) };
635+
let ret = unsafe { libc::sendmsg(fd, &mhdr, flags.bits()) };
615636

616637
Errno::result(ret).map(|r| r as usize)
617638
}
@@ -625,16 +646,18 @@ pub fn recvmsg<'a, T>(fd: RawFd, iov: &[IoVec<&mut [u8]>], cmsg_buffer: Option<&
625646
Some(cmsg_buffer) => (cmsg_buffer as *mut _, mem::size_of_val(cmsg_buffer)),
626647
None => (0 as *mut _, 0),
627648
};
628-
let mut mhdr = msghdr {
629-
msg_name: &mut address as *const _ as *const c_void,
630-
msg_namelen: mem::size_of::<sockaddr_storage>() as socklen_t,
631-
msg_iov: iov.as_ptr() as *const IoVec<&[u8]>, // safe cast to add const-ness
632-
msg_iovlen: iov.len() as type_of_msg_iovlen,
633-
msg_control: msg_control as *const c_void,
634-
msg_controllen: msg_controllen as type_of_cmsg_len,
635-
msg_flags: 0,
649+
let mut mhdr = unsafe {
650+
let mut mhdr: msghdr = mem::uninitialized();
651+
mhdr.msg_name = &mut address as *mut _ as *mut _;
652+
mhdr.msg_namelen = mem::size_of::<sockaddr_storage>() as socklen_t;
653+
mhdr.msg_iov = iov.as_ptr() as *mut _;
654+
mhdr.msg_iovlen = iov.len() as _;
655+
mhdr.msg_control = msg_control as *mut _;
656+
mhdr.msg_controllen = msg_controllen as _;
657+
mhdr.msg_flags = 0;
658+
mhdr
636659
};
637-
let ret = unsafe { ffi::recvmsg(fd, &mut mhdr, flags.bits()) };
660+
let ret = unsafe { libc::recvmsg(fd, &mut mhdr, flags.bits()) };
638661

639662
Ok(unsafe { RecvMsg {
640663
bytes: try!(Errno::result(ret)) as usize,
@@ -851,7 +874,7 @@ pub fn connect(fd: RawFd, addr: &SockAddr) -> Result<()> {
851874
/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/recv.html)
852875
pub fn recv(sockfd: RawFd, buf: &mut [u8], flags: MsgFlags) -> Result<usize> {
853876
unsafe {
854-
let ret = ffi::recv(
877+
let ret = libc::recv(
855878
sockfd,
856879
buf.as_ptr() as *mut c_void,
857880
buf.len() as size_t,
@@ -870,7 +893,7 @@ pub fn recvfrom(sockfd: RawFd, buf: &mut [u8]) -> Result<(usize, SockAddr)> {
870893
let addr: sockaddr_storage = mem::zeroed();
871894
let mut len = mem::size_of::<sockaddr_storage>() as socklen_t;
872895

873-
let ret = try!(Errno::result(ffi::recvfrom(
896+
let ret = try!(Errno::result(libc::recvfrom(
874897
sockfd,
875898
buf.as_ptr() as *mut c_void,
876899
buf.len() as size_t,

0 commit comments

Comments
 (0)