1 // Copyright 2017 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE-BSD-3-Clause file.
4 // SPDX-License-Identifier: BSD-3-Clause
5
6 /* Copied from the crosvm Project, commit 186eb8b */
7
8 //! Wrapper for sending and receiving messages with file descriptors on sockets that accept
9 //! control messages (e.g. Unix domain sockets).
10
11 use std::fs::File;
12 use std::mem::size_of;
13 use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
14 use std::os::unix::net::{UnixDatagram, UnixStream};
15 use std::ptr::{copy_nonoverlapping, null_mut, write_unaligned};
16
17 use crate::errno::{Error, Result};
18 use libc::{
19 c_long, c_void, cmsghdr, iovec, msghdr, recvmsg, sendmsg, MSG_NOSIGNAL, SCM_RIGHTS, SOL_SOCKET,
20 };
21 use std::os::raw::c_int;
22
23 // Each of the following macros performs the same function as their C counterparts. They are each
24 // macros because they are used to size statically allocated arrays.
25
26 macro_rules! CMSG_ALIGN {
27 ($len:expr) => {
28 (($len) as usize + size_of::<c_long>() - 1) & !(size_of::<c_long>() - 1)
29 };
30 }
31
32 macro_rules! CMSG_SPACE {
33 ($len:expr) => {
34 size_of::<cmsghdr>() + CMSG_ALIGN!($len)
35 };
36 }
37
38 // This function (macro in the C version) is not used in any compile time constant slots, so is just
39 // an ordinary function. The returned pointer is hard coded to be RawFd because that's all that this
40 // module supports.
41 #[allow(non_snake_case)]
42 #[inline(always)]
CMSG_DATA(cmsg_buffer: *mut cmsghdr) -> *mut RawFd43 fn CMSG_DATA(cmsg_buffer: *mut cmsghdr) -> *mut RawFd {
44 // Essentially returns a pointer to just past the header.
45 cmsg_buffer.wrapping_offset(1) as *mut RawFd
46 }
47
48 #[cfg(not(target_env = "musl"))]
49 macro_rules! CMSG_LEN {
50 ($len:expr) => {
51 size_of::<cmsghdr>() + ($len)
52 };
53 }
54
55 #[cfg(target_env = "musl")]
56 macro_rules! CMSG_LEN {
57 ($len:expr) => {{
58 let sz = size_of::<cmsghdr>() + ($len);
59 assert!(sz <= (std::u32::MAX as usize));
60 sz as u32
61 }};
62 }
63
64 #[cfg(not(target_env = "musl"))]
new_msghdr(iovecs: &mut [iovec]) -> msghdr65 fn new_msghdr(iovecs: &mut [iovec]) -> msghdr {
66 msghdr {
67 msg_name: null_mut(),
68 msg_namelen: 0,
69 msg_iov: iovecs.as_mut_ptr(),
70 msg_iovlen: iovecs.len(),
71 msg_control: null_mut(),
72 msg_controllen: 0,
73 msg_flags: 0,
74 }
75 }
76
77 #[cfg(target_env = "musl")]
new_msghdr(iovecs: &mut [iovec]) -> msghdr78 fn new_msghdr(iovecs: &mut [iovec]) -> msghdr {
79 assert!(iovecs.len() <= (std::i32::MAX as usize));
80 let mut msg: msghdr = unsafe { std::mem::zeroed() };
81 msg.msg_name = null_mut();
82 msg.msg_iov = iovecs.as_mut_ptr();
83 msg.msg_iovlen = iovecs.len() as i32;
84 msg.msg_control = null_mut();
85 msg
86 }
87
88 #[cfg(not(target_env = "musl"))]
set_msg_controllen(msg: &mut msghdr, cmsg_capacity: usize)89 fn set_msg_controllen(msg: &mut msghdr, cmsg_capacity: usize) {
90 msg.msg_controllen = cmsg_capacity;
91 }
92
93 #[cfg(target_env = "musl")]
set_msg_controllen(msg: &mut msghdr, cmsg_capacity: usize)94 fn set_msg_controllen(msg: &mut msghdr, cmsg_capacity: usize) {
95 assert!(cmsg_capacity <= (std::u32::MAX as usize));
96 msg.msg_controllen = cmsg_capacity as u32;
97 }
98
99 // This function is like CMSG_NEXT, but safer because it reads only from references, although it
100 // does some pointer arithmetic on cmsg_ptr.
101 #[cfg_attr(
102 feature = "cargo-clippy",
103 allow(clippy::cast_ptr_alignment, clippy::unnecessary_cast)
104 )]
get_next_cmsg(msghdr: &msghdr, cmsg: &cmsghdr, cmsg_ptr: *mut cmsghdr) -> *mut cmsghdr105 fn get_next_cmsg(msghdr: &msghdr, cmsg: &cmsghdr, cmsg_ptr: *mut cmsghdr) -> *mut cmsghdr {
106 let next_cmsg = (cmsg_ptr as *mut u8).wrapping_add(CMSG_ALIGN!(cmsg.cmsg_len)) as *mut cmsghdr;
107 if next_cmsg
108 .wrapping_offset(1)
109 .wrapping_sub(msghdr.msg_control as usize) as usize
110 > msghdr.msg_controllen as usize
111 {
112 null_mut()
113 } else {
114 next_cmsg
115 }
116 }
117
118 const CMSG_BUFFER_INLINE_CAPACITY: usize = CMSG_SPACE!(size_of::<RawFd>() * 32);
119
120 enum CmsgBuffer {
121 Inline([u64; (CMSG_BUFFER_INLINE_CAPACITY + 7) / 8]),
122 Heap(Box<[cmsghdr]>),
123 }
124
125 impl CmsgBuffer {
with_capacity(capacity: usize) -> CmsgBuffer126 fn with_capacity(capacity: usize) -> CmsgBuffer {
127 let cap_in_cmsghdr_units =
128 (capacity.checked_add(size_of::<cmsghdr>()).unwrap() - 1) / size_of::<cmsghdr>();
129 if capacity <= CMSG_BUFFER_INLINE_CAPACITY {
130 CmsgBuffer::Inline([0u64; (CMSG_BUFFER_INLINE_CAPACITY + 7) / 8])
131 } else {
132 CmsgBuffer::Heap(
133 vec![
134 cmsghdr {
135 cmsg_len: 0,
136 cmsg_level: 0,
137 cmsg_type: 0,
138 #[cfg(all(target_env = "musl", target_pointer_width = "64"))]
139 __pad1: 0,
140 };
141 cap_in_cmsghdr_units
142 ]
143 .into_boxed_slice(),
144 )
145 }
146 }
147
as_mut_ptr(&mut self) -> *mut cmsghdr148 fn as_mut_ptr(&mut self) -> *mut cmsghdr {
149 match self {
150 CmsgBuffer::Inline(a) => a.as_mut_ptr() as *mut cmsghdr,
151 CmsgBuffer::Heap(a) => a.as_mut_ptr(),
152 }
153 }
154 }
155
raw_sendmsg<D: IntoIovec>(fd: RawFd, out_data: &[D], out_fds: &[RawFd]) -> Result<usize>156 fn raw_sendmsg<D: IntoIovec>(fd: RawFd, out_data: &[D], out_fds: &[RawFd]) -> Result<usize> {
157 let cmsg_capacity = CMSG_SPACE!(std::mem::size_of_val(out_fds));
158 let mut cmsg_buffer = CmsgBuffer::with_capacity(cmsg_capacity);
159
160 let mut iovecs = Vec::with_capacity(out_data.len());
161 for data in out_data {
162 iovecs.push(iovec {
163 iov_base: data.as_ptr() as *mut c_void,
164 iov_len: data.size(),
165 });
166 }
167
168 let mut msg = new_msghdr(&mut iovecs);
169
170 if !out_fds.is_empty() {
171 let cmsg = cmsghdr {
172 cmsg_len: CMSG_LEN!(std::mem::size_of_val(out_fds)),
173 cmsg_level: SOL_SOCKET,
174 cmsg_type: SCM_RIGHTS,
175 #[cfg(all(target_env = "musl", target_pointer_width = "64"))]
176 __pad1: 0,
177 };
178 // SAFETY: Check comments below for each call.
179 unsafe {
180 // Safe because cmsg_buffer was allocated to be large enough to contain cmsghdr.
181 write_unaligned(cmsg_buffer.as_mut_ptr(), cmsg);
182 // Safe because the cmsg_buffer was allocated to be large enough to hold out_fds.len()
183 // file descriptors.
184 copy_nonoverlapping(
185 out_fds.as_ptr(),
186 CMSG_DATA(cmsg_buffer.as_mut_ptr()),
187 out_fds.len(),
188 );
189 }
190
191 msg.msg_control = cmsg_buffer.as_mut_ptr() as *mut c_void;
192 set_msg_controllen(&mut msg, cmsg_capacity);
193 }
194
195 // SAFETY: Safe because the msghdr was properly constructed from valid (or null) pointers of
196 // the indicated length and we check the return value.
197 let write_count = unsafe { sendmsg(fd, &msg, MSG_NOSIGNAL) };
198
199 if write_count == -1 {
200 Err(Error::last())
201 } else {
202 Ok(write_count as usize)
203 }
204 }
205
206 #[cfg_attr(feature = "cargo-clippy", allow(clippy::unnecessary_cast))]
raw_recvmsg( fd: RawFd, iovecs: &mut [iovec], in_fds: &mut [RawFd], ) -> Result<(usize, usize)>207 unsafe fn raw_recvmsg(
208 fd: RawFd,
209 iovecs: &mut [iovec],
210 in_fds: &mut [RawFd],
211 ) -> Result<(usize, usize)> {
212 let cmsg_capacity = CMSG_SPACE!(std::mem::size_of_val(in_fds));
213 let mut cmsg_buffer = CmsgBuffer::with_capacity(cmsg_capacity);
214 let mut msg = new_msghdr(iovecs);
215
216 if !in_fds.is_empty() {
217 // MSG control len is size_of(cmsghdr) + size_of(RawFd) * in_fds.len().
218 msg.msg_control = cmsg_buffer.as_mut_ptr() as *mut c_void;
219 set_msg_controllen(&mut msg, cmsg_capacity);
220 }
221
222 // Safe because the msghdr was properly constructed from valid (or null) pointers of the
223 // indicated length and we check the return value.
224 // TODO: Should we handle MSG_TRUNC in a specific way?
225 let total_read = recvmsg(fd, &mut msg, 0);
226 if total_read == -1 {
227 return Err(Error::last());
228 }
229
230 if total_read == 0 && (msg.msg_controllen as usize) < size_of::<cmsghdr>() {
231 return Ok((0, 0));
232 }
233
234 // Reference to a memory area with a CmsgBuffer, which contains a `cmsghdr` struct followed
235 // by a sequence of `in_fds.len()` count RawFds.
236 let mut cmsg_ptr = msg.msg_control as *mut cmsghdr;
237 let mut copied_fds_count = 0;
238 // If the control data was truncated, then this might be a sign of incorrect communication
239 // protocol. If MSG_CTRUNC was set we must close the fds from the control data.
240 let mut teardown_control_data = msg.msg_flags & libc::MSG_CTRUNC != 0;
241
242 while !cmsg_ptr.is_null() {
243 // Safe because we checked that cmsg_ptr was non-null, and the loop is constructed such
244 // that it only happens when there is at least sizeof(cmsghdr) space after the pointer to
245 // read.
246 let cmsg = (cmsg_ptr as *mut cmsghdr).read_unaligned();
247 if cmsg.cmsg_level == SOL_SOCKET && cmsg.cmsg_type == SCM_RIGHTS {
248 let fds_count: usize = ((cmsg.cmsg_len - CMSG_LEN!(0)) as usize) / size_of::<RawFd>();
249 // The sender can transmit more data than we can buffer. If a message is too long to
250 // fit in the supplied buffer, excess bytes may be discarded depending on the type of
251 // socket the message is received from.
252 let fds_to_be_copied_count = std::cmp::min(in_fds.len() - copied_fds_count, fds_count);
253 teardown_control_data |= fds_count > fds_to_be_copied_count;
254 if teardown_control_data {
255 // Allocating space for cmesg buffer might provide extra space for fds, due to
256 // alignment. If these fds can not be stored in `in_fds` buffer, then all the control
257 // data must be dropped to insufficient buffer space for returning them to outer
258 // scope. This might be a sign of incorrect protocol communication.
259 for fd_offset in 0..fds_count {
260 let raw_fds_ptr = CMSG_DATA(cmsg_ptr);
261 // The cmsg_ptr is valid here because is checked at the beginning of the
262 // loop and it is assured to have `fds_count` fds available.
263 let raw_fd = *(raw_fds_ptr.wrapping_add(fd_offset)) as c_int;
264 libc::close(raw_fd);
265 }
266 } else {
267 // Safe because `cmsg_ptr` is checked against null and we copy from `cmesg_buffer` to
268 // `in_fds` according to their current capacity.
269 copy_nonoverlapping(
270 CMSG_DATA(cmsg_ptr),
271 in_fds[copied_fds_count..(copied_fds_count + fds_to_be_copied_count)]
272 .as_mut_ptr(),
273 fds_to_be_copied_count,
274 );
275
276 copied_fds_count += fds_to_be_copied_count;
277 }
278 }
279
280 // Remove the previously copied fds.
281 if teardown_control_data {
282 for fd in in_fds.iter().take(copied_fds_count) {
283 // This is safe because we close only the previously copied fds. We do not care
284 // about `close` return code.
285 libc::close(*fd);
286 }
287
288 return Err(Error::new(libc::ENOBUFS));
289 }
290
291 cmsg_ptr = get_next_cmsg(&msg, &cmsg, cmsg_ptr);
292 }
293
294 Ok((total_read as usize, copied_fds_count))
295 }
296
297 /// Trait for file descriptors can send and receive socket control messages via `sendmsg` and
298 /// `recvmsg`.
299 ///
300 /// # Examples
301 ///
302 /// ```
303 /// # extern crate libc;
304 /// extern crate vmm_sys_util;
305 /// use vmm_sys_util::sock_ctrl_msg::ScmSocket;
306 /// # use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
307 /// # use std::fs::File;
308 /// # use std::io::Write;
309 /// # use std::os::unix::io::{AsRawFd, FromRawFd};
310 /// # use std::os::unix::net::UnixDatagram;
311 /// # use std::slice::from_raw_parts;
312 ///
313 /// # use libc::{c_void, iovec};
314 ///
315 /// let (s1, s2) = UnixDatagram::pair().expect("failed to create socket pair");
316 /// let evt = EventFd::new(0).expect("failed to create eventfd");
317 ///
318 /// let write_count = s1
319 /// .send_with_fds(&[[237].as_ref()], &[evt.as_raw_fd()])
320 /// .expect("failed to send fd");
321 ///
322 /// let mut files = [0; 2];
323 /// let mut buf = [0u8];
324 /// let mut iovecs = [iovec {
325 /// iov_base: buf.as_mut_ptr() as *mut c_void,
326 /// iov_len: buf.len(),
327 /// }];
328 /// let (read_count, file_count) = unsafe {
329 /// s2.recv_with_fds(&mut iovecs[..], &mut files)
330 /// .expect("failed to recv fd")
331 /// };
332 ///
333 /// let mut file = unsafe { File::from_raw_fd(files[0]) };
334 /// file.write(unsafe { from_raw_parts(&1203u64 as *const u64 as *const u8, 8) })
335 /// .expect("failed to write to sent fd");
336 /// assert_eq!(evt.read().expect("failed to read from eventfd"), 1203);
337 /// ```
338 pub trait ScmSocket {
339 /// Gets the file descriptor of this socket.
socket_fd(&self) -> RawFd340 fn socket_fd(&self) -> RawFd;
341
342 /// Sends the given data and file descriptor over the socket.
343 ///
344 /// On success, returns the number of bytes sent.
345 ///
346 /// # Arguments
347 ///
348 /// * `buf` - A buffer of data to send on the `socket`.
349 /// * `fd` - A file descriptors to be sent.
send_with_fd<D: IntoIovec>(&self, buf: D, fd: RawFd) -> Result<usize>350 fn send_with_fd<D: IntoIovec>(&self, buf: D, fd: RawFd) -> Result<usize> {
351 self.send_with_fds(&[buf], &[fd])
352 }
353
354 /// Sends the given data and file descriptors over the socket.
355 ///
356 /// On success, returns the number of bytes sent.
357 ///
358 /// # Arguments
359 ///
360 /// * `bufs` - A list of data buffer to send on the `socket`.
361 /// * `fds` - A list of file descriptors to be sent.
send_with_fds<D: IntoIovec>(&self, bufs: &[D], fds: &[RawFd]) -> Result<usize>362 fn send_with_fds<D: IntoIovec>(&self, bufs: &[D], fds: &[RawFd]) -> Result<usize> {
363 raw_sendmsg(self.socket_fd(), bufs, fds)
364 }
365
366 /// Receives data and potentially a file descriptor from the socket.
367 ///
368 /// On success, returns the number of bytes and an optional file descriptor.
369 ///
370 /// # Arguments
371 ///
372 /// * `buf` - A buffer to receive data from the socket.
recv_with_fd(&self, buf: &mut [u8]) -> Result<(usize, Option<File>)>373 fn recv_with_fd(&self, buf: &mut [u8]) -> Result<(usize, Option<File>)> {
374 let mut fd = [0];
375 let mut iovecs = [iovec {
376 iov_base: buf.as_mut_ptr() as *mut c_void,
377 iov_len: buf.len(),
378 }];
379
380 // SAFETY: Safe because we have mutably borrowed buf and it's safe to write arbitrary data
381 // to a slice.
382 let (read_count, fd_count) = unsafe { self.recv_with_fds(&mut iovecs[..], &mut fd)? };
383 let file = if fd_count == 0 {
384 None
385 } else {
386 // SAFETY: Safe because the first fd from recv_with_fds is owned by us and valid
387 // because this branch was taken.
388 Some(unsafe { File::from_raw_fd(fd[0]) })
389 };
390 Ok((read_count, file))
391 }
392
393 /// Receives data and file descriptors from the socket.
394 ///
395 /// On success, returns the number of bytes and file descriptors received as a tuple
396 /// `(bytes count, files count)`.
397 ///
398 /// # Arguments
399 ///
400 /// * `iovecs` - A list of iovec to receive data from the socket.
401 /// * `fds` - A slice of `RawFd`s to put the received file descriptors into. On success, the
402 /// number of valid file descriptors is indicated by the second element of the
403 /// returned tuple. The caller owns these file descriptors, but they will not be
404 /// closed on drop like a `File`-like type would be. It is recommended that each valid
405 /// file descriptor gets wrapped in a drop type that closes it after this returns.
406 ///
407 /// # Safety
408 ///
409 /// It is the callers responsibility to ensure it is safe for arbitrary data to be
410 /// written to the iovec pointers.
recv_with_fds( &self, iovecs: &mut [iovec], fds: &mut [RawFd], ) -> Result<(usize, usize)>411 unsafe fn recv_with_fds(
412 &self,
413 iovecs: &mut [iovec],
414 fds: &mut [RawFd],
415 ) -> Result<(usize, usize)> {
416 raw_recvmsg(self.socket_fd(), iovecs, fds)
417 }
418 }
419
420 impl ScmSocket for UnixDatagram {
socket_fd(&self) -> RawFd421 fn socket_fd(&self) -> RawFd {
422 self.as_raw_fd()
423 }
424 }
425
426 impl ScmSocket for UnixStream {
socket_fd(&self) -> RawFd427 fn socket_fd(&self) -> RawFd {
428 self.as_raw_fd()
429 }
430 }
431
432 /// Trait for types that can be converted into an `iovec` that can be referenced by a syscall for
433 /// the lifetime of this object.
434 ///
435 /// # Safety
436 ///
437 /// This is marked unsafe because the implementation must ensure that the returned pointer and size
438 /// is valid and that the lifetime of the returned pointer is at least that of the trait object.
439 pub unsafe trait IntoIovec {
440 /// Gets the base pointer of this `iovec`.
as_ptr(&self) -> *const c_void441 fn as_ptr(&self) -> *const c_void;
442
443 /// Gets the size in bytes of this `iovec`.
size(&self) -> usize444 fn size(&self) -> usize;
445 }
446
447 // SAFETY: Safe because this slice can not have another mutable reference and it's pointer and
448 // size are guaranteed to be valid.
449 unsafe impl<'a> IntoIovec for &'a [u8] {
450 // Clippy false positive: https://github.com/rust-lang/rust-clippy/issues/3480
451 #[cfg_attr(feature = "cargo-clippy", allow(clippy::useless_asref))]
as_ptr(&self) -> *const c_void452 fn as_ptr(&self) -> *const c_void {
453 self.as_ref().as_ptr() as *const c_void
454 }
455
size(&self) -> usize456 fn size(&self) -> usize {
457 self.len()
458 }
459 }
460
461 #[cfg(test)]
462 mod tests {
463 #![allow(clippy::undocumented_unsafe_blocks)]
464 use super::*;
465 use crate::eventfd::EventFd;
466
467 use std::io::Write;
468 use std::mem::size_of;
469 use std::os::raw::c_long;
470 use std::os::unix::net::UnixDatagram;
471 use std::slice::from_raw_parts;
472
473 use libc::cmsghdr;
474
475 #[test]
buffer_len()476 fn buffer_len() {
477 assert_eq!(CMSG_SPACE!(0), size_of::<cmsghdr>());
478 assert_eq!(
479 CMSG_SPACE!(size_of::<RawFd>()),
480 size_of::<cmsghdr>() + size_of::<c_long>()
481 );
482 if size_of::<RawFd>() == 4 {
483 assert_eq!(
484 CMSG_SPACE!(2 * size_of::<RawFd>()),
485 size_of::<cmsghdr>() + size_of::<c_long>()
486 );
487 assert_eq!(
488 CMSG_SPACE!(3 * size_of::<RawFd>()),
489 size_of::<cmsghdr>() + size_of::<c_long>() * 2
490 );
491 assert_eq!(
492 CMSG_SPACE!(4 * size_of::<RawFd>()),
493 size_of::<cmsghdr>() + size_of::<c_long>() * 2
494 );
495 } else if size_of::<RawFd>() == 8 {
496 assert_eq!(
497 CMSG_SPACE!(2 * size_of::<RawFd>()),
498 size_of::<cmsghdr>() + size_of::<c_long>() * 2
499 );
500 assert_eq!(
501 CMSG_SPACE!(3 * size_of::<RawFd>()),
502 size_of::<cmsghdr>() + size_of::<c_long>() * 3
503 );
504 assert_eq!(
505 CMSG_SPACE!(4 * size_of::<RawFd>()),
506 size_of::<cmsghdr>() + size_of::<c_long>() * 4
507 );
508 }
509 }
510
511 #[test]
send_recv_no_fd()512 fn send_recv_no_fd() {
513 let (s1, s2) = UnixDatagram::pair().expect("failed to create socket pair");
514
515 let write_count = s1
516 .send_with_fds(&[[1u8, 1, 2].as_ref(), [21u8, 34, 55].as_ref()], &[])
517 .expect("failed to send data");
518
519 assert_eq!(write_count, 6);
520
521 let mut buf = [0u8; 6];
522 let mut files = [0; 1];
523 let mut iovecs = [iovec {
524 iov_base: buf.as_mut_ptr() as *mut c_void,
525 iov_len: buf.len(),
526 }];
527 let (read_count, file_count) = unsafe {
528 s2.recv_with_fds(&mut iovecs[..], &mut files)
529 .expect("failed to recv data")
530 };
531
532 assert_eq!(read_count, 6);
533 assert_eq!(file_count, 0);
534 assert_eq!(buf, [1, 1, 2, 21, 34, 55]);
535 }
536
537 #[test]
send_recv_only_fd()538 fn send_recv_only_fd() {
539 let (s1, s2) = UnixDatagram::pair().expect("failed to create socket pair");
540
541 let evt = EventFd::new(0).expect("failed to create eventfd");
542 let write_count = s1
543 .send_with_fd([].as_ref(), evt.as_raw_fd())
544 .expect("failed to send fd");
545
546 assert_eq!(write_count, 0);
547
548 let (read_count, file_opt) = s2.recv_with_fd(&mut []).expect("failed to recv fd");
549
550 let mut file = file_opt.unwrap();
551
552 assert_eq!(read_count, 0);
553 assert!(file.as_raw_fd() >= 0);
554 assert_ne!(file.as_raw_fd(), s1.as_raw_fd());
555 assert_ne!(file.as_raw_fd(), s2.as_raw_fd());
556 assert_ne!(file.as_raw_fd(), evt.as_raw_fd());
557
558 file.write_all(unsafe { from_raw_parts(&1203u64 as *const u64 as *const u8, 8) })
559 .expect("failed to write to sent fd");
560
561 assert_eq!(evt.read().expect("failed to read from eventfd"), 1203);
562 }
563
564 #[test]
send_recv_with_fd()565 fn send_recv_with_fd() {
566 let (s1, s2) = UnixDatagram::pair().expect("failed to create socket pair");
567
568 let evt = EventFd::new(0).expect("failed to create eventfd");
569 let write_count = s1
570 .send_with_fds(&[[237].as_ref()], &[evt.as_raw_fd()])
571 .expect("failed to send fd");
572
573 assert_eq!(write_count, 1);
574
575 let mut files = [0; 2];
576 let mut buf = [0u8];
577 let mut iovecs = [iovec {
578 iov_base: buf.as_mut_ptr() as *mut c_void,
579 iov_len: buf.len(),
580 }];
581 let (read_count, file_count) = unsafe {
582 s2.recv_with_fds(&mut iovecs[..], &mut files)
583 .expect("failed to recv fd")
584 };
585
586 assert_eq!(read_count, 1);
587 assert_eq!(buf[0], 237);
588 assert_eq!(file_count, 1);
589 assert!(files[0] >= 0);
590 assert_ne!(files[0], s1.as_raw_fd());
591 assert_ne!(files[0], s2.as_raw_fd());
592 assert_ne!(files[0], evt.as_raw_fd());
593
594 let mut file = unsafe { File::from_raw_fd(files[0]) };
595
596 file.write_all(unsafe { from_raw_parts(&1203u64 as *const u64 as *const u8, 8) })
597 .expect("failed to write to sent fd");
598
599 assert_eq!(evt.read().expect("failed to read from eventfd"), 1203);
600 }
601
602 #[test]
603 // Exercise the code paths that activate the issue of receiving the all the ancillary data,
604 // but missing to provide enough buffer space to store it.
send_more_recv_less1()605 fn send_more_recv_less1() {
606 let (s1, s2) = UnixDatagram::pair().expect("failed to create socket pair");
607
608 let evt1 = EventFd::new(0).expect("failed to create eventfd");
609 let evt2 = EventFd::new(0).expect("failed to create eventfd");
610 let evt3 = EventFd::new(0).expect("failed to create eventfd");
611 let evt4 = EventFd::new(0).expect("failed to create eventfd");
612 let write_count = s1
613 .send_with_fds(
614 &[[237].as_ref()],
615 &[
616 evt1.as_raw_fd(),
617 evt2.as_raw_fd(),
618 evt3.as_raw_fd(),
619 evt4.as_raw_fd(),
620 ],
621 )
622 .expect("failed to send fd");
623
624 assert_eq!(write_count, 1);
625
626 let mut files = [0; 2];
627 let mut buf = [0u8];
628 let mut iovecs = [iovec {
629 iov_base: buf.as_mut_ptr() as *mut c_void,
630 iov_len: buf.len(),
631 }];
632 assert!(unsafe { s2.recv_with_fds(&mut iovecs[..], &mut files).is_err() });
633 }
634
635 // Exercise the code paths that activate the issue of receiving part of the sent ancillary
636 // data due to insufficient buffer space, activating `msg_flags` `MSG_CTRUNC` flag.
637 #[test]
send_more_recv_less2()638 fn send_more_recv_less2() {
639 let (s1, s2) = UnixDatagram::pair().expect("failed to create socket pair");
640
641 let evt1 = EventFd::new(0).expect("failed to create eventfd");
642 let evt2 = EventFd::new(0).expect("failed to create eventfd");
643 let evt3 = EventFd::new(0).expect("failed to create eventfd");
644 let evt4 = EventFd::new(0).expect("failed to create eventfd");
645 let write_count = s1
646 .send_with_fds(
647 &[[237].as_ref()],
648 &[
649 evt1.as_raw_fd(),
650 evt2.as_raw_fd(),
651 evt3.as_raw_fd(),
652 evt4.as_raw_fd(),
653 ],
654 )
655 .expect("failed to send fd");
656
657 assert_eq!(write_count, 1);
658
659 let mut files = [0; 1];
660 let mut buf = [0u8];
661 let mut iovecs = [iovec {
662 iov_base: buf.as_mut_ptr() as *mut c_void,
663 iov_len: buf.len(),
664 }];
665 assert!(unsafe { s2.recv_with_fds(&mut iovecs[..], &mut files).is_err() });
666 }
667 }
668