• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 //! Used to send and receive messages with file descriptors on sockets that accept control messages
6 //! (e.g. Unix domain sockets).
7 
8 use std::fs::File;
9 use std::io;
10 use std::io::IoSlice;
11 use std::io::IoSliceMut;
12 use std::mem::size_of;
13 use std::mem::size_of_val;
14 use std::mem::MaybeUninit;
15 use std::os::unix::io::RawFd;
16 use std::ptr::copy_nonoverlapping;
17 use std::ptr::null_mut;
18 use std::ptr::write_unaligned;
19 use std::slice;
20 
21 use libc::c_long;
22 use libc::c_void;
23 use libc::cmsghdr;
24 use libc::iovec;
25 use libc::msghdr;
26 use libc::recvmsg;
27 use libc::SCM_RIGHTS;
28 use libc::SOL_SOCKET;
29 use serde::Deserialize;
30 use serde::Serialize;
31 
32 use crate::sys::sendmsg;
33 use crate::AsRawDescriptor;
34 use crate::FromRawDescriptor;
35 use crate::IoBufMut;
36 use crate::RawDescriptor;
37 use crate::SafeDescriptor;
38 use crate::VolatileSlice;
39 
40 // Each of the following functions performs the same function as their C counterparts. They are
41 // reimplemented as const fns here because they are used to size statically allocated arrays.
42 
43 #[allow(non_snake_case)]
CMSG_ALIGN(len: usize) -> usize44 const fn CMSG_ALIGN(len: usize) -> usize {
45     (len + size_of::<c_long>() - 1) & !(size_of::<c_long>() - 1)
46 }
47 
48 #[allow(non_snake_case)]
CMSG_SPACE(len: usize) -> usize49 const fn CMSG_SPACE(len: usize) -> usize {
50     size_of::<cmsghdr>() + CMSG_ALIGN(len)
51 }
52 
53 #[allow(non_snake_case)]
CMSG_LEN(len: usize) -> usize54 const fn CMSG_LEN(len: usize) -> usize {
55     size_of::<cmsghdr>() + len
56 }
57 
58 // This function (macro in the C version) is not used in any compile time constant slots, so is just
59 // an ordinary function. The returned pointer is hard coded to be RawFd because that's all that this
60 // module supports.
61 #[allow(non_snake_case)]
62 #[inline(always)]
CMSG_DATA(cmsg_buffer: *mut cmsghdr) -> *mut RawFd63 fn CMSG_DATA(cmsg_buffer: *mut cmsghdr) -> *mut RawFd {
64     // Essentially returns a pointer to just past the header.
65     cmsg_buffer.wrapping_offset(1) as *mut RawFd
66 }
67 
68 // This function is like CMSG_NEXT, but safer because it reads only from references, although it
69 // does some pointer arithmetic on cmsg_ptr.
70 #[allow(clippy::cast_ptr_alignment, clippy::unnecessary_cast)]
get_next_cmsg(msghdr: &msghdr, cmsg: &cmsghdr, cmsg_ptr: *mut cmsghdr) -> *mut cmsghdr71 fn get_next_cmsg(msghdr: &msghdr, cmsg: &cmsghdr, cmsg_ptr: *mut cmsghdr) -> *mut cmsghdr {
72     // The extra cast of cmsg_len to usize is required to build against musl libc, which uses
73     // u32 for cmsg_len.
74     let next_cmsg =
75         (cmsg_ptr as *mut u8).wrapping_add(CMSG_ALIGN(cmsg.cmsg_len as usize)) as *mut cmsghdr;
76     if next_cmsg
77         .wrapping_offset(1)
78         .wrapping_sub(msghdr.msg_control as usize) as usize
79         > msghdr.msg_controllen as usize
80     {
81         null_mut()
82     } else {
83         next_cmsg
84     }
85 }
86 
87 const CMSG_BUFFER_INLINE_CAPACITY: usize = CMSG_SPACE(size_of::<RawFd>() * 32);
88 
89 enum CmsgBuffer {
90     Inline([u64; (CMSG_BUFFER_INLINE_CAPACITY + 7) / 8]),
91     Heap(Box<[cmsghdr]>),
92 }
93 
94 impl CmsgBuffer {
with_capacity(capacity: usize) -> CmsgBuffer95     fn with_capacity(capacity: usize) -> CmsgBuffer {
96         let cap_in_cmsghdr_units =
97             (capacity.checked_add(size_of::<cmsghdr>()).unwrap() - 1) / size_of::<cmsghdr>();
98         if capacity <= CMSG_BUFFER_INLINE_CAPACITY {
99             CmsgBuffer::Inline([0u64; (CMSG_BUFFER_INLINE_CAPACITY + 7) / 8])
100         } else {
101             CmsgBuffer::Heap(
102                 vec![
103                     // SAFETY:
104                     // Safe because cmsghdr only contains primitive types for
105                     // which zero initialization is valid.
106                     unsafe { MaybeUninit::<cmsghdr>::zeroed().assume_init() };
107                     cap_in_cmsghdr_units
108                 ]
109                 .into_boxed_slice(),
110             )
111         }
112     }
113 
as_mut_ptr(&mut self) -> *mut cmsghdr114     fn as_mut_ptr(&mut self) -> *mut cmsghdr {
115         match self {
116             CmsgBuffer::Inline(a) => a.as_mut_ptr() as *mut cmsghdr,
117             CmsgBuffer::Heap(a) => a.as_mut_ptr(),
118         }
119     }
120 }
121 
122 // Musl requires a try_into when assigning to msg_iovlen and msg_controllen
123 // that is unnecessary when compiling for glibc.
124 #[allow(clippy::useless_conversion)]
raw_sendmsg(fd: RawFd, iovec: &[iovec], out_fds: &[RawFd]) -> io::Result<usize>125 fn raw_sendmsg(fd: RawFd, iovec: &[iovec], out_fds: &[RawFd]) -> io::Result<usize> {
126     let cmsg_capacity = CMSG_SPACE(size_of_val(out_fds));
127     let mut cmsg_buffer = CmsgBuffer::with_capacity(cmsg_capacity);
128 
129     // SAFETY:
130     // msghdr on musl has private __pad1 and __pad2 fields that cannot be initialized.
131     // Safe because msghdr only contains primitive types for which zero
132     // initialization is valid.
133     let mut msg: msghdr = unsafe { MaybeUninit::zeroed().assume_init() };
134     msg.msg_iov = iovec.as_ptr() as *mut iovec;
135     msg.msg_iovlen = iovec.len().try_into().unwrap();
136 
137     if !out_fds.is_empty() {
138         // SAFETY:
139         // msghdr on musl has an extra __pad1 field, initialize the whole struct to zero.
140         // Safe because cmsghdr only contains primitive types for which zero
141         // initialization is valid.
142         let mut cmsg: cmsghdr = unsafe { MaybeUninit::zeroed().assume_init() };
143         cmsg.cmsg_len = CMSG_LEN(size_of_val(out_fds)).try_into().unwrap();
144         cmsg.cmsg_level = SOL_SOCKET;
145         cmsg.cmsg_type = SCM_RIGHTS;
146         // SAFETY: See call specific comments within unsafe block.
147         unsafe {
148             // SAFETY:
149             // Safe because cmsg_buffer was allocated to be large enough to contain cmsghdr.
150             write_unaligned(cmsg_buffer.as_mut_ptr(), cmsg);
151             // SAFETY:
152             // Safe because the cmsg_buffer was allocated to be large enough to hold out_fds.len()
153             // file descriptors.
154             copy_nonoverlapping(
155                 out_fds.as_ptr(),
156                 CMSG_DATA(cmsg_buffer.as_mut_ptr()),
157                 out_fds.len(),
158             );
159         }
160 
161         msg.msg_control = cmsg_buffer.as_mut_ptr() as *mut c_void;
162         msg.msg_controllen = cmsg_capacity.try_into().unwrap();
163     }
164 
165     // SAFETY:
166     // Safe because the msghdr was properly constructed from valid (or null) pointers of the
167     // indicated length and we check the return value.
168     let write_count = unsafe { sendmsg(fd, &msg, 0) };
169 
170     if write_count == -1 {
171         Err(io::Error::last_os_error())
172     } else {
173         Ok(write_count as usize)
174     }
175 }
176 
177 // Musl requires a try_into when assigning to msg_iovlen, msg_controllen and
178 // cmsg_len that is unnecessary when compiling for glibc.
179 #[allow(clippy::useless_conversion, clippy::unnecessary_cast)]
raw_recvmsg( fd: RawFd, iovs: &mut [iovec], max_fds: usize, ) -> io::Result<(usize, Vec<SafeDescriptor>)>180 fn raw_recvmsg(
181     fd: RawFd,
182     iovs: &mut [iovec],
183     max_fds: usize,
184 ) -> io::Result<(usize, Vec<SafeDescriptor>)> {
185     let cmsg_capacity = CMSG_SPACE(max_fds * size_of::<RawFd>());
186     let mut cmsg_buffer = CmsgBuffer::with_capacity(cmsg_capacity);
187 
188     // SAFETY:
189     // msghdr on musl has private __pad1 and __pad2 fields that cannot be initialized.
190     // Safe because msghdr only contains primitive types for which zero
191     // initialization is valid.
192     let mut msg: msghdr = unsafe { MaybeUninit::zeroed().assume_init() };
193     msg.msg_iov = iovs.as_mut_ptr() as *mut iovec;
194     msg.msg_iovlen = iovs.len().try_into().unwrap();
195 
196     if max_fds > 0 {
197         msg.msg_control = cmsg_buffer.as_mut_ptr() as *mut c_void;
198         msg.msg_controllen = cmsg_capacity.try_into().unwrap();
199     }
200 
201     // SAFETY:
202     // Safe because the msghdr was properly constructed from valid (or null) pointers of the
203     // indicated length and we check the return value.
204     let total_read = unsafe { recvmsg(fd, &mut msg, 0) };
205 
206     if total_read == -1 {
207         return Err(io::Error::last_os_error());
208     }
209 
210     if total_read == 0 && (msg.msg_controllen as usize) < size_of::<cmsghdr>() {
211         return Ok((0, Vec::new()));
212     }
213 
214     let mut cmsg_ptr = msg.msg_control as *mut cmsghdr;
215     let mut in_fds: Vec<SafeDescriptor> = Vec::with_capacity(max_fds);
216     while !cmsg_ptr.is_null() {
217         // SAFETY:
218         // Safe because we checked that cmsg_ptr was non-null, and the loop is constructed such that
219         // that only happens when there is at least sizeof(cmsghdr) space after the pointer to read.
220         let cmsg = unsafe { (cmsg_ptr as *mut cmsghdr).read_unaligned() };
221 
222         if cmsg.cmsg_level == SOL_SOCKET && cmsg.cmsg_type == SCM_RIGHTS {
223             let fd_count = (cmsg.cmsg_len as usize - CMSG_LEN(0)) / size_of::<RawFd>();
224             let fd_ptr: *const RawFd = CMSG_DATA(cmsg_ptr);
225             for i in 0..fd_count {
226                 // SAFETY: `fd_ptr[i]` is within the `CMsgBuffer` allocation.
227                 let fd: RawFd = unsafe { fd_ptr.add(i).read_unaligned() };
228                 // SAFETY: We own the raw descriptor returned from `recvmsg()`.
229                 let sd = unsafe { SafeDescriptor::from_raw_descriptor(fd) };
230                 in_fds.push(sd);
231             }
232         }
233 
234         cmsg_ptr = get_next_cmsg(&msg, &cmsg, cmsg_ptr);
235     }
236 
237     Ok((total_read as usize, in_fds))
238 }
239 
240 /// The maximum number of FDs that can be sent in a single send.
241 pub const SCM_SOCKET_MAX_FD_COUNT: usize = 253;
242 
243 /// Trait for file descriptors can send and receive socket control messages via `sendmsg` and
244 /// `recvmsg`.
245 ///
246 /// On Linux, this uses MSG_NOSIGNAL to avoid triggering signals. On MacOS, this sets the
247 /// SO_NOSIGPIPE option on the file descriptor to avoid triggering signals.
248 #[derive(Serialize, Deserialize)]
249 pub struct ScmSocket<T: AsRawDescriptor> {
250     pub(in crate::sys) socket: T,
251 }
252 
253 impl<T: AsRawDescriptor> ScmSocket<T> {
254     /// Sends the given data and file descriptors over the socket.
255     ///
256     /// On success, returns the number of bytes sent.
257     ///
258     /// The error is constructed via `std::io::Error::last_os_error()`.
259     ///
260     /// # Arguments
261     ///
262     /// * `buf` - A buffer of data to send on the `socket`.
263     /// * `fds` - A list of file descriptors to be sent.
send_with_fds(&self, buf: &[u8], fds: &[RawFd]) -> io::Result<usize>264     pub fn send_with_fds(&self, buf: &[u8], fds: &[RawFd]) -> io::Result<usize> {
265         self.send_vectored_with_fds(&[IoSlice::new(buf)], fds)
266     }
267 
268     /// Sends the given data and file descriptors over the socket.
269     ///
270     /// On success, returns the number of bytes sent.
271     ///
272     /// The error is constructed via `std::io::Error::last_os_error()`.
273     ///
274     /// # Arguments
275     ///
276     /// * `bufs` - A slice of buffers of data to send on the `socket`.
277     /// * `fds` - A list of file descriptors to be sent.
send_vectored_with_fds( &self, bufs: &[impl AsIobuf], fds: &[RawFd], ) -> io::Result<usize>278     pub fn send_vectored_with_fds(
279         &self,
280         bufs: &[impl AsIobuf],
281         fds: &[RawFd],
282     ) -> io::Result<usize> {
283         raw_sendmsg(
284             self.socket.as_raw_descriptor(),
285             AsIobuf::as_iobuf_slice(bufs),
286             fds,
287         )
288     }
289 
290     /// Receives data and file descriptors from the socket.
291     ///
292     /// On success, returns the number of bytes and file descriptors received as a tuple
293     /// `(bytes count, descriptors)`.
294     ///
295     /// The error is constructed via `std::io::Error::last_os_error()`.
296     ///
297     /// # Arguments
298     ///
299     /// * `buf` - A buffer to store received data.
300     /// * `max_descriptors` - Maximum number of file descriptors to receive.
recv_with_fds( &self, buf: &mut [u8], max_descriptors: usize, ) -> io::Result<(usize, Vec<SafeDescriptor>)>301     pub fn recv_with_fds(
302         &self,
303         buf: &mut [u8],
304         max_descriptors: usize,
305     ) -> io::Result<(usize, Vec<SafeDescriptor>)> {
306         self.recv_vectored_with_fds(&mut [IoSliceMut::new(buf)], max_descriptors)
307     }
308 
309     /// Receives data and file descriptors from the socket.
310     ///
311     /// On success, returns the number of bytes and file descriptors received as a tuple
312     /// `(bytes count, files count)`.
313     ///
314     /// The error is constructed via `std::io::Error::last_os_error()`.
315     ///
316     /// # Arguments
317     ///
318     /// * `bufs` - A slice of buffers to store received data.
319     /// * `max_descriptors` - Maximum number of file descriptors to receive.
recv_vectored_with_fds( &self, bufs: &mut [IoSliceMut], max_descriptors: usize, ) -> io::Result<(usize, Vec<SafeDescriptor>)>320     pub fn recv_vectored_with_fds(
321         &self,
322         bufs: &mut [IoSliceMut],
323         max_descriptors: usize,
324     ) -> io::Result<(usize, Vec<SafeDescriptor>)> {
325         raw_recvmsg(
326             self.socket.as_raw_descriptor(),
327             IoSliceMut::as_iobuf_mut_slice(bufs),
328             max_descriptors,
329         )
330     }
331 
332     /// Receives data and potentially a file descriptor from the socket.
333     ///
334     /// On success, returns the number of bytes and an optional file descriptor.
335     ///
336     /// The error is constructed via `std::io::Error::last_os_error()`.
337     ///
338     /// # Arguments
339     ///
340     /// * `buf` - A buffer to receive data from the socket.vm
recv_with_file(&self, buf: &mut [u8]) -> io::Result<(usize, Option<File>)>341     pub fn recv_with_file(&self, buf: &mut [u8]) -> io::Result<(usize, Option<File>)> {
342         let (read_count, mut descriptors) = self.recv_with_fds(buf, 1)?;
343         let file = if descriptors.len() == 1 {
344             Some(File::from(descriptors.swap_remove(0)))
345         } else {
346             None
347         };
348         Ok((read_count, file))
349     }
350 
351     /// Returns a reference to the wrapped instance.
inner(&self) -> &T352     pub fn inner(&self) -> &T {
353         &self.socket
354     }
355 
356     /// Returns a mutable reference to the wrapped instance.
inner_mut(&mut self) -> &mut T357     pub fn inner_mut(&mut self) -> &mut T {
358         &mut self.socket
359     }
360 
361     /// Returns the inner object, destroying the ScmSocket.
into_inner(self) -> T362     pub fn into_inner(self) -> T {
363         self.socket
364     }
365 }
366 
367 impl<T: AsRawDescriptor> AsRawDescriptor for ScmSocket<T> {
as_raw_descriptor(&self) -> RawDescriptor368     fn as_raw_descriptor(&self) -> RawDescriptor {
369         self.socket.as_raw_descriptor()
370     }
371 }
372 
373 /// Trait for types that can be converted into an `iovec` that can be referenced by a syscall for
374 /// the lifetime of this object.
375 ///
376 /// # Safety
377 /// This trait is unsafe because interfaces that use this trait depend on the base pointer and size
378 /// being accurate.
379 pub unsafe trait AsIobuf: Sized {
380     /// Returns a `iovec` that describes a contiguous region of memory.
as_iobuf(&self) -> iovec381     fn as_iobuf(&self) -> iovec;
382 
383     /// Returns a slice of `iovec`s that each describe a contiguous region of memory.
384     #[allow(clippy::wrong_self_convention)]
as_iobuf_slice(bufs: &[Self]) -> &[iovec]385     fn as_iobuf_slice(bufs: &[Self]) -> &[iovec];
386 
387     /// Returns a mutable slice of `iovecs` that each describe a contiguous region of memory.
as_iobuf_mut_slice(bufs: &mut [Self]) -> &mut [iovec]388     fn as_iobuf_mut_slice(bufs: &mut [Self]) -> &mut [iovec];
389 }
390 
391 // SAFETY:
392 // Safe because there are no other mutable references to the memory described by `IoSlice` and it is
393 // guaranteed to be ABI-compatible with `iovec`.
394 unsafe impl<'a> AsIobuf for IoSlice<'a> {
as_iobuf(&self) -> iovec395     fn as_iobuf(&self) -> iovec {
396         iovec {
397             iov_base: self.as_ptr() as *mut c_void,
398             iov_len: self.len(),
399         }
400     }
401 
as_iobuf_slice(bufs: &[Self]) -> &[iovec]402     fn as_iobuf_slice(bufs: &[Self]) -> &[iovec] {
403         // SAFETY:
404         // Safe because `IoSlice` is guaranteed to be ABI-compatible with `iovec`.
405         unsafe { slice::from_raw_parts(bufs.as_ptr() as *const iovec, bufs.len()) }
406     }
407 
as_iobuf_mut_slice(bufs: &mut [Self]) -> &mut [iovec]408     fn as_iobuf_mut_slice(bufs: &mut [Self]) -> &mut [iovec] {
409         // SAFETY:
410         // Safe because `IoSlice` is guaranteed to be ABI-compatible with `iovec`.
411         unsafe { slice::from_raw_parts_mut(bufs.as_mut_ptr() as *mut iovec, bufs.len()) }
412     }
413 }
414 
415 // SAFETY:
416 // Safe because there are no other references to the memory described by `IoSliceMut` and it is
417 // guaranteed to be ABI-compatible with `iovec`.
418 unsafe impl<'a> AsIobuf for IoSliceMut<'a> {
as_iobuf(&self) -> iovec419     fn as_iobuf(&self) -> iovec {
420         iovec {
421             iov_base: self.as_ptr() as *mut c_void,
422             iov_len: self.len(),
423         }
424     }
425 
as_iobuf_slice(bufs: &[Self]) -> &[iovec]426     fn as_iobuf_slice(bufs: &[Self]) -> &[iovec] {
427         // SAFETY:
428         // Safe because `IoSliceMut` is guaranteed to be ABI-compatible with `iovec`.
429         unsafe { slice::from_raw_parts(bufs.as_ptr() as *const iovec, bufs.len()) }
430     }
431 
as_iobuf_mut_slice(bufs: &mut [Self]) -> &mut [iovec]432     fn as_iobuf_mut_slice(bufs: &mut [Self]) -> &mut [iovec] {
433         // SAFETY:
434         // Safe because `IoSliceMut` is guaranteed to be ABI-compatible with `iovec`.
435         unsafe { slice::from_raw_parts_mut(bufs.as_mut_ptr() as *mut iovec, bufs.len()) }
436     }
437 }
438 
439 // SAFETY:
440 // Safe because volatile slices are only ever accessed with other volatile interfaces and the
441 // pointer and size are guaranteed to be accurate.
442 unsafe impl<'a> AsIobuf for VolatileSlice<'a> {
as_iobuf(&self) -> iovec443     fn as_iobuf(&self) -> iovec {
444         *self.as_iobuf().as_ref()
445     }
446 
as_iobuf_slice(bufs: &[Self]) -> &[iovec]447     fn as_iobuf_slice(bufs: &[Self]) -> &[iovec] {
448         IoBufMut::as_iobufs(VolatileSlice::as_iobufs(bufs))
449     }
450 
as_iobuf_mut_slice(bufs: &mut [Self]) -> &mut [iovec]451     fn as_iobuf_mut_slice(bufs: &mut [Self]) -> &mut [iovec] {
452         IoBufMut::as_iobufs_mut(VolatileSlice::as_iobufs_mut(bufs))
453     }
454 }
455 
456 #[cfg(test)]
457 #[cfg(any(target_os = "android", target_os = "linux"))] // TODO: eliminate Linux-specific EventExt usage
458 mod tests {
459     use std::io::Write;
460     use std::mem::size_of;
461     use std::os::fd::AsRawFd;
462     use std::os::unix::net::UnixDatagram;
463     use std::slice::from_raw_parts;
464 
465     use super::*;
466     use crate::AsRawDescriptor;
467     use crate::Event;
468     use crate::EventExt;
469 
470     // Doing this as a macro makes it easier to see the line if it fails
471     macro_rules! CMSG_SPACE_TEST {
472         ($len:literal) => {
473             assert_eq!(
474                 CMSG_SPACE(size_of::<[RawFd; $len]>()) as libc::c_uint,
475                 // SAFETY: trivially safe
476                 unsafe { libc::CMSG_SPACE(size_of::<[RawFd; $len]>() as libc::c_uint) }
477             );
478         };
479     }
480 
481     #[test]
482     #[allow(clippy::erasing_op, clippy::identity_op)]
buffer_len()483     fn buffer_len() {
484         CMSG_SPACE_TEST!(0);
485         CMSG_SPACE_TEST!(1);
486         CMSG_SPACE_TEST!(2);
487         CMSG_SPACE_TEST!(3);
488         CMSG_SPACE_TEST!(4);
489     }
490 
491     #[test]
send_recv_no_fd()492     fn send_recv_no_fd() {
493         let (u1, u2) = UnixDatagram::pair().expect("failed to create socket pair");
494         let (s1, s2) = (
495             ScmSocket::try_from(u1).unwrap(),
496             ScmSocket::try_from(u2).unwrap(),
497         );
498 
499         let send_buf = [1u8, 1, 2, 21, 34, 55];
500         let write_count = s1
501             .send_with_fds(&send_buf, &[])
502             .expect("failed to send data");
503 
504         assert_eq!(write_count, 6);
505 
506         let mut buf = [0; 6];
507         let (read_count, files) = s2.recv_with_fds(&mut buf, 1).expect("failed to recv data");
508 
509         assert_eq!(read_count, 6);
510         assert_eq!(files.len(), 0);
511         assert_eq!(buf, [1, 1, 2, 21, 34, 55]);
512 
513         let write_count = s1
514             .send_with_fds(&send_buf, &[])
515             .expect("failed to send data");
516 
517         assert_eq!(write_count, 6);
518         let (read_count, files) = s2.recv_with_fds(&mut buf, 1).expect("failed to recv data");
519 
520         assert_eq!(read_count, 6);
521         assert_eq!(files.len(), 0);
522         assert_eq!(buf, [1, 1, 2, 21, 34, 55]);
523     }
524 
525     #[test]
send_recv_only_fd()526     fn send_recv_only_fd() {
527         let (u1, u2) = UnixDatagram::pair().expect("failed to create socket pair");
528         let (s1, s2) = (
529             ScmSocket::try_from(u1).unwrap(),
530             ScmSocket::try_from(u2).unwrap(),
531         );
532 
533         let evt = Event::new().expect("failed to create event");
534         let write_count = s1
535             .send_with_fds(&[], &[evt.as_raw_descriptor()])
536             .expect("failed to send fd");
537 
538         assert_eq!(write_count, 0);
539 
540         let mut buf = [];
541         let (read_count, file_opt) = s2.recv_with_file(&mut buf).expect("failed to recv fd");
542 
543         let mut file = file_opt.unwrap();
544 
545         assert_eq!(read_count, 0);
546         assert!(file.as_raw_fd() >= 0);
547         assert_ne!(file.as_raw_fd(), s1.as_raw_descriptor());
548         assert_ne!(file.as_raw_fd(), s2.as_raw_descriptor());
549         assert_ne!(file.as_raw_fd(), evt.as_raw_descriptor());
550 
551         // SAFETY: trivially safe
552         file.write_all(unsafe { from_raw_parts(&1203u64 as *const u64 as *const u8, 8) })
553             .expect("failed to write to sent fd");
554 
555         assert_eq!(evt.read_count().expect("failed to read from event"), 1203);
556     }
557 
558     #[test]
send_recv_with_fd()559     fn send_recv_with_fd() {
560         let (u1, u2) = UnixDatagram::pair().expect("failed to create socket pair");
561         let (s1, s2) = (
562             ScmSocket::try_from(u1).unwrap(),
563             ScmSocket::try_from(u2).unwrap(),
564         );
565 
566         let evt = Event::new().expect("failed to create event");
567         let write_count = s1
568             .send_with_fds(&[237], &[evt.as_raw_descriptor()])
569             .expect("failed to send fd");
570 
571         assert_eq!(write_count, 1);
572 
573         let mut buf = [0u8];
574         let (read_count, mut files) = s2.recv_with_fds(&mut buf, 2).expect("failed to recv fd");
575 
576         assert_eq!(read_count, 1);
577         assert_eq!(buf[0], 237);
578         assert_eq!(files.len(), 1);
579         assert!(files[0].as_raw_descriptor() >= 0);
580         assert_ne!(files[0].as_raw_descriptor(), s1.as_raw_descriptor());
581         assert_ne!(files[0].as_raw_descriptor(), s2.as_raw_descriptor());
582         assert_ne!(files[0].as_raw_descriptor(), evt.as_raw_descriptor());
583 
584         let mut file = File::from(files.swap_remove(0));
585 
586         // SAFETY: trivially safe
587         file.write_all(unsafe { from_raw_parts(&1203u64 as *const u64 as *const u8, 8) })
588             .expect("failed to write to sent fd");
589 
590         assert_eq!(evt.read_count().expect("failed to read from event"), 1203);
591     }
592 }
593