• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 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::{IoSlice, IoSliceMut};
10 use std::mem::size_of;
11 use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
12 use std::os::unix::net::{UnixDatagram, UnixStream};
13 use std::ptr::{copy_nonoverlapping, null_mut, write_unaligned};
14 use std::slice;
15 
16 use libc::{
17     c_long, c_void, cmsghdr, iovec, msghdr, recvmsg, sendmsg, MSG_NOSIGNAL, SCM_RIGHTS, SOL_SOCKET,
18 };
19 
20 use data_model::VolatileSlice;
21 
22 use crate::net::UnixSeqpacket;
23 use crate::{Error, Result};
24 
25 // Each of the following macros performs the same function as their C counterparts. They are each
26 // macros because they are used to size statically allocated arrays.
27 
28 macro_rules! CMSG_ALIGN {
29     ($len:expr) => {
30         (($len) + size_of::<c_long>() - 1) & !(size_of::<c_long>() - 1)
31     };
32 }
33 
34 macro_rules! CMSG_SPACE {
35     ($len:expr) => {
36         size_of::<cmsghdr>() + CMSG_ALIGN!($len)
37     };
38 }
39 
40 macro_rules! CMSG_LEN {
41     ($len:expr) => {
42         size_of::<cmsghdr>() + ($len)
43     };
44 }
45 
46 // This function (macro in the C version) is not used in any compile time constant slots, so is just
47 // an ordinary function. The returned pointer is hard coded to be RawFd because that's all that this
48 // module supports.
49 #[allow(non_snake_case)]
50 #[inline(always)]
CMSG_DATA(cmsg_buffer: *mut cmsghdr) -> *mut RawFd51 fn CMSG_DATA(cmsg_buffer: *mut cmsghdr) -> *mut RawFd {
52     // Essentially returns a pointer to just past the header.
53     cmsg_buffer.wrapping_offset(1) as *mut RawFd
54 }
55 
56 // This function is like CMSG_NEXT, but safer because it reads only from references, although it
57 // does some pointer arithmetic on cmsg_ptr.
58 #[allow(clippy::cast_ptr_alignment)]
get_next_cmsg(msghdr: &msghdr, cmsg: &cmsghdr, cmsg_ptr: *mut cmsghdr) -> *mut cmsghdr59 fn get_next_cmsg(msghdr: &msghdr, cmsg: &cmsghdr, cmsg_ptr: *mut cmsghdr) -> *mut cmsghdr {
60     let next_cmsg = (cmsg_ptr as *mut u8).wrapping_add(CMSG_ALIGN!(cmsg.cmsg_len)) as *mut cmsghdr;
61     if next_cmsg
62         .wrapping_offset(1)
63         .wrapping_sub(msghdr.msg_control as usize) as usize
64         > msghdr.msg_controllen
65     {
66         null_mut()
67     } else {
68         next_cmsg
69     }
70 }
71 
72 const CMSG_BUFFER_INLINE_CAPACITY: usize = CMSG_SPACE!(size_of::<RawFd>() * 32);
73 
74 enum CmsgBuffer {
75     Inline([u64; (CMSG_BUFFER_INLINE_CAPACITY + 7) / 8]),
76     Heap(Box<[cmsghdr]>),
77 }
78 
79 impl CmsgBuffer {
with_capacity(capacity: usize) -> CmsgBuffer80     fn with_capacity(capacity: usize) -> CmsgBuffer {
81         let cap_in_cmsghdr_units =
82             (capacity.checked_add(size_of::<cmsghdr>()).unwrap() - 1) / size_of::<cmsghdr>();
83         if capacity <= CMSG_BUFFER_INLINE_CAPACITY {
84             CmsgBuffer::Inline([0u64; (CMSG_BUFFER_INLINE_CAPACITY + 7) / 8])
85         } else {
86             CmsgBuffer::Heap(
87                 vec![
88                     cmsghdr {
89                         cmsg_len: 0,
90                         cmsg_level: 0,
91                         cmsg_type: 0,
92                     };
93                     cap_in_cmsghdr_units
94                 ]
95                 .into_boxed_slice(),
96             )
97         }
98     }
99 
as_mut_ptr(&mut self) -> *mut cmsghdr100     fn as_mut_ptr(&mut self) -> *mut cmsghdr {
101         match self {
102             CmsgBuffer::Inline(a) => a.as_mut_ptr() as *mut cmsghdr,
103             CmsgBuffer::Heap(a) => a.as_mut_ptr(),
104         }
105     }
106 }
107 
raw_sendmsg<D: AsIobuf>(fd: RawFd, out_data: &[D], out_fds: &[RawFd]) -> Result<usize>108 fn raw_sendmsg<D: AsIobuf>(fd: RawFd, out_data: &[D], out_fds: &[RawFd]) -> Result<usize> {
109     let cmsg_capacity = CMSG_SPACE!(size_of::<RawFd>() * out_fds.len());
110     let mut cmsg_buffer = CmsgBuffer::with_capacity(cmsg_capacity);
111 
112     let iovec = AsIobuf::as_iobuf_slice(out_data);
113 
114     let mut msg = msghdr {
115         msg_name: null_mut(),
116         msg_namelen: 0,
117         msg_iov: iovec.as_ptr() as *mut iovec,
118         msg_iovlen: iovec.len(),
119         msg_control: null_mut(),
120         msg_controllen: 0,
121         msg_flags: 0,
122     };
123 
124     if !out_fds.is_empty() {
125         let cmsg = cmsghdr {
126             cmsg_len: CMSG_LEN!(size_of::<RawFd>() * out_fds.len()),
127             cmsg_level: SOL_SOCKET,
128             cmsg_type: SCM_RIGHTS,
129         };
130         unsafe {
131             // Safe because cmsg_buffer was allocated to be large enough to contain cmsghdr.
132             write_unaligned(cmsg_buffer.as_mut_ptr() as *mut cmsghdr, cmsg);
133             // Safe because the cmsg_buffer was allocated to be large enough to hold out_fds.len()
134             // file descriptors.
135             copy_nonoverlapping(
136                 out_fds.as_ptr(),
137                 CMSG_DATA(cmsg_buffer.as_mut_ptr()),
138                 out_fds.len(),
139             );
140         }
141 
142         msg.msg_control = cmsg_buffer.as_mut_ptr() as *mut c_void;
143         msg.msg_controllen = cmsg_capacity;
144     }
145 
146     // Safe because the msghdr was properly constructed from valid (or null) pointers of the
147     // indicated length and we check the return value.
148     let write_count = unsafe { sendmsg(fd, &msg, MSG_NOSIGNAL) };
149 
150     if write_count == -1 {
151         Err(Error::last())
152     } else {
153         Ok(write_count as usize)
154     }
155 }
156 
raw_recvmsg(fd: RawFd, in_data: &mut [u8], in_fds: &mut [RawFd]) -> Result<(usize, usize)>157 fn raw_recvmsg(fd: RawFd, in_data: &mut [u8], in_fds: &mut [RawFd]) -> Result<(usize, usize)> {
158     let iovec = iovec {
159         iov_base: in_data.as_mut_ptr() as *mut c_void,
160         iov_len: in_data.len(),
161     };
162     raw_recvmsg_iovecs(fd, &mut [iovec], in_fds)
163 }
164 
raw_recvmsg_iovecs( fd: RawFd, iovecs: &mut [iovec], in_fds: &mut [RawFd], ) -> Result<(usize, usize)>165 fn raw_recvmsg_iovecs(
166     fd: RawFd,
167     iovecs: &mut [iovec],
168     in_fds: &mut [RawFd],
169 ) -> Result<(usize, usize)> {
170     let cmsg_capacity = CMSG_SPACE!(size_of::<RawFd>() * in_fds.len());
171     let mut cmsg_buffer = CmsgBuffer::with_capacity(cmsg_capacity);
172 
173     let mut msg = msghdr {
174         msg_name: null_mut(),
175         msg_namelen: 0,
176         msg_iov: iovecs.as_mut_ptr() as *mut iovec,
177         msg_iovlen: iovecs.len(),
178         msg_control: null_mut(),
179         msg_controllen: 0,
180         msg_flags: 0,
181     };
182 
183     if !in_fds.is_empty() {
184         msg.msg_control = cmsg_buffer.as_mut_ptr() as *mut c_void;
185         msg.msg_controllen = cmsg_capacity;
186     }
187 
188     // Safe because the msghdr was properly constructed from valid (or null) pointers of the
189     // indicated length and we check the return value.
190     let total_read = unsafe { recvmsg(fd, &mut msg, 0) };
191 
192     if total_read == -1 {
193         return Err(Error::last());
194     }
195 
196     if total_read == 0 && msg.msg_controllen < size_of::<cmsghdr>() {
197         return Ok((0, 0));
198     }
199 
200     let mut cmsg_ptr = msg.msg_control as *mut cmsghdr;
201     let mut in_fds_count = 0;
202     while !cmsg_ptr.is_null() {
203         // Safe because we checked that cmsg_ptr was non-null, and the loop is constructed such that
204         // that only happens when there is at least sizeof(cmsghdr) space after the pointer to read.
205         let cmsg = unsafe { (cmsg_ptr as *mut cmsghdr).read_unaligned() };
206 
207         if cmsg.cmsg_level == SOL_SOCKET && cmsg.cmsg_type == SCM_RIGHTS {
208             let fd_count = (cmsg.cmsg_len - CMSG_LEN!(0)) / size_of::<RawFd>();
209             unsafe {
210                 copy_nonoverlapping(
211                     CMSG_DATA(cmsg_ptr),
212                     in_fds[in_fds_count..(in_fds_count + fd_count)].as_mut_ptr(),
213                     fd_count,
214                 );
215             }
216             in_fds_count += fd_count;
217         }
218 
219         cmsg_ptr = get_next_cmsg(&msg, &cmsg, cmsg_ptr);
220     }
221 
222     Ok((total_read as usize, in_fds_count))
223 }
224 
225 /// The maximum number of FDs that can be sent in a single send.
226 pub const SCM_SOCKET_MAX_FD_COUNT: usize = 253;
227 
228 /// Trait for file descriptors can send and receive socket control messages via `sendmsg` and
229 /// `recvmsg`.
230 pub trait ScmSocket {
231     /// Gets the file descriptor of this socket.
socket_fd(&self) -> RawFd232     fn socket_fd(&self) -> RawFd;
233 
234     /// Sends the given data and file descriptor over the socket.
235     ///
236     /// On success, returns the number of bytes sent.
237     ///
238     /// # Arguments
239     ///
240     /// * `buf` - A buffer of data to send on the `socket`.
241     /// * `fd` - A file descriptors to be sent.
send_with_fd<D: AsIobuf>(&self, buf: &[D], fd: RawFd) -> Result<usize>242     fn send_with_fd<D: AsIobuf>(&self, buf: &[D], fd: RawFd) -> Result<usize> {
243         self.send_with_fds(buf, &[fd])
244     }
245 
246     /// Sends the given data and file descriptors over the socket.
247     ///
248     /// On success, returns the number of bytes sent.
249     ///
250     /// # Arguments
251     ///
252     /// * `buf` - A buffer of data to send on the `socket`.
253     /// * `fds` - A list of file descriptors to be sent.
send_with_fds<D: AsIobuf>(&self, buf: &[D], fd: &[RawFd]) -> Result<usize>254     fn send_with_fds<D: AsIobuf>(&self, buf: &[D], fd: &[RawFd]) -> Result<usize> {
255         raw_sendmsg(self.socket_fd(), buf, fd)
256     }
257 
258     /// Sends the given data and file descriptor over the socket.
259     ///
260     /// On success, returns the number of bytes sent.
261     ///
262     /// # Arguments
263     ///
264     /// * `bufs` - A slice of slices of data to send on the `socket`.
265     /// * `fd` - A file descriptors to be sent.
send_bufs_with_fd(&self, bufs: &[&[u8]], fd: RawFd) -> Result<usize>266     fn send_bufs_with_fd(&self, bufs: &[&[u8]], fd: RawFd) -> Result<usize> {
267         self.send_bufs_with_fds(bufs, &[fd])
268     }
269 
270     /// Sends the given data and file descriptors over the socket.
271     ///
272     /// On success, returns the number of bytes sent.
273     ///
274     /// # Arguments
275     ///
276     /// * `bufs` - A slice of slices of data to send on the `socket`.
277     /// * `fds` - A list of file descriptors to be sent.
send_bufs_with_fds(&self, bufs: &[&[u8]], fd: &[RawFd]) -> Result<usize>278     fn send_bufs_with_fds(&self, bufs: &[&[u8]], fd: &[RawFd]) -> Result<usize> {
279         let slices: Vec<IoSlice> = bufs.iter().map(|&b| IoSlice::new(b)).collect();
280         raw_sendmsg(self.socket_fd(), &slices, fd)
281     }
282 
283     /// Receives data and potentially a file descriptor from the socket.
284     ///
285     /// On success, returns the number of bytes and an optional file descriptor.
286     ///
287     /// # Arguments
288     ///
289     /// * `buf` - A buffer to receive data from the socket.vm
recv_with_fd(&self, buf: &mut [u8]) -> Result<(usize, Option<File>)>290     fn recv_with_fd(&self, buf: &mut [u8]) -> Result<(usize, Option<File>)> {
291         let mut fd = [0];
292         let (read_count, fd_count) = self.recv_with_fds(buf, &mut fd)?;
293         let file = if fd_count == 0 {
294             None
295         } else {
296             // Safe because the first fd from recv_with_fds is owned by us and valid because this
297             // branch was taken.
298             Some(unsafe { File::from_raw_fd(fd[0]) })
299         };
300         Ok((read_count, file))
301     }
302 
303     /// Receives data and file descriptors from the socket.
304     ///
305     /// On success, returns the number of bytes and file descriptors received as a tuple
306     /// `(bytes count, files count)`.
307     ///
308     /// # Arguments
309     ///
310     /// * `buf` - A buffer to receive data from the socket.
311     /// * `fds` - A slice of `RawFd`s to put the received file descriptors into. On success, the
312     ///           number of valid file descriptors is indicated by the second element of the
313     ///           returned tuple. The caller owns these file descriptors, but they will not be
314     ///           closed on drop like a `File`-like type would be. It is recommended that each valid
315     ///           file descriptor gets wrapped in a drop type that closes it after this returns.
recv_with_fds(&self, buf: &mut [u8], fds: &mut [RawFd]) -> Result<(usize, usize)>316     fn recv_with_fds(&self, buf: &mut [u8], fds: &mut [RawFd]) -> Result<(usize, usize)> {
317         raw_recvmsg(self.socket_fd(), buf, fds)
318     }
319 
320     /// Receives data and file descriptors from the socket.
321     ///
322     /// On success, returns the number of bytes and file descriptors received as a tuple
323     /// `(bytes count, files count)`.
324     ///
325     /// # Arguments
326     ///
327     /// * `ioves` - A slice of iovecs to store received data.
328     /// * `fds` - A slice of `RawFd`s to put the received file descriptors into. On success, the
329     ///           number of valid file descriptors is indicated by the second element of the
330     ///           returned tuple. The caller owns these file descriptors, but they will not be
331     ///           closed on drop like a `File`-like type would be. It is recommended that each valid
332     ///           file descriptor gets wrapped in a drop type that closes it after this returns.
recv_iovecs_with_fds( &self, iovecs: &mut [iovec], fds: &mut [RawFd], ) -> Result<(usize, usize)>333     fn recv_iovecs_with_fds(
334         &self,
335         iovecs: &mut [iovec],
336         fds: &mut [RawFd],
337     ) -> Result<(usize, usize)> {
338         raw_recvmsg_iovecs(self.socket_fd(), iovecs, fds)
339     }
340 }
341 
342 impl ScmSocket for UnixDatagram {
socket_fd(&self) -> RawFd343     fn socket_fd(&self) -> RawFd {
344         self.as_raw_fd()
345     }
346 }
347 
348 impl ScmSocket for UnixStream {
socket_fd(&self) -> RawFd349     fn socket_fd(&self) -> RawFd {
350         self.as_raw_fd()
351     }
352 }
353 
354 impl ScmSocket for UnixSeqpacket {
socket_fd(&self) -> RawFd355     fn socket_fd(&self) -> RawFd {
356         self.as_raw_fd()
357     }
358 }
359 
360 /// Trait for types that can be converted into an `iovec` that can be referenced by a syscall for
361 /// the lifetime of this object.
362 ///
363 /// This trait is unsafe because interfaces that use this trait depend on the base pointer and size
364 /// being accurate.
365 pub unsafe trait AsIobuf: Sized {
366     /// Returns a `iovec` that describes a contiguous region of memory.
as_iobuf(&self) -> iovec367     fn as_iobuf(&self) -> iovec;
368 
369     /// Returns a slice of `iovec`s that each describe a contiguous region of memory.
370     #[allow(clippy::wrong_self_convention)]
as_iobuf_slice(bufs: &[Self]) -> &[iovec]371     fn as_iobuf_slice(bufs: &[Self]) -> &[iovec];
372 }
373 
374 // Safe because there are no other mutable references to the memory described by `IoSlice` and it is
375 // guaranteed to be ABI-compatible with `iovec`.
376 unsafe impl<'a> AsIobuf for IoSlice<'a> {
as_iobuf(&self) -> iovec377     fn as_iobuf(&self) -> iovec {
378         iovec {
379             iov_base: self.as_ptr() as *mut c_void,
380             iov_len: self.len(),
381         }
382     }
383 
as_iobuf_slice(bufs: &[Self]) -> &[iovec]384     fn as_iobuf_slice(bufs: &[Self]) -> &[iovec] {
385         // Safe because `IoSlice` is guaranteed to be ABI-compatible with `iovec`.
386         unsafe { slice::from_raw_parts(bufs.as_ptr() as *const iovec, bufs.len()) }
387     }
388 }
389 
390 // Safe because there are no other references to the memory described by `IoSliceMut` and it is
391 // guaranteed to be ABI-compatible with `iovec`.
392 unsafe impl<'a> AsIobuf for IoSliceMut<'a> {
as_iobuf(&self) -> iovec393     fn as_iobuf(&self) -> iovec {
394         iovec {
395             iov_base: self.as_ptr() as *mut c_void,
396             iov_len: self.len(),
397         }
398     }
399 
as_iobuf_slice(bufs: &[Self]) -> &[iovec]400     fn as_iobuf_slice(bufs: &[Self]) -> &[iovec] {
401         // Safe because `IoSliceMut` is guaranteed to be ABI-compatible with `iovec`.
402         unsafe { slice::from_raw_parts(bufs.as_ptr() as *const iovec, bufs.len()) }
403     }
404 }
405 
406 // Safe because volatile slices are only ever accessed with other volatile interfaces and the
407 // pointer and size are guaranteed to be accurate.
408 unsafe impl<'a> AsIobuf for VolatileSlice<'a> {
as_iobuf(&self) -> iovec409     fn as_iobuf(&self) -> iovec {
410         *self.as_iobuf()
411     }
412 
as_iobuf_slice(bufs: &[Self]) -> &[iovec]413     fn as_iobuf_slice(bufs: &[Self]) -> &[iovec] {
414         VolatileSlice::as_iobufs(bufs)
415     }
416 }
417 
418 #[cfg(test)]
419 mod tests {
420     use super::*;
421 
422     use std::io::Write;
423     use std::mem::size_of;
424     use std::os::raw::c_long;
425     use std::os::unix::net::UnixDatagram;
426     use std::slice::from_raw_parts;
427 
428     use libc::cmsghdr;
429 
430     use crate::EventFd;
431 
432     #[test]
433     #[allow(clippy::erasing_op, clippy::identity_op)]
buffer_len()434     fn buffer_len() {
435         assert_eq!(CMSG_SPACE!(0 * size_of::<RawFd>()), size_of::<cmsghdr>());
436         assert_eq!(
437             CMSG_SPACE!(1 * size_of::<RawFd>()),
438             size_of::<cmsghdr>() + size_of::<c_long>()
439         );
440         if size_of::<RawFd>() == 4 {
441             assert_eq!(
442                 CMSG_SPACE!(2 * size_of::<RawFd>()),
443                 size_of::<cmsghdr>() + size_of::<c_long>()
444             );
445             assert_eq!(
446                 CMSG_SPACE!(3 * size_of::<RawFd>()),
447                 size_of::<cmsghdr>() + size_of::<c_long>() * 2
448             );
449             assert_eq!(
450                 CMSG_SPACE!(4 * size_of::<RawFd>()),
451                 size_of::<cmsghdr>() + size_of::<c_long>() * 2
452             );
453         } else if size_of::<RawFd>() == 8 {
454             assert_eq!(
455                 CMSG_SPACE!(2 * size_of::<RawFd>()),
456                 size_of::<cmsghdr>() + size_of::<c_long>() * 2
457             );
458             assert_eq!(
459                 CMSG_SPACE!(3 * size_of::<RawFd>()),
460                 size_of::<cmsghdr>() + size_of::<c_long>() * 3
461             );
462             assert_eq!(
463                 CMSG_SPACE!(4 * size_of::<RawFd>()),
464                 size_of::<cmsghdr>() + size_of::<c_long>() * 4
465             );
466         }
467     }
468 
469     #[test]
send_recv_no_fd()470     fn send_recv_no_fd() {
471         let (s1, s2) = UnixDatagram::pair().expect("failed to create socket pair");
472 
473         let send_buf = [1u8, 1, 2, 21, 34, 55];
474         let ioslice = IoSlice::new(&send_buf);
475         let write_count = s1
476             .send_with_fds(&[ioslice], &[])
477             .expect("failed to send data");
478 
479         assert_eq!(write_count, 6);
480 
481         let mut buf = [0; 6];
482         let mut files = [0; 1];
483         let (read_count, file_count) = s2
484             .recv_with_fds(&mut buf[..], &mut files)
485             .expect("failed to recv data");
486 
487         assert_eq!(read_count, 6);
488         assert_eq!(file_count, 0);
489         assert_eq!(buf, [1, 1, 2, 21, 34, 55]);
490 
491         let write_count = s1
492             .send_bufs_with_fds(&[&send_buf[..]], &[])
493             .expect("failed to send data");
494 
495         assert_eq!(write_count, 6);
496         let (read_count, file_count) = s2
497             .recv_with_fds(&mut buf[..], &mut files)
498             .expect("failed to recv data");
499 
500         assert_eq!(read_count, 6);
501         assert_eq!(file_count, 0);
502         assert_eq!(buf, [1, 1, 2, 21, 34, 55]);
503     }
504 
505     #[test]
send_recv_only_fd()506     fn send_recv_only_fd() {
507         let (s1, s2) = UnixDatagram::pair().expect("failed to create socket pair");
508 
509         let evt = EventFd::new().expect("failed to create eventfd");
510         let ioslice = IoSlice::new([].as_ref());
511         let write_count = s1
512             .send_with_fd(&[ioslice], evt.as_raw_fd())
513             .expect("failed to send fd");
514 
515         assert_eq!(write_count, 0);
516 
517         let (read_count, file_opt) = s2.recv_with_fd(&mut []).expect("failed to recv fd");
518 
519         let mut file = file_opt.unwrap();
520 
521         assert_eq!(read_count, 0);
522         assert!(file.as_raw_fd() >= 0);
523         assert_ne!(file.as_raw_fd(), s1.as_raw_fd());
524         assert_ne!(file.as_raw_fd(), s2.as_raw_fd());
525         assert_ne!(file.as_raw_fd(), evt.as_raw_fd());
526 
527         file.write_all(unsafe { from_raw_parts(&1203u64 as *const u64 as *const u8, 8) })
528             .expect("failed to write to sent fd");
529 
530         assert_eq!(evt.read().expect("failed to read from eventfd"), 1203);
531     }
532 
533     #[test]
send_recv_with_fd()534     fn send_recv_with_fd() {
535         let (s1, s2) = UnixDatagram::pair().expect("failed to create socket pair");
536 
537         let evt = EventFd::new().expect("failed to create eventfd");
538         let ioslice = IoSlice::new([237].as_ref());
539         let write_count = s1
540             .send_with_fds(&[ioslice], &[evt.as_raw_fd()])
541             .expect("failed to send fd");
542 
543         assert_eq!(write_count, 1);
544 
545         let mut files = [0; 2];
546         let mut buf = [0u8];
547         let (read_count, file_count) = s2
548             .recv_with_fds(&mut buf, &mut files)
549             .expect("failed to recv fd");
550 
551         assert_eq!(read_count, 1);
552         assert_eq!(buf[0], 237);
553         assert_eq!(file_count, 1);
554         assert!(files[0] >= 0);
555         assert_ne!(files[0], s1.as_raw_fd());
556         assert_ne!(files[0], s2.as_raw_fd());
557         assert_ne!(files[0], evt.as_raw_fd());
558 
559         let mut file = unsafe { File::from_raw_fd(files[0]) };
560 
561         file.write_all(unsafe { from_raw_parts(&1203u64 as *const u64 as *const u8, 8) })
562             .expect("failed to write to sent fd");
563 
564         assert_eq!(evt.read().expect("failed to read from eventfd"), 1203);
565     }
566 }
567