Skip to main content

miri/shims/unix/
socket.rs

1use std::io;
2use std::net::{Shutdown, SocketAddr};
3use std::time::Duration;
4
5use rustc_abi::Size;
6use rustc_target::spec::Os;
7
8use crate::shims::files::FdNum;
9use crate::shims::unix::UnixFileDescription;
10use crate::shims::unix::socket_address::EvalContextExt as _;
11use crate::shims::unix::tcp_socket::TcpSocket;
12use crate::shims::unix::udp_socket::UdpSocket;
13use crate::shims::{FileDescription, FileDescriptionRef};
14use crate::*;
15
16/// Represents unix-specific socket file descriptions.
17///
18/// Not to be confused with Unix domain sockets.
19pub trait UnixSocketFileDescription: UnixFileDescription {
20    /// Bind the socket to `address`.
21    fn bind<'tcx>(
22        self: FileDescriptionRef<Self>,
23        _communicate_allowed: bool,
24        _address: SocketAddr,
25        _ecx: &mut MiriInterpCx<'tcx>,
26    ) -> InterpResult<'tcx, Result<(), IoError>> {
27        throw_unsup_format!("cannot bind {}", self.name());
28    }
29
30    /// Start listening on the socket.
31    /// `backlog` specifies how many pending incoming connections can exist at the same
32    /// time before new requests are rejected.
33    fn listen<'tcx>(
34        self: FileDescriptionRef<Self>,
35        _communicate_allowed: bool,
36        _backlog: i32,
37        _ecx: &mut MiriInterpCx<'tcx>,
38    ) -> InterpResult<'tcx, Result<(), IoError>> {
39        throw_unsup_format!("cannot listen on {}", self.name());
40    }
41
42    /// Accept an incoming connection on the socket.
43    /// `is_client_sock_non_block` specifies whether the newly accepted client connection
44    /// should be non-blocking.
45    /// After a successful accept, `finish` should be called with a tuple containing the
46    /// file descriptor of the peer socket and it's address.
47    fn accept<'tcx>(
48        self: FileDescriptionRef<Self>,
49        _communicate_allowed: bool,
50        _is_client_sock_non_block: bool,
51        _ecx: &mut MiriInterpCx<'tcx>,
52        _finish: DynMachineCallback<'tcx, Result<(FdNum, SocketAddr), IoError>>,
53    ) -> InterpResult<'tcx> {
54        throw_unsup_format!("cannot accept {}", self.name());
55    }
56
57    /// Connect the socket to `address`.
58    fn connect<'tcx>(
59        self: FileDescriptionRef<Self>,
60        _communicate_allowed: bool,
61        _address: SocketAddr,
62        _ecx: &mut MiriInterpCx<'tcx>,
63        _finish: DynMachineCallback<'tcx, Result<(), IoError>>,
64    ) -> InterpResult<'tcx> {
65        throw_unsup_format!("cannot connect {}", self.name());
66    }
67
68    /// Receive data on the socket into the given buffer `ptr` and return the socket
69    /// address from where the data originated.
70    /// `len` indicates how many bytes we should try to receive.
71    /// `is_peek` specifies whether the receive removes the bytes from the receive buffer
72    /// ([`false`]) or leaves them in the receive buffer ([`true`]).
73    /// `is_non_block` specifies whether the receive operation is non-blocking.
74    /// After a successful receive, `finish` should be called with a tuple containing the
75    /// amount of bytes received and the socket address from where the data originated, should
76    /// it be provided by the protocol.
77    fn recv<'tcx>(
78        self: FileDescriptionRef<Self>,
79        _communicate_allowed: bool,
80        _ptr: Pointer,
81        _len: u64,
82        _is_peek: bool,
83        _is_non_block: bool,
84        _ecx: &mut MiriInterpCx<'tcx>,
85        _finish: DynMachineCallback<'tcx, Result<(u64, Option<SocketAddr>), IoError>>,
86    ) -> InterpResult<'tcx> {
87        throw_unsup_format!("cannot receive from {}", self.name());
88    }
89
90    /// Send data from the given buffer `ptr` into the socket.
91    /// `len` indicates how many bytes we should try to send.
92    /// `is_non_block` specifies whether the send operation is non-blocking.
93    /// `dest_addr` optionally contains a socket address to which the data
94    /// should be sent instead of the default destination address.
95    /// After a successful send, `finish` should be called with the amount of bytes sent.
96    fn send<'tcx>(
97        self: FileDescriptionRef<Self>,
98        _communicate_allowed: bool,
99        _ptr: Pointer,
100        _len: u64,
101        _is_non_block: bool,
102        _dest_addr: Option<SocketAddr>,
103        _ecx: &mut MiriInterpCx<'tcx>,
104        _finish: DynMachineCallback<'tcx, Result<u64, IoError>>,
105    ) -> InterpResult<'tcx> {
106        throw_unsup_format!("cannot send to {}", self.name());
107    }
108
109    /// Set the socket option `option` on `level`.
110    /// `value_ptr` points to the new value of the socket option, and `value_len` contains
111    /// the amount of bytes the value uses at `value_ptr`.
112    fn setsockopt<'tcx>(
113        self: FileDescriptionRef<Self>,
114        _level: i32,
115        _option: i32,
116        _value_ptr: Pointer,
117        _value_len: u64,
118        _ecx: &mut MiriInterpCx<'tcx>,
119    ) -> InterpResult<'tcx, Result<(), IoError>> {
120        throw_unsup_format!("cannot set socket option on {}", self.name());
121    }
122
123    /// Get the value of a socket option `option` on `level`.
124    fn getsockopt<'tcx>(
125        self: FileDescriptionRef<Self>,
126        _level: i32,
127        _option: i32,
128        _ecx: &mut MiriInterpCx<'tcx>,
129    ) -> InterpResult<'tcx, Result<MPlaceTy<'tcx>, IoError>> {
130        throw_unsup_format!("cannot get socket option for {}", self.name());
131    }
132
133    /// Get the local address of the socket.
134    fn getsockname<'tcx>(
135        self: FileDescriptionRef<Self>,
136        _communicate_allowed: bool,
137        _ecx: &mut MiriInterpCx<'tcx>,
138    ) -> InterpResult<'tcx, Result<SocketAddr, IoError>> {
139        throw_unsup_format!("cannot get socket name for {}", self.name());
140    }
141
142    /// Get the remote address of the socket.
143    fn getpeername<'tcx>(
144        self: FileDescriptionRef<Self>,
145        _communicate_allowed: bool,
146        _ecx: &mut MiriInterpCx<'tcx>,
147        _finish: DynMachineCallback<'tcx, Result<SocketAddr, IoError>>,
148    ) -> InterpResult<'tcx> {
149        throw_unsup_format!("cannot get peer name for {}", self.name());
150    }
151
152    /// Shut down the read and/or the write end of the socket.
153    fn shutdown<'tcx>(
154        self: FileDescriptionRef<Self>,
155        _communicate_allowed: bool,
156        _how: Shutdown,
157        _ecx: &mut MiriInterpCx<'tcx>,
158    ) -> InterpResult<'tcx, Result<(), IoError>> {
159        throw_unsup_format!("cannot shut down {}", self.name());
160    }
161
162    // Socket options from SOL_SOCKET which are supported by all socket kinds.
163
164    /// Set the read timeout for the socket.
165    /// If `timeout` is [`None`], then read operations should block indefinitely.
166    /// Otherwise, read operations should block until `timeout` is expired.
167    fn set_read_timeout<'tcx>(
168        self: FileDescriptionRef<Self>,
169        _timeout: Option<Duration>,
170    ) -> InterpResult<'tcx> {
171        throw_unsup_format!("setsockopt: option SO_RCVTIMEO is unsupported for {}", self.name())
172    }
173
174    /// Get the read timeout for the socket.
175    fn get_read_timeout<'tcx>(
176        self: FileDescriptionRef<Self>,
177    ) -> InterpResult<'tcx, Option<Duration>> {
178        throw_unsup_format!("getsockopt: option SO_RCVTIMEO is unsupported for {}", self.name())
179    }
180
181    /// Set the write timeout for the socket.
182    /// If `timeout` is [`None`], then write operations should block indefinitely.
183    /// Otherwise, write operations should block until `timeout` is expired.
184    fn set_write_timeout<'tcx>(
185        self: FileDescriptionRef<Self>,
186        _timeout: Option<Duration>,
187    ) -> InterpResult<'tcx> {
188        throw_unsup_format!("setsockopt: option SO_SNDTIMEO is unsupported for {}", self.name())
189    }
190
191    /// Get the write timeout for the socket.
192    fn get_write_timeout<'tcx>(
193        self: FileDescriptionRef<Self>,
194    ) -> InterpResult<'tcx, Option<Duration>> {
195        throw_unsup_format!("getsockopt: option SO_SNDTIMEO is unsupported for {}", self.name())
196    }
197
198    /// Get the stored I/O error of the socket, if any exists.
199    /// This method should consume the stored error.
200    fn take_error<'tcx>(
201        self: FileDescriptionRef<Self>,
202        _ecx: &mut MiriInterpCx<'tcx>,
203    ) -> InterpResult<'tcx, Option<io::Error>> {
204        throw_unsup_format!("getsockopt: option SO_ERROR is unsupported for {}", self.name())
205    }
206
207    /// If the shimmed socket contains an underlying host socket, the `cb` callback
208    /// function is invoked with a [`socket2::SockRef`] to the underlying host socket
209    /// and [`Ok`] with the return value of the callback is returned.
210    /// Otherwise, when the shimmed socket is not backed by a host socket, [`Err`] is
211    /// returned.
212    fn with_socket_ref<T>(&self, _cb: impl FnOnce(socket2::SockRef<'_>) -> T) -> Result<T, ()>
213    where
214        Self: Sized,
215    {
216        Err(())
217    }
218}
219
220impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
221pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
222    /// For more information on the arguments see the socket manpage:
223    /// <https://linux.die.net/man/2/socket>
224    fn socket(
225        &mut self,
226        domain: &OpTy<'tcx>,
227        type_: &OpTy<'tcx>,
228        protocol: &OpTy<'tcx>,
229    ) -> InterpResult<'tcx, Scalar> {
230        let this = self.eval_context_mut();
231
232        let domain = this.read_scalar(domain)?.to_i32()?;
233        let mut flags = this.read_scalar(type_)?.to_i32()?;
234        let protocol = this.read_scalar(protocol)?.to_i32()?;
235
236        // Reject if isolation is enabled
237        if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
238            this.reject_in_isolation("`socket`", reject_with)?;
239            return this.set_errno_and_return_neg1_i32(LibcError("EACCES"));
240        }
241
242        let mut is_non_block = false;
243
244        // Interpret the flag. Every flag we recognize is "subtracted" from `flags`, so
245        // if there is anything left at the end, that's an unsupported flag.
246        if matches!(
247            this.tcx.sess.target.os,
248            Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
249        ) {
250            // SOCK_NONBLOCK and SOCK_CLOEXEC only exist on Linux, Android, FreeBSD,
251            // Solaris, and Illumos targets.
252            let sock_nonblock = this.eval_libc_i32("SOCK_NONBLOCK");
253            let sock_cloexec = this.eval_libc_i32("SOCK_CLOEXEC");
254            if flags & sock_nonblock == sock_nonblock {
255                is_non_block = true;
256                flags &= !sock_nonblock;
257            }
258            if flags & sock_cloexec == sock_cloexec {
259                // We don't support `exec` so we can ignore this.
260                flags &= !sock_cloexec;
261            }
262        }
263
264        let family = if domain == this.eval_libc_i32("AF_INET") {
265            socket2::Domain::IPV4
266        } else if domain == this.eval_libc_i32("AF_INET6") {
267            socket2::Domain::IPV6
268        } else {
269            throw_unsup_format!(
270                "socket: domain {:#x} is unsupported, only AF_INET and \
271            AF_INET6 are allowed.",
272                domain
273            );
274        };
275
276        let fd_result = if flags == this.eval_libc_i32("SOCK_STREAM") {
277            if ![0, this.eval_libc_i32("IPPROTO_TCP")].contains(&protocol) {
278                throw_unsup_format!(
279                    "socket: socket protocol {protocol} is unsupported for \
280                    streaming socket, only IPPROTO_TCP and 0 are allowed"
281                );
282            }
283
284            TcpSocket::new(family, is_non_block, this).map(|fd| fd.as_unix())
285        } else if flags == this.eval_libc_i32("SOCK_DGRAM") {
286            if ![0, this.eval_libc_i32("IPPROTO_UDP")].contains(&protocol) {
287                throw_unsup_format!(
288                    "socket: socket protocol {protocol} is unsupported for \
289                    datagram socket, only IPPROTO_UDP and 0 are allowed"
290                );
291            }
292
293            UdpSocket::new(family, is_non_block, this).map(|fd| fd.as_unix())
294        } else {
295            throw_unsup_format!(
296                "socket: type {:#x} is unsupported, only SOCK_STREAM, \
297                SOCK_DGRAM, SOCK_CLOEXEC and SOCK_NONBLOCK are allowed",
298                flags
299            );
300        };
301        let fd = match fd_result {
302            Ok(fd) => fd,
303            Err(e) => return this.set_errno_and_return_neg1_i32(e),
304        };
305        let Some(fd_num) = this.machine.fds.insert(fd) else {
306            return this.set_errno_and_return_neg1_i32(LibcError("EMFILE"));
307        };
308        interp_ok(Scalar::from_i32(fd_num))
309    }
310
311    fn bind(
312        &mut self,
313        socket: &OpTy<'tcx>,
314        address: &OpTy<'tcx>,
315        address_len: &OpTy<'tcx>,
316    ) -> InterpResult<'tcx, Scalar> {
317        let this = self.eval_context_mut();
318
319        let socket = this.read_scalar(socket)?.to_i32()?;
320        let address = match this.read_socket_address(address, address_len, "bind")? {
321            Ok(addr) => addr,
322            Err(e) => return this.set_errno_and_return_neg1_i32(e),
323        };
324
325        // Get the file handle
326        let Some(fd) = this.machine.fds.get(socket) else {
327            return this.set_errno_and_return_neg1_i32(LibcError("EBADF"));
328        };
329
330        let Some(socket) = fd.as_unix().as_socket() else {
331            return this.set_errno_and_return_neg1_i32(LibcError("ENOTSOCK"));
332        };
333
334        match socket.bind(this.machine.communicate(), address, this)? {
335            Ok(_) => interp_ok(Scalar::from_i32(0)),
336            Err(e) => this.set_errno_and_return_neg1_i32(e),
337        }
338    }
339
340    fn listen(&mut self, socket: &OpTy<'tcx>, backlog: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
341        let this = self.eval_context_mut();
342
343        let socket = this.read_scalar(socket)?.to_i32()?;
344        let backlog = this.read_scalar(backlog)?.to_i32()?;
345
346        // Get the file handle
347        let Some(fd) = this.machine.fds.get(socket) else {
348            return this.set_errno_and_return_neg1_i32(LibcError("EBADF"));
349        };
350
351        let Some(socket) = fd.as_unix().as_socket() else {
352            return this.set_errno_and_return_neg1_i32(LibcError("ENOTSOCK"));
353        };
354
355        match socket.listen(this.machine.communicate(), backlog, this)? {
356            Ok(_) => interp_ok(Scalar::from_i32(0)),
357            Err(e) => this.set_errno_and_return_neg1_i32(e),
358        }
359    }
360
361    /// For more information on the arguments see the accept manpage:
362    /// <https://linux.die.net/man/2/accept4>
363    fn accept4(
364        &mut self,
365        socket: &OpTy<'tcx>,
366        address: &OpTy<'tcx>,
367        address_len: &OpTy<'tcx>,
368        flags: Option<&OpTy<'tcx>>,
369        // Location where the output scalar is written to.
370        dest: &MPlaceTy<'tcx>,
371    ) -> InterpResult<'tcx> {
372        let this = self.eval_context_mut();
373
374        let socket = this.read_scalar(socket)?.to_i32()?;
375        let address_ptr = this.read_pointer(address)?;
376        let address_len_place = this.deref_pointer(address_len)?;
377        let mut flags =
378            if let Some(flags) = flags { this.read_scalar(flags)?.to_i32()? } else { 0 };
379
380        // Get the file handle
381        let Some(fd) = this.machine.fds.get(socket) else {
382            return this.set_errno_and_return_neg1(LibcError("EBADF"), dest);
383        };
384
385        let Some(socket) = fd.as_unix().as_socket() else {
386            return this.set_errno_and_return_neg1(LibcError("ENOTSOCK"), dest);
387        };
388
389        let mut is_client_sock_nonblock = false;
390
391        // Interpret the flag. Every flag we recognize is "subtracted" from `flags`, so
392        // if there is anything left at the end, that's an unsupported flag.
393        if matches!(
394            this.tcx.sess.target.os,
395            Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
396        ) {
397            // SOCK_NONBLOCK and SOCK_CLOEXEC only exist on Linux, Android, FreeBSD,
398            // Solaris, and Illumos targets.
399            let sock_nonblock = this.eval_libc_i32("SOCK_NONBLOCK");
400            let sock_cloexec = this.eval_libc_i32("SOCK_CLOEXEC");
401            if flags & sock_nonblock == sock_nonblock {
402                is_client_sock_nonblock = true;
403                flags &= !sock_nonblock;
404            }
405            if flags & sock_cloexec == sock_cloexec {
406                // We don't support `exec` so we can ignore this.
407                flags &= !sock_cloexec;
408            }
409        }
410
411        if flags != 0 {
412            throw_unsup_format!(
413                "accept4: flag {flags:#x} is unsupported, only SOCK_CLOEXEC \
414                and SOCK_NONBLOCK are allowed",
415            );
416        }
417
418        let dest = dest.clone();
419        socket.accept(
420            this.machine.communicate(),
421            is_client_sock_nonblock,
422            this,
423            callback!(
424                @capture<'tcx> {
425                    address_ptr: Pointer,
426                    address_len_place: MPlaceTy<'tcx>,
427                    dest: MPlaceTy<'tcx>
428                } |ecx, result: Result<(FdNum, SocketAddr), IoError>| {
429                    let (client_sockfd, address) = match result {
430                        Ok(data) => data,
431                        Err(e) => return ecx.set_errno_and_return_neg1(e, &dest),
432                    };
433
434                    if !ecx.ptr_is_null(address_ptr)? {
435                        // We only attempt a write if the address pointer is not a null pointer.
436                        // If the address pointer is a null pointer the user isn't interested in the
437                        // address and we don't need to write anything.
438                        ecx.write_socket_address(Some(&address), address_ptr, &address_len_place)?;
439                    }
440
441                    // We need to create the scalar using the destination size since
442                    // `syscall(SYS_accept4, ...)` returns a long which doesn't match
443                    // the int returned from the `accept`/`accept4` syscalls.
444                    // See <https://man7.org/linux/man-pages/man2/syscall.2.html>.
445                    ecx.write_scalar(Scalar::from_int(client_sockfd, dest.layout.size), &dest)
446                }
447            ),
448        )
449    }
450
451    fn connect(
452        &mut self,
453        socket: &OpTy<'tcx>,
454        address: &OpTy<'tcx>,
455        address_len: &OpTy<'tcx>,
456        // Location where the output scalar is written to.
457        dest: &MPlaceTy<'tcx>,
458    ) -> InterpResult<'tcx> {
459        let this = self.eval_context_mut();
460
461        let socket = this.read_scalar(socket)?.to_i32()?;
462        let address = match this.read_socket_address(address, address_len, "connect")? {
463            Ok(address) => address,
464            Err(e) => return this.set_errno_and_return_neg1(e, dest),
465        };
466
467        // Get the file handle
468        let Some(fd) = this.machine.fds.get(socket) else {
469            return this.set_errno_and_return_neg1(LibcError("EBADF"), dest);
470        };
471
472        let Some(socket) = fd.as_unix().as_socket() else {
473            return this.set_errno_and_return_neg1(LibcError("ENOTSOCK"), dest);
474        };
475
476        let dest = dest.clone();
477
478        socket.connect(
479            this.machine.communicate(),
480            address,
481            this,
482            callback!(
483                @capture<'tcx> {
484                    dest: MPlaceTy<'tcx>
485                } |ecx, result: Result<(), IoError>| {
486                    ecx.return_io_result_via_errno(result.map(|()| 0), &dest)
487                }
488            ),
489        )
490    }
491
492    fn send(
493        &mut self,
494        socket: &OpTy<'tcx>,
495        buffer: &OpTy<'tcx>,
496        length: &OpTy<'tcx>,
497        flags: &OpTy<'tcx>,
498        dest_addr: Option<(&OpTy<'tcx>, &OpTy<'tcx>)>,
499        // Location where the output scalar is written to.
500        dest: &MPlaceTy<'tcx>,
501    ) -> InterpResult<'tcx> {
502        let this = self.eval_context_mut();
503
504        let socket = this.read_scalar(socket)?.to_i32()?;
505        let buffer_ptr = this.read_pointer(buffer)?;
506        let length = this.read_target_usize(length)?;
507        let mut flags = this.read_scalar(flags)?.to_i32()?;
508
509        let dest_addr = match dest_addr {
510            Some((dest_addr, dest_addr_len)) => {
511                let dest_addr_ptr = this.read_pointer(dest_addr)?;
512                let socklen_layout = this.libc_ty_layout("socklen_t");
513                let dest_addr_len_int =
514                    this.read_scalar(dest_addr_len)?.to_uint(socklen_layout.size)?;
515
516                // POSIX says the destination is entirely ignored for TCP sockets, but that does not
517                // match reality, so we eagerly read the destination when one is given.
518                if this.ptr_is_null(dest_addr_ptr)? || dest_addr_len_int == 0 {
519                    // If the address pointer is a nullptr or the address length is zero we ignore
520                    // the destination address. BSDs return EINVAL when the address length is zero
521                    // and the address pointer is an invalid pointer; that is so far away from what
522                    // POSIX documents for TCP sockets that we do not try to emulate it.
523                    None
524                } else {
525                    match this.read_socket_address(dest_addr, dest_addr_len, "sendto")? {
526                        Ok(dest_addr) => Some(dest_addr),
527                        Err(e) => return this.set_errno_and_return_neg1(e, dest),
528                    }
529                }
530            }
531            None => None,
532        };
533
534        // Get the file handle
535        let Some(fd) = this.machine.fds.get(socket) else {
536            return this.set_errno_and_return_neg1(LibcError("EBADF"), dest);
537        };
538
539        let Some(socket) = fd.as_unix().as_socket() else {
540            return this.set_errno_and_return_neg1(LibcError("ENOTSOCK"), dest);
541        };
542
543        let mut is_non_block = false;
544
545        // Interpret the flag. Every flag we recognize is "subtracted" from `flags`, so
546        // if there is anything left at the end, that's an unsupported flag.
547        if matches!(
548            this.tcx.sess.target.os,
549            Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
550        ) {
551            // MSG_NOSIGNAL and MSG_DONTWAIT only exist on Linux, Android, FreeBSD,
552            // Solaris, and Illumos targets.
553            let msg_nosignal = this.eval_libc_i32("MSG_NOSIGNAL");
554            let msg_dontwait = this.eval_libc_i32("MSG_DONTWAIT");
555            if flags & msg_nosignal == msg_nosignal {
556                // This is only needed to ensure that no EPIPE signal is sent when
557                // trying to send into a stream which is no longer connected.
558                // Since we don't support signals, we can ignore this.
559                flags &= !msg_nosignal;
560            }
561            if flags & msg_dontwait == msg_dontwait {
562                flags &= !msg_dontwait;
563                is_non_block = true;
564            }
565        }
566
567        if flags != 0 {
568            throw_unsup_format!(
569                "send: flag {flags:#x} is unsupported, only MSG_NOSIGNAL and MSG_DONTWAIT are allowed",
570            );
571        }
572
573        let dest = dest.clone();
574
575        socket.send(
576            this.machine.communicate(),
577            buffer_ptr,
578            length,
579            is_non_block,
580            dest_addr,
581            this,
582            callback!(
583                @capture<'tcx> {
584                    dest: MPlaceTy<'tcx>,
585                } |ecx, result: Result<u64, IoError>| {
586                    match result {
587                        Ok(bytes_sent) =>
588                            ecx.write_scalar(Scalar::from_target_isize(bytes_sent.try_into().unwrap(), ecx), &dest),
589                        Err(e) => ecx.set_errno_and_return_neg1(e, &dest)
590                    }
591                }
592            )
593        )
594    }
595
596    fn recv(
597        &mut self,
598        socket: &OpTy<'tcx>,
599        buffer: &OpTy<'tcx>,
600        length: &OpTy<'tcx>,
601        flags: &OpTy<'tcx>,
602        dest_addr: Option<(&OpTy<'tcx>, &OpTy<'tcx>)>,
603        // Location where the output scalar is written to.
604        dest: &MPlaceTy<'tcx>,
605    ) -> InterpResult<'tcx> {
606        let this = self.eval_context_mut();
607
608        let socket = this.read_scalar(socket)?.to_i32()?;
609        let buffer_ptr = this.read_pointer(buffer)?;
610        let length = this.read_target_usize(length)?;
611        let mut flags = this.read_scalar(flags)?.to_i32()?;
612
613        let dest_addr = match dest_addr {
614            Some((addr, addr_len)) =>
615                Some((this.read_pointer(addr)?, this.deref_pointer(addr_len)?)),
616            None => None,
617        };
618
619        // Get the file handle
620        let Some(fd) = this.machine.fds.get(socket) else {
621            return this.set_errno_and_return_neg1(LibcError("EBADF"), dest);
622        };
623
624        let Some(socket) = fd.as_unix().as_socket() else {
625            return this.set_errno_and_return_neg1(LibcError("ENOTSOCK"), dest);
626        };
627
628        let mut is_peek = false;
629        let mut is_non_block = false;
630
631        // Interpret the flag. Every flag we recognize is "subtracted" from `flags`, so
632        // if there is anything left at the end, that's an unsupported flag.
633
634        let msg_peek = this.eval_libc_i32("MSG_PEEK");
635        if flags & msg_peek == msg_peek {
636            is_peek = true;
637            flags &= !msg_peek;
638        }
639
640        if matches!(this.tcx.sess.target.os, Os::Linux | Os::Android | Os::FreeBsd | Os::Illumos) {
641            // MSG_CMSG_CLOEXEC only exists on Linux, Android, FreeBSD,
642            // and Illumos targets.
643            let msg_cmsg_cloexec = this.eval_libc_i32("MSG_CMSG_CLOEXEC");
644            if flags & msg_cmsg_cloexec == msg_cmsg_cloexec {
645                // We don't support `exec` so we can ignore this.
646                flags &= !msg_cmsg_cloexec;
647            }
648        }
649
650        if matches!(
651            this.tcx.sess.target.os,
652            Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
653        ) {
654            // MSG_DONTWAIT only exists on Linux, Android, FreeBSD,
655            // Solaris, and Illumos targets.
656            let msg_dontwait = this.eval_libc_i32("MSG_DONTWAIT");
657            if flags & msg_dontwait == msg_dontwait {
658                flags &= !msg_dontwait;
659                is_non_block = true;
660            }
661        }
662
663        if flags != 0 {
664            throw_unsup_format!(
665                "recv: flag {flags:#x} is unsupported, only MSG_PEEK, MSG_DONTWAIT \
666                and MSG_CMSG_CLOEXEC are allowed",
667            );
668        }
669
670        let dest = dest.clone();
671
672        socket.recv(
673            this.machine.communicate(),
674            buffer_ptr,
675            length,
676            is_peek,
677            is_non_block,
678            this,
679            callback!(
680                @capture<'tcx> {
681                    dest_addr: Option<(Pointer, MPlaceTy<'tcx>)>,
682                    dest: MPlaceTy<'tcx>,
683                } |ecx, result: Result<(u64, Option<SocketAddr>), IoError>| {
684                    let (bytes_received, mut origin_addr_opt) = match result {
685                        Ok(data) => data,
686                        Err(e) => return ecx.set_errno_and_return_neg1(e, &dest)
687                    };
688
689                    if let Some((addr_ptr, addr_len_mplace)) = dest_addr  {
690                        if ecx.ptr_is_null(addr_ptr)? {
691                            // When the `addr_ptr` is a null pointer the caller is not interested in
692                            // the socket address.
693                            origin_addr_opt = None;
694                        }
695                        // The recvfrom(3) man page specifies that "[...] the value stored in the object pointed to
696                        // `address` is unspecified [...]" should the protocol not provide the source address of the message.
697                        // In this case we do the same thing as the Linux implementation and simply don't write anything to the
698                        // `addr_ptr` pointer and zero the `addr_len_mplace` place.
699                        ecx.write_socket_address(origin_addr_opt.as_ref(), addr_ptr, &addr_len_mplace)?;
700                    }
701
702                    ecx.write_scalar(Scalar::from_target_isize(bytes_received.try_into().unwrap(), ecx), &dest)
703
704                }
705            ),
706        )
707    }
708
709    fn setsockopt(
710        &mut self,
711        socket: &OpTy<'tcx>,
712        level: &OpTy<'tcx>,
713        option_name: &OpTy<'tcx>,
714        option_value: &OpTy<'tcx>,
715        option_len: &OpTy<'tcx>,
716    ) -> InterpResult<'tcx, Scalar> {
717        let this = self.eval_context_mut();
718
719        let socket = this.read_scalar(socket)?.to_i32()?;
720        let level = this.read_scalar(level)?.to_i32()?;
721        let option_name = this.read_scalar(option_name)?.to_i32()?;
722        let option_value_ptr = this.read_pointer(option_value)?;
723        let Some(option_len) = this.read_socklen(option_len)? else {
724            return this.set_errno_and_return_neg1_i32(LibcError("EINVAL"));
725        };
726
727        // Check that the buffer has (at least) the given size.
728        this.check_ptr_access(
729            option_value_ptr,
730            Size::from_bytes(option_len),
731            CheckInAllocMsg::MemoryAccess,
732        )?;
733
734        // Get the file handle
735        let Some(fd) = this.machine.fds.get(socket) else {
736            return this.set_errno_and_return_neg1_i32(LibcError("EBADF"));
737        };
738
739        let Some(socket) = fd.as_unix().as_socket() else {
740            return this.set_errno_and_return_neg1_i32(LibcError("ENOTSOCK"));
741        };
742
743        let result = socket.setsockopt(level, option_name, option_value_ptr, option_len, this)?;
744        match result {
745            Ok(_) => interp_ok(Scalar::from_i32(0)),
746            Err(e) => this.set_errno_and_return_neg1_i32(e),
747        }
748    }
749
750    fn getsockopt(
751        &mut self,
752        socket: &OpTy<'tcx>,
753        level: &OpTy<'tcx>,
754        option_name: &OpTy<'tcx>,
755        option_value: &OpTy<'tcx>,
756        option_len: &OpTy<'tcx>,
757    ) -> InterpResult<'tcx, Scalar> {
758        let this = self.eval_context_mut();
759
760        let socket = this.read_scalar(socket)?.to_i32()?;
761        let level = this.read_scalar(level)?.to_i32()?;
762        let option_name = this.read_scalar(option_name)?.to_i32()?;
763        // These two pointers are used to return the value: `len_ptr` initially stores how much space
764        // is available. If the actual value fits into that space, it is written to
765        // `value_ptr` and `len_ptr` is updated to represent how many bytes
766        // were actually written. If the value does not fit, it is silently truncated.
767        // Also see <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getsockopt.html>.
768        let option_value_ptr = this.read_pointer(option_value)?;
769        let option_len_ptr = this.read_pointer(option_len)?;
770
771        // Get the file handle
772        let Some(fd) = this.machine.fds.get(socket) else {
773            return this.set_errno_and_return_neg1_i32(LibcError("EBADF"));
774        };
775
776        let Some(socket) = fd.as_unix().as_socket() else {
777            return this.set_errno_and_return_neg1_i32(LibcError("ENOTSOCK"));
778        };
779
780        if this.ptr_is_null(option_value_ptr)? || this.ptr_is_null(option_len_ptr)? {
781            // This socket option returns a value and thus we need to return EFAULT
782            // when either the value or the length pointers are null pointers.
783            return this.set_errno_and_return_neg1_i32(LibcError("EFAULT"));
784        }
785
786        let socklen_layout = this.libc_ty_layout("socklen_t");
787        let option_len_ptr_mplace = this.ptr_to_mplace(option_len_ptr, socklen_layout);
788        let Some(option_len) = this.read_socklen(&option_len_ptr_mplace)? else {
789            return this.set_errno_and_return_neg1_i32(LibcError("EINVAL"));
790        };
791
792        // Check that the buffer has (at least) the given size.
793        this.check_ptr_access(
794            option_value_ptr,
795            Size::from_bytes(option_len),
796            CheckInAllocMsg::MemoryAccess,
797        )?;
798
799        // `socket.getsockopt` returns a temporary buffer as `option_value_ptr` might not point
800        // to a large enough buffer, in which case we have to truncate.
801        let value_mplace = match socket.getsockopt(level, option_name, this)? {
802            Ok(value_mplace) => value_mplace,
803            Err(e) => return this.set_errno_and_return_neg1_i32(e),
804        };
805
806        // Truncated size of the output value.
807        let output_value_len = value_mplace.layout.size.min(Size::from_bytes(option_len));
808        // Copy the truncated value into the buffer pointed to by `option_value_ptr`.
809        this.mem_copy(
810            value_mplace.ptr(),
811            option_value_ptr,
812            // Truncate the value to fit the provided buffer.
813            output_value_len,
814            // The buffers are guaranteed to not overlap since the `value_mplace`
815            // was just newly allocated on the stack.
816            true,
817        )?;
818        // Deallocate the value buffer as it was only needed to store the value and
819        // copy it into the buffer pointed to by `option_value_ptr`.
820        this.deallocate_ptr(value_mplace.ptr(), None, MemoryKind::Stack)?;
821
822        // On output, the length pointer contains the amount of bytes written -- not the size
823        // of the value before truncation.
824        this.write_scalar(
825            Scalar::from_uint(output_value_len.bytes(), socklen_layout.size),
826            &option_len_ptr_mplace,
827        )?;
828
829        interp_ok(Scalar::from_i32(0))
830    }
831
832    fn getsockname(
833        &mut self,
834        socket: &OpTy<'tcx>,
835        address: &OpTy<'tcx>,
836        address_len: &OpTy<'tcx>,
837    ) -> InterpResult<'tcx, Scalar> {
838        let this = self.eval_context_mut();
839
840        let socket = this.read_scalar(socket)?.to_i32()?;
841        let address_ptr = this.read_pointer(address)?;
842        let address_len_place = this.deref_pointer(address_len)?;
843
844        // Get the file handle
845        let Some(fd) = this.machine.fds.get(socket) else {
846            return this.set_errno_and_return_neg1_i32(LibcError("EBADF"));
847        };
848
849        let Some(socket) = fd.as_unix().as_socket() else {
850            return this.set_errno_and_return_neg1_i32(LibcError("ENOTSOCK"));
851        };
852
853        let address = match socket.getsockname(this.machine.communicate(), this)? {
854            Ok(address) => address,
855            Err(e) => return this.set_errno_and_return_neg1_i32(e),
856        };
857
858        this.write_socket_address(Some(&address), address_ptr, &address_len_place)
859            .map(|()| Scalar::from_i32(0))
860    }
861
862    fn getpeername(
863        &mut self,
864        socket: &OpTy<'tcx>,
865        address: &OpTy<'tcx>,
866        address_len: &OpTy<'tcx>,
867        // Location where the output scalar is written to.
868        dest: &MPlaceTy<'tcx>,
869    ) -> InterpResult<'tcx> {
870        let this = self.eval_context_mut();
871
872        let socket = this.read_scalar(socket)?.to_i32()?;
873        let address_ptr = this.read_pointer(address)?;
874        let address_len_place = this.deref_pointer(address_len)?;
875
876        // Get the file handle
877        let Some(fd) = this.machine.fds.get(socket) else {
878            return this.set_errno_and_return_neg1(LibcError("EBADF"), dest);
879        };
880
881        let Some(socket) = fd.as_unix().as_socket() else {
882            return this.set_errno_and_return_neg1(LibcError("ENOTSOCK"), dest);
883        };
884
885        let dest = dest.clone();
886
887        socket.getpeername(
888            this.machine.communicate(),
889            this,
890            callback!(
891                @capture<'tcx> {
892                    address_ptr: Pointer,
893                    address_len_place: MPlaceTy<'tcx>,
894                    dest: MPlaceTy<'tcx>,
895                } |ecx, result: Result<SocketAddr, IoError>| {
896                    let address = match result {
897                        Ok(address) => address,
898                        Err(e) => return ecx.set_errno_and_return_neg1(e, &dest)
899                    };
900
901                    ecx.write_socket_address(
902                        Some(&address),
903                        address_ptr,
904                        &address_len_place,
905                    )?;
906                    ecx.write_scalar(Scalar::from_i32(0), &dest)
907                }
908            ),
909        )
910    }
911
912    fn shutdown(&mut self, socket: &OpTy<'tcx>, how: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
913        let this = self.eval_context_mut();
914
915        let socket = this.read_scalar(socket)?.to_i32()?;
916        let how = this.read_scalar(how)?.to_i32()?;
917
918        // Get the file handle
919        let Some(fd) = this.machine.fds.get(socket) else {
920            return this.set_errno_and_return_neg1_i32(LibcError("EBADF"));
921        };
922
923        let Some(socket) = fd.as_unix().as_socket() else {
924            return this.set_errno_and_return_neg1_i32(LibcError("ENOTSOCK"));
925        };
926
927        let is_read_shutdown = how == this.eval_libc_i32("SHUT_RD");
928        let is_write_shutdown = how == this.eval_libc_i32("SHUT_WR");
929        let is_read_write_shutdown = how == this.eval_libc_i32("SHUT_RDWR");
930
931        let how = match () {
932            _ if is_read_shutdown => Shutdown::Read,
933            _ if is_write_shutdown => Shutdown::Write,
934            _ if is_read_write_shutdown => Shutdown::Both,
935            // An invalid value was passed to `how`.
936            _ => return this.set_errno_and_return_neg1_i32(LibcError("EINVAL")),
937        };
938
939        match socket.shutdown(this.machine.communicate(), how, this)? {
940            Ok(_) => interp_ok(Scalar::from_i32(0)),
941            Err(e) => this.set_errno_and_return_neg1_i32(e),
942        }
943    }
944
945    /// Set the `option` socket option on level SOL_SOCKET for `socket`.
946    /// `value_ptr` points to the new value of the socket option, and `value_len`
947    /// contains the amount of bytes the value uses at `value_ptr`.
948    fn set_generic_socket_option<T: UnixSocketFileDescription>(
949        &mut self,
950        option: i32,
951        value_ptr: Pointer,
952        value_len: u64,
953        socket: FileDescriptionRef<T>,
954    ) -> InterpResult<'tcx, Result<(), IoError>> {
955        let ecx = self.eval_context_mut();
956
957        let opt_so_rcvtimeo = ecx.eval_libc_i32("SO_RCVTIMEO");
958        let opt_so_sndtimeo = ecx.eval_libc_i32("SO_SNDTIMEO");
959        let opt_so_reuseaddr = ecx.eval_libc_i32("SO_REUSEADDR");
960        let opt_so_linger = if matches!(ecx.tcx.sess.target.os, Os::MacOs) {
961            // On macOS the SO_LINGER socket option sets the linger duration in kernel ticks
962            // while the SO_LINGER_SEC socket option sets the linger duration in seconds.
963            ecx.eval_libc_i32("SO_LINGER_SEC")
964        } else {
965            // For all other targets SO_LINGER sets the linger duration in seconds.
966            ecx.eval_libc_i32("SO_LINGER")
967        };
968        let opt_so_keepalive = ecx.eval_libc_i32("SO_KEEPALIVE");
969
970        if matches!(ecx.tcx.sess.target.os, Os::MacOs | Os::FreeBsd | Os::NetBsd) {
971            // SO_NOSIGPIPE only exists on MacOS, FreeBSD, and NetBSD.
972            let opt_so_nosigpipe = ecx.eval_libc_i32("SO_NOSIGPIPE");
973
974            if option == opt_so_nosigpipe {
975                if value_len != 4 {
976                    // Option value should be C-int which is usually 4 bytes.
977                    return interp_ok(Err(LibcError("EINVAL")));
978                }
979                let option_value = ecx.ptr_to_mplace(value_ptr, ecx.machine.layouts.i32);
980                let _val = ecx.read_scalar(&option_value)?.to_i32()?;
981                // We entirely ignore this value since we do not support signals anyway.
982
983                return interp_ok(Ok(()));
984            }
985        }
986
987        if option == opt_so_rcvtimeo || option == opt_so_sndtimeo {
988            let timeval_layout = ecx.libc_ty_layout("timeval");
989            let option_value = ecx.ptr_to_mplace(value_ptr, timeval_layout);
990
991            let timeout = match ecx.read_timeval(&option_value)? {
992                None => return interp_ok(Err(LibcError("EINVAL"))),
993                Some(Duration::ZERO) => None,
994                Some(duration) => Some(duration),
995            };
996
997            if option == opt_so_rcvtimeo {
998                socket.set_read_timeout(timeout)?;
999            } else {
1000                socket.set_write_timeout(timeout)?;
1001            }
1002
1003            return interp_ok(Ok(()));
1004        }
1005
1006        if option == opt_so_reuseaddr {
1007            if value_len != 4 {
1008                // Option value should be C-int which is usually 4 bytes.
1009                return interp_ok(Err(LibcError("EINVAL")));
1010            }
1011            let option_value = ecx.ptr_to_mplace(value_ptr, ecx.machine.layouts.i32);
1012            let reuse = ecx.read_scalar(&option_value)?.to_i32()? != 0;
1013
1014            let result = socket.with_socket_ref(|s| s.set_reuse_address(reuse)).map_err(|()| {
1015                err_unsup_format!(
1016                    "setsockopt: option SO_REUSEADDR is unsupported for {}",
1017                    socket.name()
1018                )
1019            })?;
1020            return interp_ok(result.map_err(IoError::HostError));
1021        } else if option == opt_so_linger {
1022            let linger_layout = ecx.libc_ty_layout("linger");
1023            if value_len != linger_layout.size.bytes() {
1024                // Size should be equal to the size of a linger struct.
1025                return interp_ok(Err(LibcError("EINVAL")));
1026            }
1027            let option_value = ecx.ptr_to_mplace(value_ptr, linger_layout);
1028
1029            let onoff_field = ecx.project_field_named(&option_value, "l_onoff")?;
1030            let onoff = ecx.read_scalar(&onoff_field)?.to_i32()? != 0;
1031
1032            let linger_field = ecx.project_field_named(&option_value, "l_linger")?;
1033            let linger = ecx.read_scalar(&linger_field)?.to_i32()?;
1034
1035            let linger = onoff.then_some(Duration::from_secs(u64::try_from(linger).unwrap()));
1036
1037            let result = socket.with_socket_ref(|s| s.set_linger(linger)).map_err(|()| {
1038                err_unsup_format!(
1039                    "setsockopt: option SO_LINGER is unsupported for {}",
1040                    socket.name()
1041                )
1042            })?;
1043            return interp_ok(result.map_err(IoError::HostError));
1044        } else if option == opt_so_keepalive {
1045            if value_len != 4 {
1046                // Option value should be C-int which is usually 4 bytes.
1047                return interp_ok(Err(LibcError("EINVAL")));
1048            }
1049            let option_value = ecx.ptr_to_mplace(value_ptr, ecx.machine.layouts.i32);
1050            let keepalive = ecx.read_scalar(&option_value)?.to_i32()? != 0;
1051
1052            let result = socket.with_socket_ref(|s| s.set_keepalive(keepalive)).map_err(|()| {
1053                err_unsup_format!(
1054                    "setsockopt: option SO_LINGER is unsupported for {}",
1055                    socket.name()
1056                )
1057            })?;
1058            return interp_ok(result.map_err(IoError::HostError));
1059        }
1060
1061        throw_unsup_format!("setsockopt: option {option:#x} is unsupported for level SOL_SOCKET");
1062    }
1063
1064    /// Set the `option` socket option on level IPPROTO_IP for `socket`.
1065    /// `value_ptr` points to the new value of the socket option, and `value_len`
1066    /// contains the amount of bytes the value uses at `value_ptr`.
1067    /// `socket` is a [`socket2::SockRef`] to the underlying host socket of
1068    /// the shimmed socket.
1069    fn set_ip_socket_option(
1070        &self,
1071        option: i32,
1072        value_ptr: Pointer,
1073        value_len: u64,
1074        socket: socket2::SockRef<'_>,
1075    ) -> InterpResult<'tcx, Result<(), IoError>> {
1076        let ecx = self.eval_context_ref();
1077
1078        let opt_ip_ttl = ecx.eval_libc_i32("IP_TTL");
1079
1080        if option == opt_ip_ttl {
1081            if value_len != 4 {
1082                // Option value should be C-uint which is usually 4 bytes.
1083                return interp_ok(Err(LibcError("EINVAL")));
1084            }
1085            let option_value = ecx.ptr_to_mplace(value_ptr, ecx.machine.layouts.u32);
1086            let ttl = ecx.read_scalar(&option_value)?.to_u32()?;
1087
1088            return interp_ok(socket.set_ttl_v4(ttl).map_err(IoError::HostError));
1089        }
1090
1091        throw_unsup_format!("setsockopt: option {option:#x} is unsupported for level IPPROTO_IP",);
1092    }
1093
1094    /// Set the `option` socket option on level IPPROTO_TCP for `socket`.
1095    /// `value_ptr` points to the new value of the socket option, and `value_len`
1096    /// contains the amount of bytes the value uses at `value_ptr`.
1097    /// `socket` is a [`socket2::SockRef`] to the underlying host socket of
1098    /// the shimmed socket.
1099    fn set_tcp_socket_option(
1100        &self,
1101        option: i32,
1102        value_ptr: Pointer,
1103        value_len: u64,
1104        socket: socket2::SockRef<'_>,
1105    ) -> InterpResult<'tcx, Result<(), IoError>> {
1106        let ecx = self.eval_context_ref();
1107
1108        let opt_tcp_nodelay = ecx.eval_libc_i32("TCP_NODELAY");
1109
1110        if option == opt_tcp_nodelay {
1111            if value_len != 4 {
1112                // Option value should be C-int which is usually 4 bytes.
1113                return interp_ok(Err(LibcError("EINVAL")));
1114            }
1115            let option_value = ecx.ptr_to_mplace(value_ptr, ecx.machine.layouts.i32);
1116            let nodelay = ecx.read_scalar(&option_value)?.to_i32()? != 0;
1117
1118            return interp_ok(socket.set_tcp_nodelay(nodelay).map_err(IoError::HostError));
1119        }
1120
1121        throw_unsup_format!("setsockopt: option {option:#x} is unsupported for level IPPROTO_TCP");
1122    }
1123
1124    /// Get the `option` socket option on level SOL_SOCKET for `socket`.
1125    fn get_generic_socket_option<T: UnixSocketFileDescription>(
1126        &mut self,
1127        option: i32,
1128        socket: FileDescriptionRef<T>,
1129    ) -> InterpResult<'tcx, Result<MPlaceTy<'tcx>, IoError>> {
1130        let ecx = self.eval_context_mut();
1131
1132        let opt_so_error = ecx.eval_libc_i32("SO_ERROR");
1133        let opt_so_rcvtimeo = ecx.eval_libc_i32("SO_RCVTIMEO");
1134        let opt_so_sndtimeo = ecx.eval_libc_i32("SO_SNDTIMEO");
1135        let opt_so_reuseaddr = ecx.eval_libc_i32("SO_REUSEADDR");
1136        let opt_so_linger = if matches!(ecx.tcx.sess.target.os, Os::MacOs) {
1137            // On macOS the SO_LINGER socket option sets the linger duration in kernel ticks
1138            // while the SO_LINGER_SEC socket option sets the linger duration in seconds.
1139            ecx.eval_libc_i32("SO_LINGER_SEC")
1140        } else {
1141            // For all other targets SO_LINGER sets the linger duration in seconds.
1142            ecx.eval_libc_i32("SO_LINGER")
1143        };
1144        let opt_so_keepalive = ecx.eval_libc_i32("SO_KEEPALIVE");
1145
1146        if option == opt_so_error {
1147            let return_value = match socket.take_error(ecx)? {
1148                Some(err) => ecx.io_error_to_errnum(err)?.to_i32()?,
1149                // If there is no error, we return 0 as the option value.
1150                None => 0,
1151            };
1152
1153            // Allocate new buffer on the stack with the `i32` layout.
1154            let value_buffer = ecx.allocate(ecx.machine.layouts.i32, MemoryKind::Stack)?;
1155            ecx.write_int(return_value, &value_buffer)?;
1156            return interp_ok(Ok(value_buffer));
1157        } else if option == opt_so_rcvtimeo || option == opt_so_sndtimeo {
1158            let timeout = if option == opt_so_rcvtimeo {
1159                socket.get_read_timeout()?
1160            } else {
1161                socket.get_write_timeout()?
1162            }
1163            .unwrap_or_default();
1164
1165            let secs = timeout.as_secs();
1166            let usecs = timeout.subsec_micros();
1167
1168            let timeval_layout = ecx.libc_ty_layout("timeval");
1169            // Allocate new buffer on the stack with the `timeval` layout.
1170            let timeval_buffer = ecx.allocate(timeval_layout, MemoryKind::Stack)?;
1171
1172            let sec_field = ecx.project_field_named(&timeval_buffer, "tv_sec")?;
1173            ecx.write_int(secs, &sec_field)?;
1174
1175            let usec_field = ecx.project_field_named(&timeval_buffer, "tv_usec")?;
1176            ecx.write_int(usecs, &usec_field)?;
1177
1178            return interp_ok(Ok(timeval_buffer));
1179        }
1180
1181        if option == opt_so_reuseaddr {
1182            let reuse = match socket.with_socket_ref(|s| s.reuse_address()) {
1183                Ok(Ok(reuse)) => reuse,
1184                Ok(Err(e)) => return interp_ok(Err(IoError::HostError(e))),
1185                Err(()) =>
1186                    throw_unsup_format!(
1187                        "getsockopt: option SO_REUSEADDR is unsupported for {}",
1188                        socket.name()
1189                    ),
1190            };
1191
1192            // Allocate new buffer on the stack with the `i32` layout.
1193            let value_buffer = ecx.allocate(ecx.machine.layouts.i32, MemoryKind::Stack)?;
1194            ecx.write_int(i32::from(reuse), &value_buffer)?;
1195
1196            return interp_ok(Ok(value_buffer));
1197        } else if option == opt_so_linger {
1198            let linger = match socket.with_socket_ref(|s| s.linger()) {
1199                Ok(Ok(linger)) => linger,
1200                Ok(Err(e)) => return interp_ok(Err(IoError::HostError(e))),
1201                Err(()) =>
1202                    throw_unsup_format!(
1203                        "getsockopt: option SO_LINGER is unsupported for {}",
1204                        socket.name()
1205                    ),
1206            };
1207
1208            let linger_layout = ecx.libc_ty_layout("linger");
1209            // Allocate new buffer on the stack with the `linger` layout.
1210            let linger_buffer = ecx.allocate(linger_layout, MemoryKind::Stack)?;
1211
1212            let onoff_field = ecx.project_field_named(&linger_buffer, "l_onoff")?;
1213            let onoff = i32::from(linger.is_some());
1214            ecx.write_int(onoff, &onoff_field)?;
1215
1216            let linger_field = ecx.project_field_named(&linger_buffer, "l_linger")?;
1217            let secs = i32::try_from(linger.unwrap_or_default().as_secs()).unwrap();
1218            ecx.write_int(secs, &linger_field)?;
1219
1220            return interp_ok(Ok(linger_buffer));
1221        } else if option == opt_so_keepalive {
1222            let keepalive = match socket.with_socket_ref(|s| s.keepalive()) {
1223                Ok(Ok(keepalive)) => keepalive,
1224                Ok(Err(e)) => return interp_ok(Err(IoError::HostError(e))),
1225                Err(()) =>
1226                    throw_unsup_format!(
1227                        "getsockopt: option SO_KEEPALIVE is unsupported for {}",
1228                        socket.name()
1229                    ),
1230            };
1231
1232            // Allocate new buffer on the stack with the `i32` layout.
1233            let value_buffer = ecx.allocate(ecx.machine.layouts.i32, MemoryKind::Stack)?;
1234            ecx.write_int(i32::from(keepalive), &value_buffer)?;
1235
1236            return interp_ok(Ok(value_buffer));
1237        }
1238
1239        throw_unsup_format!("getsockopt: option {option:#x} is unsupported for level SOL_SOCKET",);
1240    }
1241
1242    /// Get the `option` socket option on level IPPROTO_IP for `socket`.
1243    /// `socket` is a [`socket2::SockRef`] to the underlying host socket of
1244    /// the shimmed socket.
1245    fn get_ip_socket_option(
1246        &mut self,
1247        option: i32,
1248        socket: socket2::SockRef<'_>,
1249    ) -> InterpResult<'tcx, Result<MPlaceTy<'tcx>, IoError>> {
1250        let ecx = self.eval_context_mut();
1251
1252        let opt_ip_ttl = ecx.eval_libc_i32("IP_TTL");
1253
1254        if option == opt_ip_ttl {
1255            let ttl = match socket.ttl_v4() {
1256                Ok(ttl) => ttl,
1257                Err(e) => return interp_ok(Err(IoError::HostError(e))),
1258            };
1259
1260            // Allocate new buffer on the stack with the `u32` layout.
1261            let value_buffer = ecx.allocate(ecx.machine.layouts.u32, MemoryKind::Stack)?;
1262            ecx.write_int(ttl, &value_buffer)?;
1263            return interp_ok(Ok(value_buffer));
1264        }
1265
1266        throw_unsup_format!("getsockopt: option {option:#x} is unsupported for level IPPROTO_IP",);
1267    }
1268
1269    /// Get the `option` socket option on level IPPROTO_TCP for `socket`.
1270    /// `socket` is a [`socket2::SockRef`] to the underlying host socket of
1271    /// the shimmed socket.
1272    fn get_tcp_socket_option(
1273        &mut self,
1274        option: i32,
1275        socket: socket2::SockRef<'_>,
1276    ) -> InterpResult<'tcx, Result<MPlaceTy<'tcx>, IoError>> {
1277        let ecx = self.eval_context_mut();
1278
1279        let opt_tcp_nodelay = ecx.eval_libc_i32("TCP_NODELAY");
1280
1281        if option == opt_tcp_nodelay {
1282            let nodelay = match socket.tcp_nodelay() {
1283                Ok(nodelay) => nodelay,
1284                Err(e) => return interp_ok(Err(IoError::HostError(e))),
1285            };
1286
1287            // Allocate new buffer on the stack with the `i32` layout.
1288            let value_buffer = ecx.allocate(ecx.machine.layouts.i32, MemoryKind::Stack)?;
1289            ecx.write_int(i32::from(nodelay), &value_buffer)?;
1290            return interp_ok(Ok(value_buffer));
1291        }
1292
1293        throw_unsup_format!("getsockopt: option {option:#x} is unsupported for level IPPROTO_TCP");
1294    }
1295}