Skip to content

Commit e0f612d

Browse files
committed
Replace CmsgSpace with a macro
CmsgSpace had three problems: 1) It would oversize buffers that expect multiple control messages 2) It didn't use the libc CMSG_SPACE(3) macro, so it might actually undersize a buffer for a single control message. 3) It could do bad things on drop, if you instantiate it with a type that implements Drop (which none of the currently supported ControlMessage types do). Fixes #994
1 parent 368b9fe commit e0f612d

File tree

2 files changed

+118
-43
lines changed

2 files changed

+118
-43
lines changed

src/sys/socket/mod.rs

Lines changed: 88 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
//! [Further reading](http://man7.org/linux/man-pages/man7/socket.7.html)
44
use {Error, Result};
55
use errno::Errno;
6-
use libc::{self, c_void, c_int, iovec, socklen_t, size_t};
6+
use libc::{self, c_void, c_int, iovec, socklen_t, size_t,
7+
CMSG_FIRSTHDR, CMSG_NXTHDR, CMSG_DATA, CMSG_LEN};
78
use std::{fmt, mem, ptr, slice};
89
use std::os::unix::io::RawFd;
910
use sys::time::TimeVal;
@@ -32,11 +33,6 @@ pub use self::addr::{
3233
pub use ::sys::socket::addr::netlink::NetlinkAddr;
3334

3435
pub use libc::{
35-
CMSG_FIRSTHDR,
36-
CMSG_NXTHDR,
37-
CMSG_DATA,
38-
CMSG_SPACE,
39-
CMSG_LEN,
4036
cmsghdr,
4137
msghdr,
4238
sa_family_t,
@@ -47,6 +43,10 @@ pub use libc::{
4743
sockaddr_un,
4844
};
4945

46+
// Needed by the cmsg_space macro
47+
#[doc(hidden)]
48+
pub use libc::{c_uint, CMSG_SPACE};
49+
5050
/// These constants are used to specify the communication semantics
5151
/// when creating a socket with [`socket()`](fn.socket.html)
5252
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
@@ -317,6 +317,55 @@ cfg_if! {
317317
}
318318
}
319319

320+
/// A type that can be used to store ancillary data received by
321+
/// [`recvmsg`](fn.recvmsg.html)
322+
pub trait CmsgBuffer {
323+
fn as_bytes_mut(&mut self) -> &mut [u8];
324+
}
325+
326+
/// Create a buffer large enough for storing some control messages as returned
327+
/// by [`recvmsg`](fn.recvmsg.html).
328+
///
329+
/// # Examples
330+
///
331+
/// ```
332+
/// # #[macro_use] extern crate nix;
333+
/// # use nix::sys::time::TimeVal;
334+
/// # use std::os::unix::io::RawFd;
335+
/// # fn main() {
336+
/// // Create a buffer for a `ControlMessage::ScmTimestamp` message
337+
/// let _ = cmsg_space!(TimeVal);
338+
/// // Create a buffer big enough for a `ControlMessage::ScmRights` message
339+
/// // with two file descriptors
340+
/// let _ = cmsg_space!([RawFd; 2]);
341+
/// // Create a buffer big enough for a `ControlMessage::ScmRights` message
342+
/// // and a `ControlMessage::ScmTimestamp` message
343+
/// let _ = cmsg_space!(RawFd, TimeVal);
344+
/// # }
345+
/// ```
346+
// Unfortunately, CMSG_SPACE isn't a const_fn, or else we could return a
347+
// stack-allocated array.
348+
#[macro_export]
349+
macro_rules! cmsg_space {
350+
( $( $x:ty ),* ) => {
351+
{
352+
use nix::sys::socket::{c_uint, CMSG_SPACE};
353+
use std::mem;
354+
let mut space = 0;
355+
$(
356+
// CMSG_SPACE is always safe
357+
space += unsafe {
358+
CMSG_SPACE(mem::size_of::<$x>() as c_uint)
359+
} as usize;
360+
)*
361+
let mut v = Vec::<u8>::with_capacity(space);
362+
// safe because any bit pattern is a valid u8
363+
unsafe {v.set_len(space)};
364+
v
365+
}
366+
}
367+
}
368+
320369
/// A structure used to make room in a cmsghdr passed to recvmsg. The
321370
/// size and alignment match that of a cmsghdr followed by a T, but the
322371
/// fields are not accessible, as the actual types will change on a call
@@ -341,12 +390,29 @@ pub struct CmsgSpace<T> {
341390
impl<T> CmsgSpace<T> {
342391
/// Create a CmsgSpace<T>. The structure is used only for space, so
343392
/// the fields are uninitialized.
393+
#[deprecated( since="0.14.0", note="Use the cmsg_space! macro instead")]
344394
pub fn new() -> Self {
345395
// Safe because the fields themselves aren't accessible.
346396
unsafe { mem::uninitialized() }
347397
}
348398
}
349399

400+
impl<T> CmsgBuffer for CmsgSpace<T> {
401+
fn as_bytes_mut(&mut self) -> &mut [u8] {
402+
// Safe because nothing ever attempts to access CmsgSpace's fields
403+
unsafe {
404+
slice::from_raw_parts_mut(self as *mut CmsgSpace<T> as *mut u8,
405+
mem::size_of::<Self>())
406+
}
407+
}
408+
}
409+
410+
impl CmsgBuffer for Vec<u8> {
411+
fn as_bytes_mut(&mut self) -> &mut [u8] {
412+
&mut self[..]
413+
}
414+
}
415+
350416
#[allow(missing_debug_implementations)] // msghdr isn't Debug
351417
pub struct RecvMsg<'a> {
352418
cmsghdr: Option<&'a cmsghdr>,
@@ -437,11 +503,12 @@ pub enum ControlMessage<'a> {
437503
// https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=222039
438504
#[cfg_attr(not(all(target_os = "freebsd", target_arch = "x86")), doc = " ```")]
439505
#[cfg_attr(all(target_os = "freebsd", target_arch = "x86"), doc = " ```no_run")]
440-
/// use nix::sys::socket::*;
441-
/// use nix::sys::uio::IoVec;
442-
/// use nix::sys::time::*;
443-
/// use std::time::*;
444-
///
506+
/// # #[macro_use] extern crate nix;
507+
/// # use nix::sys::socket::*;
508+
/// # use nix::sys::uio::IoVec;
509+
/// # use nix::sys::time::*;
510+
/// # use std::time::*;
511+
/// # fn main() {
445512
/// // Set up
446513
/// let message = "Ohayō!".as_bytes();
447514
/// let in_socket = socket(
@@ -462,11 +529,11 @@ pub enum ControlMessage<'a> {
462529
/// assert_eq!(message.len(), l);
463530
/// // Receive the message
464531
/// let mut buffer = vec![0u8; message.len()];
465-
/// let mut cmsgspace: CmsgSpace<TimeVal> = CmsgSpace::new();
532+
/// let mut cmsgspace = cmsg_space!(TimeVal);
466533
/// let iov = [IoVec::from_mut_slice(&mut buffer)];
467534
/// let r = recvmsg(in_socket, &iov, Some(&mut cmsgspace), flags).unwrap();
468535
/// let rtime = match r.cmsgs().next() {
469-
/// Some(ControlMessage::ScmTimestamp(&rtime)) => rtime,
536+
/// Some(ControlMessage::ScmTimestamp(rtime)) => rtime,
470537
/// Some(_) => panic!("Unexpected control message"),
471538
/// None => panic!("No control message")
472539
/// };
@@ -480,9 +547,9 @@ pub enum ControlMessage<'a> {
480547
/// assert!(rduration <= time1.duration_since(UNIX_EPOCH).unwrap());
481548
/// // Close socket
482549
/// nix::unistd::close(in_socket).unwrap();
550+
/// # }
483551
/// ```
484552
ScmTimestamp(&'a TimeVal),
485-
486553
#[cfg(any(
487554
target_os = "android",
488555
target_os = "ios",
@@ -903,13 +970,16 @@ pub fn sendmsg(fd: RawFd, iov: &[IoVec<&[u8]>], cmsgs: &[ControlMessage],
903970
///
904971
/// # References
905972
/// [recvmsg(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/recvmsg.html)
906-
pub fn recvmsg<'a, T>(fd: RawFd, iov: &[IoVec<&mut [u8]>],
907-
cmsg_buffer: Option<&'a mut CmsgSpace<T>>,
908-
flags: MsgFlags) -> Result<RecvMsg<'a>>
973+
pub fn recvmsg<'a>(fd: RawFd, iov: &[IoVec<&mut [u8]>],
974+
cmsg_buffer: Option<&'a mut CmsgBuffer>,
975+
flags: MsgFlags) -> Result<RecvMsg<'a>>
909976
{
910977
let mut address: sockaddr_storage = unsafe { mem::uninitialized() };
911978
let (msg_control, msg_controllen) = match cmsg_buffer {
912-
Some(cmsg_buffer) => (cmsg_buffer as *mut _, mem::size_of_val(cmsg_buffer)),
979+
Some(cmsgspace) => {
980+
let msg_buf = cmsgspace.as_bytes_mut();
981+
(msg_buf.as_mut_ptr(), msg_buf.len())
982+
},
913983
None => (ptr::null_mut(), 0),
914984
};
915985
let mut mhdr = {

test/sys/test_socket.rs

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -128,17 +128,20 @@ pub fn test_recvmsg_ebadf() {
128128
let mut buf = [0u8; 5];
129129
let iov = [IoVec::from_mut_slice(&mut buf[..])];
130130
let fd = -1; // Bad file descriptor
131-
let r = recvmsg::<()>(fd, &iov, None, MsgFlags::empty());
131+
let r = recvmsg(fd, &iov, None, MsgFlags::empty());
132132
assert_eq!(r.err().unwrap(), Error::Sys(Errno::EBADF));
133133
}
134134

135+
// Disable the test on emulated platforms due to a bug in QEMU versions <
136+
// 2.12.0. https://bugs.launchpad.net/qemu/+bug/1701808
137+
#[cfg_attr(not(any(target_arch = "x86_64", target_arch="i686")), ignore)]
135138
#[test]
136139
pub fn test_scm_rights() {
137140
use nix::sys::uio::IoVec;
138141
use nix::unistd::{pipe, read, write, close};
139142
use nix::sys::socket::{socketpair, sendmsg, recvmsg,
140143
AddressFamily, SockType, SockFlag,
141-
ControlMessage, CmsgSpace, MsgFlags};
144+
ControlMessage, MsgFlags};
142145

143146
let (fd1, fd2) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty())
144147
.unwrap();
@@ -157,7 +160,7 @@ pub fn test_scm_rights() {
157160
{
158161
let mut buf = [0u8; 5];
159162
let iov = [IoVec::from_mut_slice(&mut buf[..])];
160-
let mut cmsgspace: CmsgSpace<[RawFd; 1]> = CmsgSpace::new();
163+
let mut cmsgspace = cmsg_space!([RawFd; 1]);
161164
let msg = recvmsg(fd2, &iov, Some(&mut cmsgspace), MsgFlags::empty()).unwrap();
162165

163166
for cmsg in msg.cmsgs() {
@@ -184,20 +187,24 @@ pub fn test_scm_rights() {
184187
}
185188

186189
/// Tests that passing multiple fds using a single `ControlMessage` works.
190+
// Disable the test on emulated platforms due to a bug in QEMU versions <
191+
// 2.12.0. https://bugs.launchpad.net/qemu/+bug/1701808
192+
#[cfg_attr(not(any(target_arch = "x86_64", target_arch="i686")), ignore)]
187193
#[test]
188194
fn test_scm_rights_single_cmsg_multiple_fds() {
189195
use std::os::unix::net::UnixDatagram;
190196
use std::os::unix::io::{RawFd, AsRawFd};
191197
use std::thread;
192-
use nix::sys::socket::{CmsgSpace, ControlMessage, MsgFlags, sendmsg, recvmsg};
198+
use nix::sys::socket::{ControlMessage, MsgFlags,
199+
sendmsg, recvmsg};
193200
use nix::sys::uio::IoVec;
194201
use libc;
195202

196203
let (send, receive) = UnixDatagram::pair().unwrap();
197204
let thread = thread::spawn(move || {
198205
let mut buf = [0u8; 8];
199206
let iovec = [IoVec::from_mut_slice(&mut buf)];
200-
let mut space = CmsgSpace::<[RawFd; 2]>::new();
207+
let mut space = cmsg_space!([RawFd; 2]);
201208
let msg = recvmsg(
202209
receive.as_raw_fd(),
203210
&iovec,
@@ -237,8 +244,7 @@ pub fn test_sendmsg_empty_cmsgs() {
237244
use nix::sys::uio::IoVec;
238245
use nix::unistd::close;
239246
use nix::sys::socket::{socketpair, sendmsg, recvmsg,
240-
AddressFamily, SockType, SockFlag,
241-
CmsgSpace, MsgFlags};
247+
AddressFamily, SockType, SockFlag, MsgFlags};
242248

243249
let (fd1, fd2) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty())
244250
.unwrap();
@@ -252,7 +258,7 @@ pub fn test_sendmsg_empty_cmsgs() {
252258
{
253259
let mut buf = [0u8; 5];
254260
let iov = [IoVec::from_mut_slice(&mut buf[..])];
255-
let mut cmsgspace: CmsgSpace<[RawFd; 1]> = CmsgSpace::new();
261+
let mut cmsgspace = cmsg_space!([RawFd; 1]);
256262
let msg = recvmsg(fd2, &iov, Some(&mut cmsgspace), MsgFlags::empty()).unwrap();
257263

258264
for _ in msg.cmsgs() {
@@ -271,7 +277,7 @@ fn test_scm_credentials() {
271277
use nix::unistd::{close, getpid, getuid, getgid};
272278
use nix::sys::socket::{socketpair, sendmsg, recvmsg, setsockopt,
273279
AddressFamily, SockType, SockFlag,
274-
ControlMessage, CmsgSpace, MsgFlags};
280+
ControlMessage, MsgFlags};
275281
use nix::sys::socket::sockopt::PassCred;
276282

277283
let (send, recv) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty())
@@ -293,7 +299,7 @@ fn test_scm_credentials() {
293299
{
294300
let mut buf = [0u8; 5];
295301
let iov = [IoVec::from_mut_slice(&mut buf[..])];
296-
let mut cmsgspace: CmsgSpace<libc::ucred> = CmsgSpace::new();
302+
let mut cmsgspace = cmsg_space!(libc::ucred);
297303
let msg = recvmsg(recv, &iov, Some(&mut cmsgspace), MsgFlags::empty()).unwrap();
298304
let mut received_cred = None;
299305

@@ -303,7 +309,7 @@ fn test_scm_credentials() {
303309
assert_eq!(cred.pid, getpid().as_raw());
304310
assert_eq!(cred.uid, getuid().as_raw());
305311
assert_eq!(cred.gid, getgid().as_raw());
306-
received_cred = Some(*cred);
312+
received_cred = Some(cred);
307313
} else {
308314
panic!("unexpected cmsg");
309315
}
@@ -322,27 +328,26 @@ fn test_scm_credentials() {
322328
#[cfg_attr(not(any(target_arch = "x86_64", target_arch = "x86")), ignore)]
323329
#[test]
324330
fn test_scm_credentials_and_rights() {
325-
use nix::sys::socket::CmsgSpace;
326331
use libc;
327332

328-
test_impl_scm_credentials_and_rights(CmsgSpace::<(libc::ucred, CmsgSpace<RawFd>)>::new());
333+
let space = cmsg_space!(libc::ucred, RawFd);
334+
test_impl_scm_credentials_and_rights(space);
329335
}
330336

331-
/// Ensure that passing a `CmsgSpace` with too much space for the received
332-
/// messages still works.
337+
/// Ensure that passing a an oversized control message buffer to recvmsg
338+
/// still works.
333339
#[cfg(any(target_os = "android", target_os = "linux"))]
334340
// qemu's handling of multiple cmsgs is bugged, ignore tests on non-x86
335341
// see https://bugs.launchpad.net/qemu/+bug/1781280
336342
#[cfg_attr(not(any(target_arch = "x86_64", target_arch = "x86")), ignore)]
337343
#[test]
338344
fn test_too_large_cmsgspace() {
339-
use nix::sys::socket::CmsgSpace;
340-
341-
test_impl_scm_credentials_and_rights(CmsgSpace::<[u8; 1024]>::new());
345+
let space = vec![0u8; 1024];
346+
test_impl_scm_credentials_and_rights(space);
342347
}
343348

344349
#[cfg(any(target_os = "android", target_os = "linux"))]
345-
fn test_impl_scm_credentials_and_rights<T>(mut space: ::nix::sys::socket::CmsgSpace<T>) {
350+
fn test_impl_scm_credentials_and_rights(mut space: Vec<u8>) {
346351
use libc;
347352
use nix::sys::uio::IoVec;
348353
use nix::unistd::{pipe, read, write, close, getpid, getuid, getgid};
@@ -537,7 +542,7 @@ pub fn test_recv_ipv4pktinfo() {
537542
use nix::sys::socket::sockopt::Ipv4PacketInfo;
538543
use nix::sys::socket::{bind, SockFlag, SockType};
539544
use nix::sys::socket::{getsockname, setsockopt, socket};
540-
use nix::sys::socket::{recvmsg, sendmsg, CmsgSpace, ControlMessage, MsgFlags};
545+
use nix::sys::socket::{recvmsg, sendmsg, ControlMessage, MsgFlags};
541546
use nix::sys::uio::IoVec;
542547
use nix::net::if_::*;
543548

@@ -573,7 +578,7 @@ pub fn test_recv_ipv4pktinfo() {
573578
{
574579
let mut buf = [0u8; 8];
575580
let iovec = [IoVec::from_mut_slice(&mut buf)];
576-
let mut space = CmsgSpace::<libc::in_pktinfo>::new();
581+
let mut space = cmsg_space!(libc::in_pktinfo);
577582
let msg = recvmsg(
578583
receive,
579584
&iovec,
@@ -627,7 +632,7 @@ pub fn test_recvif() {
627632
use nix::sys::socket::sockopt::{Ipv4RecvIf, Ipv4RecvDstAddr};
628633
use nix::sys::socket::{bind, SockFlag, SockType};
629634
use nix::sys::socket::{getsockname, setsockopt, socket, SockAddr};
630-
use nix::sys::socket::{recvmsg, sendmsg, CmsgSpace, ControlMessage, MsgFlags};
635+
use nix::sys::socket::{recvmsg, sendmsg, ControlMessage, MsgFlags};
631636
use nix::sys::uio::IoVec;
632637

633638
let lo_ifaddr = loopback_address(AddressFamily::Inet);
@@ -663,7 +668,7 @@ pub fn test_recvif() {
663668
{
664669
let mut buf = [0u8; 8];
665670
let iovec = [IoVec::from_mut_slice(&mut buf)];
666-
let mut space = CmsgSpace::<(libc::sockaddr_dl, CmsgSpace<libc::in_addr>)>::new();
671+
let mut space = cmsg_space!(libc::sockaddr_dl, libc::in_addr);
667672
let msg = recvmsg(
668673
receive,
669674
&iovec,
@@ -737,7 +742,7 @@ pub fn test_recv_ipv6pktinfo() {
737742
use nix::sys::socket::sockopt::Ipv6RecvPacketInfo;
738743
use nix::sys::socket::{bind, AddressFamily, SockFlag, SockType};
739744
use nix::sys::socket::{getsockname, setsockopt, socket};
740-
use nix::sys::socket::{recvmsg, sendmsg, CmsgSpace, ControlMessage, MsgFlags};
745+
use nix::sys::socket::{recvmsg, sendmsg, ControlMessage, MsgFlags};
741746
use nix::sys::uio::IoVec;
742747

743748
let lo_ifaddr = loopback_address(AddressFamily::Inet6);
@@ -772,7 +777,7 @@ pub fn test_recv_ipv6pktinfo() {
772777
{
773778
let mut buf = [0u8; 8];
774779
let iovec = [IoVec::from_mut_slice(&mut buf)];
775-
let mut space = CmsgSpace::<libc::in6_pktinfo>::new();
780+
let mut space = cmsg_space!(libc::in6_pktinfo);
776781
let msg = recvmsg(
777782
receive,
778783
&iovec,

0 commit comments

Comments
 (0)