summaryrefslogtreecommitdiff
path: root/src/vhost_user/sock_ctrl_msg.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/vhost_user/sock_ctrl_msg.rs')
-rw-r--r--src/vhost_user/sock_ctrl_msg.rs499
1 files changed, 0 insertions, 499 deletions
diff --git a/src/vhost_user/sock_ctrl_msg.rs b/src/vhost_user/sock_ctrl_msg.rs
deleted file mode 100644
index db3ec2e..0000000
--- a/src/vhost_user/sock_ctrl_msg.rs
+++ /dev/null
@@ -1,499 +0,0 @@
-// Copyright 2017 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-//! Used to send and receive messages with file descriptors on sockets that accept control messages
-//! (e.g. Unix domain sockets).
-
-// TODO: move this file into the vmm-sys-util crate
-
-use std::fs::File;
-use std::mem::size_of;
-use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
-use std::os::unix::net::{UnixDatagram, UnixStream};
-use std::ptr::{copy_nonoverlapping, null_mut, write_unaligned};
-
-use libc::{
- c_long, c_void, cmsghdr, iovec, msghdr, recvmsg, sendmsg, MSG_NOSIGNAL, SCM_RIGHTS, SOL_SOCKET,
-};
-use vmm_sys_util::errno::{Error, Result};
-
-// Each of the following macros performs the same function as their C counterparts. They are each
-// macros because they are used to size statically allocated arrays.
-
-macro_rules! CMSG_ALIGN {
- ($len:expr) => {
- (($len) + size_of::<c_long>() - 1) & !(size_of::<c_long>() - 1)
- };
-}
-
-macro_rules! CMSG_SPACE {
- ($len:expr) => {
- size_of::<cmsghdr>() + CMSG_ALIGN!($len)
- };
-}
-
-#[cfg(not(target_env = "musl"))]
-macro_rules! CMSG_LEN {
- ($len:expr) => {
- size_of::<cmsghdr>() + ($len)
- };
-}
-
-#[cfg(target_env = "musl")]
-macro_rules! CMSG_LEN {
- ($len:expr) => {{
- let sz = size_of::<cmsghdr>() + ($len);
- assert!(sz <= (std::u32::MAX as usize));
- sz as u32
- }};
-}
-
-#[cfg(not(target_env = "musl"))]
-fn new_msghdr(iovecs: &mut [iovec]) -> msghdr {
- msghdr {
- msg_name: null_mut(),
- msg_namelen: 0,
- msg_iov: iovecs.as_mut_ptr(),
- msg_iovlen: iovecs.len(),
- msg_control: null_mut(),
- msg_controllen: 0,
- msg_flags: 0,
- }
-}
-
-#[cfg(target_env = "musl")]
-fn new_msghdr(iovecs: &mut [iovec]) -> msghdr {
- assert!(iovecs.len() <= (std::i32::MAX as usize));
- let mut msg: msghdr = unsafe { std::mem::zeroed() };
- msg.msg_name = null_mut();
- msg.msg_iov = iovecs.as_mut_ptr();
- msg.msg_iovlen = iovecs.len() as i32;
- msg.msg_control = null_mut();
- msg
-}
-
-#[cfg(not(target_env = "musl"))]
-fn set_msg_controllen(msg: &mut msghdr, cmsg_capacity: usize) {
- msg.msg_controllen = cmsg_capacity;
-}
-
-#[cfg(target_env = "musl")]
-fn set_msg_controllen(msg: &mut msghdr, cmsg_capacity: usize) {
- assert!(cmsg_capacity <= (std::u32::MAX as usize));
- msg.msg_controllen = cmsg_capacity as u32;
-}
-
-// This function (macro in the C version) is not used in any compile time constant slots, so is just
-// an ordinary function. The returned pointer is hard coded to be RawFd because that's all that this
-// module supports.
-#[allow(non_snake_case)]
-#[inline(always)]
-fn CMSG_DATA(cmsg_buffer: *mut cmsghdr) -> *mut RawFd {
- // Essentially returns a pointer to just past the header.
- cmsg_buffer.wrapping_offset(1) as *mut RawFd
-}
-
-// This function is like CMSG_NEXT, but safer because it reads only from references, although it
-// does some pointer arithmetic on cmsg_ptr.
-#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
-fn get_next_cmsg(msghdr: &msghdr, cmsg: &cmsghdr, cmsg_ptr: *mut cmsghdr) -> *mut cmsghdr {
- let next_cmsg =
- (cmsg_ptr as *mut u8).wrapping_add(CMSG_ALIGN!(cmsg.cmsg_len as usize)) as *mut cmsghdr;
- if next_cmsg
- .wrapping_offset(1)
- .wrapping_sub(msghdr.msg_control as usize) as usize
- > msghdr.msg_controllen as usize
- {
- null_mut()
- } else {
- next_cmsg
- }
-}
-
-const CMSG_BUFFER_INLINE_CAPACITY: usize = CMSG_SPACE!(size_of::<RawFd>() * 32);
-
-enum CmsgBuffer {
- Inline([u64; (CMSG_BUFFER_INLINE_CAPACITY + 7) / 8]),
- Heap(Box<[cmsghdr]>),
-}
-
-impl CmsgBuffer {
- fn with_capacity(capacity: usize) -> CmsgBuffer {
- let cap_in_cmsghdr_units =
- (capacity.checked_add(size_of::<cmsghdr>()).unwrap() - 1) / size_of::<cmsghdr>();
- if capacity <= CMSG_BUFFER_INLINE_CAPACITY {
- CmsgBuffer::Inline([0u64; (CMSG_BUFFER_INLINE_CAPACITY + 7) / 8])
- } else {
- CmsgBuffer::Heap(
- vec![
- cmsghdr {
- cmsg_len: 0,
- cmsg_level: 0,
- cmsg_type: 0,
- #[cfg(target_env = "musl")]
- __pad1: 0,
- };
- cap_in_cmsghdr_units
- ]
- .into_boxed_slice(),
- )
- }
- }
-
- fn as_mut_ptr(&mut self) -> *mut cmsghdr {
- match self {
- CmsgBuffer::Inline(a) => a.as_mut_ptr() as *mut cmsghdr,
- CmsgBuffer::Heap(a) => a.as_mut_ptr(),
- }
- }
-}
-
-fn raw_sendmsg<D: IntoIovec>(fd: RawFd, out_data: &[D], out_fds: &[RawFd]) -> Result<usize> {
- let cmsg_capacity = CMSG_SPACE!(size_of::<RawFd>() * out_fds.len());
- let mut cmsg_buffer = CmsgBuffer::with_capacity(cmsg_capacity);
-
- let mut iovecs = Vec::with_capacity(out_data.len());
- for data in out_data {
- iovecs.push(iovec {
- iov_base: data.as_ptr() as *mut c_void,
- iov_len: data.size(),
- });
- }
-
- let mut msg = new_msghdr(&mut iovecs);
-
- if !out_fds.is_empty() {
- let cmsg = cmsghdr {
- cmsg_len: CMSG_LEN!(size_of::<RawFd>() * out_fds.len()),
- cmsg_level: SOL_SOCKET,
- cmsg_type: SCM_RIGHTS,
- #[cfg(target_env = "musl")]
- __pad1: 0,
- };
- unsafe {
- // Safe because cmsg_buffer was allocated to be large enough to contain cmsghdr.
- write_unaligned(cmsg_buffer.as_mut_ptr() as *mut cmsghdr, cmsg);
- // Safe because the cmsg_buffer was allocated to be large enough to hold out_fds.len()
- // file descriptors.
- copy_nonoverlapping(
- out_fds.as_ptr(),
- CMSG_DATA(cmsg_buffer.as_mut_ptr()),
- out_fds.len(),
- );
- }
-
- msg.msg_control = cmsg_buffer.as_mut_ptr() as *mut c_void;
- set_msg_controllen(&mut msg, cmsg_capacity);
- }
-
- // Safe because the msghdr was properly constructed from valid (or null) pointers of the
- // indicated length and we check the return value.
- let write_count = unsafe { sendmsg(fd, &msg, MSG_NOSIGNAL) };
-
- if write_count == -1 {
- Err(Error::last())
- } else {
- Ok(write_count as usize)
- }
-}
-
-fn raw_recvmsg(fd: RawFd, iovecs: &mut [iovec], in_fds: &mut [RawFd]) -> Result<(usize, usize)> {
- let cmsg_capacity = CMSG_SPACE!(size_of::<RawFd>() * in_fds.len());
- let mut cmsg_buffer = CmsgBuffer::with_capacity(cmsg_capacity);
- let mut msg = new_msghdr(iovecs);
-
- if !in_fds.is_empty() {
- msg.msg_control = cmsg_buffer.as_mut_ptr() as *mut c_void;
- set_msg_controllen(&mut msg, cmsg_capacity);
- }
-
- // Safe because the msghdr was properly constructed from valid (or null) pointers of the
- // indicated length and we check the return value.
- let total_read = unsafe { recvmsg(fd, &mut msg, libc::MSG_WAITALL) };
-
- if total_read == -1 {
- return Err(Error::last());
- }
-
- // When the connection is closed recvmsg() doesn't give an explicit error
- if total_read == 0 && (msg.msg_controllen as usize) < size_of::<cmsghdr>() {
- return Err(Error::new(libc::ECONNRESET));
- }
-
- let mut cmsg_ptr = msg.msg_control as *mut cmsghdr;
- let mut in_fds_count = 0;
- while !cmsg_ptr.is_null() {
- // Safe because we checked that cmsg_ptr was non-null, and the loop is constructed such that
- // that only happens when there is at least sizeof(cmsghdr) space after the pointer to read.
- let cmsg = unsafe { (cmsg_ptr as *mut cmsghdr).read_unaligned() };
-
- if cmsg.cmsg_level == SOL_SOCKET && cmsg.cmsg_type == SCM_RIGHTS {
- let fd_count = (cmsg.cmsg_len - CMSG_LEN!(0)) as usize / size_of::<RawFd>();
- unsafe {
- copy_nonoverlapping(
- CMSG_DATA(cmsg_ptr),
- in_fds[in_fds_count..(in_fds_count + fd_count)].as_mut_ptr(),
- fd_count,
- );
- }
- in_fds_count += fd_count;
- }
-
- cmsg_ptr = get_next_cmsg(&msg, &cmsg, cmsg_ptr);
- }
-
- Ok((total_read as usize, in_fds_count))
-}
-
-/// Trait for file descriptors can send and receive socket control messages via `sendmsg` and
-/// `recvmsg`.
-pub trait ScmSocket {
- /// Gets the file descriptor of this socket.
- fn socket_fd(&self) -> RawFd;
-
- /// Sends the given data and file descriptor over the socket.
- ///
- /// On success, returns the number of bytes sent.
- ///
- /// # Arguments
- ///
- /// * `buf` - A buffer of data to send on the `socket`.
- /// * `fd` - A file descriptors to be sent.
- fn send_with_fd<D: IntoIovec>(&self, buf: D, fd: RawFd) -> Result<usize> {
- self.send_with_fds(&[buf], &[fd])
- }
-
- /// Sends the given data and file descriptors over the socket.
- ///
- /// On success, returns the number of bytes sent.
- ///
- /// # Arguments
- ///
- /// * `bufs` - A list of data buffer to send on the `socket`.
- /// * `fds` - A list of file descriptors to be sent.
- fn send_with_fds<D: IntoIovec>(&self, bufs: &[D], fds: &[RawFd]) -> Result<usize> {
- raw_sendmsg(self.socket_fd(), bufs, fds)
- }
-
- /// Receives data and potentially a file descriptor from the socket.
- ///
- /// On success, returns the number of bytes and an optional file descriptor.
- ///
- /// # Arguments
- ///
- /// * `buf` - A buffer to receive data from the socket.
- fn recv_with_fd(&self, buf: &mut [u8]) -> Result<(usize, Option<File>)> {
- let mut fd = [0];
- let mut iovecs = [iovec {
- iov_base: buf.as_mut_ptr() as *mut c_void,
- iov_len: buf.len(),
- }];
-
- let (read_count, fd_count) = self.recv_with_fds(&mut iovecs[..], &mut fd)?;
- let file = if fd_count == 0 {
- None
- } else {
- // Safe because the first fd from recv_with_fds is owned by us and valid because this
- // branch was taken.
- Some(unsafe { File::from_raw_fd(fd[0]) })
- };
- Ok((read_count, file))
- }
-
- /// Receives data and file descriptors from the socket.
- ///
- /// On success, returns the number of bytes and file descriptors received as a tuple
- /// `(bytes count, files count)`.
- ///
- /// # Arguments
- ///
- /// * `iovecs` - A list of iovec to receive data from the socket.
- /// * `fds` - A slice of `RawFd`s to put the received file descriptors into. On success, the
- /// number of valid file descriptors is indicated by the second element of the
- /// returned tuple. The caller owns these file descriptors, but they will not be
- /// closed on drop like a `File`-like type would be. It is recommended that each valid
- /// file descriptor gets wrapped in a drop type that closes it after this returns.
- fn recv_with_fds(&self, iovecs: &mut [iovec], fds: &mut [RawFd]) -> Result<(usize, usize)> {
- raw_recvmsg(self.socket_fd(), iovecs, fds)
- }
-}
-
-impl ScmSocket for UnixDatagram {
- fn socket_fd(&self) -> RawFd {
- self.as_raw_fd()
- }
-}
-
-impl ScmSocket for UnixStream {
- fn socket_fd(&self) -> RawFd {
- self.as_raw_fd()
- }
-}
-
-/// Trait for types that can be converted into an `iovec` that can be referenced by a syscall for
-/// the lifetime of this object.
-///
-/// This trait is unsafe because interfaces that use this trait depend on the base pointer and size
-/// being accurate.
-pub unsafe trait IntoIovec {
- /// Gets the base pointer of this `iovec`.
- fn as_ptr(&self) -> *const c_void;
-
- /// Gets the size in bytes of this `iovec`.
- fn size(&self) -> usize;
-}
-
-// Safe because this slice can not have another mutable reference and it's pointer and size are
-// guaranteed to be valid.
-unsafe impl<'a> IntoIovec for &'a [u8] {
- // Clippy false positive: https://github.com/rust-lang/rust-clippy/issues/3480
- #[cfg_attr(feature = "cargo-clippy", allow(clippy::useless_asref))]
- fn as_ptr(&self) -> *const c_void {
- self.as_ref().as_ptr() as *const c_void
- }
-
- fn size(&self) -> usize {
- self.len()
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- use std::io::Write;
- use std::mem::size_of;
- use std::os::raw::c_long;
- use std::os::unix::net::UnixDatagram;
- use std::slice::from_raw_parts;
-
- use libc::cmsghdr;
-
- use vmm_sys_util::eventfd::EventFd;
-
- #[test]
- fn buffer_len() {
- assert_eq!(CMSG_SPACE!(0 * size_of::<RawFd>()), size_of::<cmsghdr>());
- assert_eq!(
- CMSG_SPACE!(1 * size_of::<RawFd>()),
- size_of::<cmsghdr>() + size_of::<c_long>()
- );
- if size_of::<RawFd>() == 4 {
- assert_eq!(
- CMSG_SPACE!(2 * size_of::<RawFd>()),
- size_of::<cmsghdr>() + size_of::<c_long>()
- );
- assert_eq!(
- CMSG_SPACE!(3 * size_of::<RawFd>()),
- size_of::<cmsghdr>() + size_of::<c_long>() * 2
- );
- assert_eq!(
- CMSG_SPACE!(4 * size_of::<RawFd>()),
- size_of::<cmsghdr>() + size_of::<c_long>() * 2
- );
- } else if size_of::<RawFd>() == 8 {
- assert_eq!(
- CMSG_SPACE!(2 * size_of::<RawFd>()),
- size_of::<cmsghdr>() + size_of::<c_long>() * 2
- );
- assert_eq!(
- CMSG_SPACE!(3 * size_of::<RawFd>()),
- size_of::<cmsghdr>() + size_of::<c_long>() * 3
- );
- assert_eq!(
- CMSG_SPACE!(4 * size_of::<RawFd>()),
- size_of::<cmsghdr>() + size_of::<c_long>() * 4
- );
- }
- }
-
- #[test]
- fn send_recv_no_fd() {
- let (s1, s2) = UnixDatagram::pair().expect("failed to create socket pair");
-
- let write_count = s1
- .send_with_fds(&[[1u8, 1, 2].as_ref(), [21u8, 34, 55].as_ref()], &[])
- .expect("failed to send data");
-
- assert_eq!(write_count, 6);
-
- let mut buf = [0u8; 6];
- let mut files = [0; 1];
- let mut iovecs = [iovec {
- iov_base: buf.as_mut_ptr() as *mut c_void,
- iov_len: buf.len(),
- }];
- let (read_count, file_count) = s2
- .recv_with_fds(&mut iovecs[..], &mut files)
- .expect("failed to recv data");
-
- assert_eq!(read_count, 6);
- assert_eq!(file_count, 0);
- assert_eq!(buf, [1, 1, 2, 21, 34, 55]);
- }
-
- #[test]
- fn send_recv_only_fd() {
- let (s1, s2) = UnixDatagram::pair().expect("failed to create socket pair");
-
- let evt = EventFd::new(0).expect("failed to create eventfd");
- let write_count = s1
- .send_with_fd([].as_ref(), evt.as_raw_fd())
- .expect("failed to send fd");
-
- assert_eq!(write_count, 0);
-
- let (read_count, file_opt) = s2.recv_with_fd(&mut []).expect("failed to recv fd");
-
- let mut file = file_opt.unwrap();
-
- assert_eq!(read_count, 0);
- assert!(file.as_raw_fd() >= 0);
- assert_ne!(file.as_raw_fd(), s1.as_raw_fd());
- assert_ne!(file.as_raw_fd(), s2.as_raw_fd());
- assert_ne!(file.as_raw_fd(), evt.as_raw_fd());
-
- file.write(unsafe { from_raw_parts(&1203u64 as *const u64 as *const u8, 8) })
- .expect("failed to write to sent fd");
-
- assert_eq!(evt.read().expect("failed to read from eventfd"), 1203);
- }
-
- #[test]
- fn send_recv_with_fd() {
- let (s1, s2) = UnixDatagram::pair().expect("failed to create socket pair");
-
- let evt = EventFd::new(0).expect("failed to create eventfd");
- let write_count = s1
- .send_with_fds(&[[237].as_ref()], &[evt.as_raw_fd()])
- .expect("failed to send fd");
-
- assert_eq!(write_count, 1);
-
- let mut files = [0; 2];
- let mut buf = [0u8];
- let mut iovecs = [iovec {
- iov_base: buf.as_mut_ptr() as *mut c_void,
- iov_len: buf.len(),
- }];
- let (read_count, file_count) = s2
- .recv_with_fds(&mut iovecs[..], &mut files)
- .expect("failed to recv fd");
-
- assert_eq!(read_count, 1);
- assert_eq!(buf[0], 237);
- assert_eq!(file_count, 1);
- assert!(files[0] >= 0);
- assert_ne!(files[0], s1.as_raw_fd());
- assert_ne!(files[0], s2.as_raw_fd());
- assert_ne!(files[0], evt.as_raw_fd());
-
- let mut file = unsafe { File::from_raw_fd(files[0]) };
-
- file.write(unsafe { from_raw_parts(&1203u64 as *const u64 as *const u8, 8) })
- .expect("failed to write to sent fd");
-
- assert_eq!(evt.read().expect("failed to read from eventfd"), 1203);
- }
-}