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