Skip to main content

miri/shims/unix/
socket.rs

1use std::cell::{Cell, RefCell};
2use std::io::Read;
3use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
4use std::{io, iter};
5
6use mio::Interest;
7use mio::event::Source;
8use mio::net::{TcpListener, TcpStream};
9use rand::Rng;
10use rustc_abi::Size;
11use rustc_const_eval::interpret::{InterpResult, interp_ok};
12use rustc_middle::throw_unsup_format;
13use rustc_target::spec::Os;
14
15use crate::shims::files::{EvalContextExt as _, FdId, FileDescription, FileDescriptionRef};
16use crate::{OpTy, Scalar, *};
17
18#[derive(Debug, PartialEq)]
19enum SocketFamily {
20    // IPv4 internet protocols
21    IPv4,
22    // IPv6 internet protocols
23    IPv6,
24}
25
26enum SocketIoError {
27    /// The socket is not yet ready. Either EINPROGRESS or ENOTCONNECTED occurred.
28    NotReady,
29    /// Any other kind of I/O error.
30    Other(io::Error),
31}
32
33impl From<io::Error> for SocketIoError {
34    fn from(value: io::Error) -> Self {
35        match value.kind() {
36            io::ErrorKind::InProgress | io::ErrorKind::NotConnected => Self::NotReady,
37            _ => Self::Other(value),
38        }
39    }
40}
41
42#[derive(Debug)]
43enum SocketState {
44    /// No syscall after `socket` has been made.
45    Initial,
46    /// The `bind` syscall has been called on the socket.
47    /// This is only reachable from the [`SocketState::Initial`] state.
48    Bound(SocketAddr),
49    /// The `listen` syscall has been called on the socket.
50    /// This is only reachable from the [`SocketState::Bound`] state.
51    Listening(TcpListener),
52    /// The `connect` syscall has been called and we weren't yet able
53    /// to ensure the connection is established. This is only reachable
54    /// from the [`SocketState::Initial`] state.
55    Connecting(TcpStream),
56    /// The `connect` syscall has been called on the socket and
57    /// we ensured that the connection is established, or
58    /// the socket was created by the `accept` syscall.
59    /// For a socket created using the `connect` syscall, this is
60    /// only reachable from the [`SocketState::Connecting`] state.
61    Connected(TcpStream),
62}
63
64impl SocketState {
65    /// If the socket is currently in [`SocketState::Connecting`], try to ensure
66    /// that the connection is established by first checking that [`TcpStream::take_error`]
67    /// doesn't return an error and then by checking that [`TcpStream::peer_addr`]
68    /// returns the address of the connected peer.
69    ///
70    /// If the connection is established or the socket is in any other state,
71    /// [`Ok`] is returned.
72    ///
73    /// **Important**: On Windows hosts this function can only be used to ensure a socket is connected
74    /// _after_ a [`Interest::WRITABLE`] event was received.
75    pub fn try_set_connected(&mut self) -> Result<(), SocketIoError> {
76        // Further explanation of the limitation on Windows hosts:
77        // Windows treats sockets which are connecting as connected until either the connection timeout hits
78        // or an error occurs. Thus, the [`TcpStream::peer_addr`] method returns [`Ok`] with the provided peer
79        // address even when the connection might not yet be established.
80
81        let SocketState::Connecting(stream) = self else { return Ok(()) };
82
83        if let Ok(Some(e)) = stream.take_error() {
84            // There was an error whilst connecting.
85            let e = SocketIoError::from(e);
86            // We won't get EINPROGRESS or ENOTCONNECTED here
87            // so we need to reset the state.
88            assert!(matches!(e, SocketIoError::Other(_)));
89            // Go back to initial state as the only way of getting into the
90            // `Connecting` state is from the `Initial` state.
91            *self = SocketState::Initial;
92            return Err(e);
93        }
94
95        if let Err(e) = stream.peer_addr() {
96            let e = SocketIoError::from(e);
97            if let SocketIoError::Other(_) = &e {
98                // All other errors are fatal for a socket and thus the state needs to be reset.
99                *self = SocketState::Initial;
100            }
101            return Err(e);
102        };
103
104        // We just read the peer address without an error so we can be
105        // sure that the connection is established.
106
107        // Temporarily use dummy state to take ownership of the stream.
108        let SocketState::Connecting(stream) = std::mem::replace(self, SocketState::Initial) else {
109            // At the start of the function we ensured that we're currently connecting.
110            unreachable!()
111        };
112        *self = SocketState::Connected(stream);
113        Ok(())
114    }
115}
116
117#[derive(Debug)]
118struct Socket {
119    /// Family of the socket, used to ensure socket only binds/connects to address of
120    /// same family.
121    family: SocketFamily,
122    /// Current state of the inner socket.
123    state: RefCell<SocketState>,
124    /// Whether this fd is non-blocking or not.
125    is_non_block: Cell<bool>,
126}
127
128impl FileDescription for Socket {
129    fn name(&self) -> &'static str {
130        "socket"
131    }
132
133    fn destroy<'tcx>(
134        self,
135        _self_id: FdId,
136        communicate_allowed: bool,
137        _ecx: &mut MiriInterpCx<'tcx>,
138    ) -> InterpResult<'tcx, std::io::Result<()>> {
139        assert!(communicate_allowed, "cannot have `Socket` with isolation enabled!");
140
141        interp_ok(Ok(()))
142    }
143
144    fn read<'tcx>(
145        self: FileDescriptionRef<Self>,
146        communicate_allowed: bool,
147        ptr: Pointer,
148        len: usize,
149        ecx: &mut MiriInterpCx<'tcx>,
150        finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
151    ) -> InterpResult<'tcx> {
152        assert!(communicate_allowed, "cannot have `Socket` with isolation enabled!");
153
154        if !matches!(&*self.state.borrow(), SocketState::Connected(_)) {
155            // We can only receive from connected sockets. For all other
156            // states we return a not connected error.
157            return finish.call(ecx, Err(LibcError("ENOTCONN")));
158        }
159
160        // Since `read` is the same as `recv` with no flags, we just treat
161        // the `read` as a `recv` here.
162        ecx.block_for_recv(self, ptr, len, /* should_peek */ false, finish);
163
164        interp_ok(())
165    }
166
167    fn write<'tcx>(
168        self: FileDescriptionRef<Self>,
169        communicate_allowed: bool,
170        ptr: Pointer,
171        len: usize,
172        ecx: &mut MiriInterpCx<'tcx>,
173        finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
174    ) -> InterpResult<'tcx> {
175        assert!(communicate_allowed, "cannot have `Socket` with isolation enabled!");
176
177        if !matches!(&*self.state.borrow(), SocketState::Connected(_)) {
178            // We can only send with connected sockets. For all other
179            // states we return a not connected error.
180            return finish.call(ecx, Err(LibcError("ENOTCONN")));
181        }
182
183        // Since `write` is the same as `send` with no flags, we just treat
184        // the `write` as a `send` here.
185        ecx.block_for_send(self, ptr, len, finish);
186
187        interp_ok(())
188    }
189
190    fn short_fd_operations(&self) -> bool {
191        // Short accesses on TCP sockets are realistic and expected to happen.
192        true
193    }
194
195    fn get_flags<'tcx>(&self, ecx: &mut MiriInterpCx<'tcx>) -> InterpResult<'tcx, Scalar> {
196        let mut flags = ecx.eval_libc_i32("O_RDWR");
197
198        if self.is_non_block.get() {
199            flags |= ecx.eval_libc_i32("O_NONBLOCK");
200        }
201
202        interp_ok(Scalar::from_i32(flags))
203    }
204
205    fn set_flags<'tcx>(
206        &self,
207        mut _flag: i32,
208        _ecx: &mut MiriInterpCx<'tcx>,
209    ) -> InterpResult<'tcx, Scalar> {
210        throw_unsup_format!("fcntl: socket flags aren't supported")
211    }
212}
213
214impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
215pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
216    /// For more information on the arguments see the socket manpage:
217    /// <https://linux.die.net/man/2/socket>
218    fn socket(
219        &mut self,
220        domain: &OpTy<'tcx>,
221        type_: &OpTy<'tcx>,
222        protocol: &OpTy<'tcx>,
223    ) -> InterpResult<'tcx, Scalar> {
224        let this = self.eval_context_mut();
225
226        let domain = this.read_scalar(domain)?.to_i32()?;
227        let mut flags = this.read_scalar(type_)?.to_i32()?;
228        let protocol = this.read_scalar(protocol)?.to_i32()?;
229
230        // Reject if isolation is enabled
231        if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
232            this.reject_in_isolation("`socket`", reject_with)?;
233            return this.set_last_error_and_return_i32(LibcError("EACCES"));
234        }
235
236        let mut is_sock_nonblock = false;
237
238        // Interpret the flag. Every flag we recognize is "subtracted" from `flags`, so
239        // if there is anything left at the end, that's an unsupported flag.
240        if matches!(
241            this.tcx.sess.target.os,
242            Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
243        ) {
244            // SOCK_NONBLOCK and SOCK_CLOEXEC only exist on Linux, Android, FreeBSD,
245            // Solaris, and Illumos targets.
246            let sock_nonblock = this.eval_libc_i32("SOCK_NONBLOCK");
247            let sock_cloexec = this.eval_libc_i32("SOCK_CLOEXEC");
248            if flags & sock_nonblock == sock_nonblock {
249                is_sock_nonblock = true;
250                flags &= !sock_nonblock;
251            }
252            if flags & sock_cloexec == sock_cloexec {
253                // We don't support `exec` so we can ignore this.
254                flags &= !sock_cloexec;
255            }
256        }
257
258        let family = if domain == this.eval_libc_i32("AF_INET") {
259            SocketFamily::IPv4
260        } else if domain == this.eval_libc_i32("AF_INET6") {
261            SocketFamily::IPv6
262        } else {
263            throw_unsup_format!(
264                "socket: domain {:#x} is unsupported, only AF_INET and \
265                AF_INET6 are allowed.",
266                domain
267            );
268        };
269
270        if flags != this.eval_libc_i32("SOCK_STREAM") {
271            throw_unsup_format!(
272                "socket: type {:#x} is unsupported, only SOCK_STREAM, \
273                SOCK_CLOEXEC and SOCK_NONBLOCK are allowed",
274                flags
275            );
276        }
277        if protocol != 0 {
278            throw_unsup_format!(
279                "socket: socket protocol {protocol} is unsupported, \
280                only 0 is allowed"
281            );
282        }
283
284        let fds = &mut this.machine.fds;
285        let fd = fds.new_ref(Socket {
286            family,
287            state: RefCell::new(SocketState::Initial),
288            is_non_block: Cell::new(is_sock_nonblock),
289        });
290
291        interp_ok(Scalar::from_i32(fds.insert(fd)))
292    }
293
294    fn bind(
295        &mut self,
296        socket: &OpTy<'tcx>,
297        address: &OpTy<'tcx>,
298        address_len: &OpTy<'tcx>,
299    ) -> InterpResult<'tcx, Scalar> {
300        let this = self.eval_context_mut();
301
302        let socket = this.read_scalar(socket)?.to_i32()?;
303        let address = match this.socket_address(address, address_len, "bind")? {
304            Ok(addr) => addr,
305            Err(e) => return this.set_last_error_and_return_i32(e),
306        };
307
308        // Get the file handle
309        let Some(fd) = this.machine.fds.get(socket) else {
310            return this.set_last_error_and_return_i32(LibcError("EBADF"));
311        };
312
313        let Some(socket) = fd.downcast::<Socket>() else {
314            // Man page specifies to return ENOTSOCK if `fd` is not a socket.
315            return this.set_last_error_and_return_i32(LibcError("ENOTSOCK"));
316        };
317
318        assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");
319
320        let mut state = socket.state.borrow_mut();
321
322        match *state {
323            SocketState::Initial => {
324                let address_family = match &address {
325                    SocketAddr::V4(_) => SocketFamily::IPv4,
326                    SocketAddr::V6(_) => SocketFamily::IPv6,
327                };
328
329                if socket.family != address_family {
330                    // Attempted to bind an address from a family that doesn't match
331                    // the family of the socket.
332                    let err = if matches!(this.tcx.sess.target.os, Os::Linux | Os::Android) {
333                        // Linux man page states that `EINVAL` is used when there is an address family mismatch.
334                        // See <https://man7.org/linux/man-pages/man2/bind.2.html>
335                        LibcError("EINVAL")
336                    } else {
337                        // POSIX man page states that `EAFNOSUPPORT` should be used when there is an address
338                        // family mismatch.
339                        // See <https://man7.org/linux/man-pages/man3/bind.3p.html>
340                        LibcError("EAFNOSUPPORT")
341                    };
342                    return this.set_last_error_and_return_i32(err);
343                }
344
345                *state = SocketState::Bound(address);
346            }
347            SocketState::Connecting(_) | SocketState::Connected(_) =>
348                throw_unsup_format!(
349                    "bind: socket is already connected and binding a
350                    connected socket is unsupported"
351                ),
352            SocketState::Bound(_) | SocketState::Listening(_) =>
353                throw_unsup_format!(
354                    "bind: socket is already bound and binding a socket \
355                    multiple times is unsupported"
356                ),
357        }
358
359        interp_ok(Scalar::from_i32(0))
360    }
361
362    fn listen(&mut self, socket: &OpTy<'tcx>, backlog: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
363        let this = self.eval_context_mut();
364
365        let socket = this.read_scalar(socket)?.to_i32()?;
366        // Since the backlog value is just a performance hint we can ignore it.
367        let _backlog = this.read_scalar(backlog)?.to_i32()?;
368
369        // Get the file handle
370        let Some(fd) = this.machine.fds.get(socket) else {
371            return this.set_last_error_and_return_i32(LibcError("EBADF"));
372        };
373
374        let Some(socket) = fd.downcast::<Socket>() else {
375            // Man page specifies to return ENOTSOCK if `fd` is not a socket.
376            return this.set_last_error_and_return_i32(LibcError("ENOTSOCK"));
377        };
378
379        assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");
380
381        let mut state = socket.state.borrow_mut();
382
383        match *state {
384            SocketState::Bound(socket_addr) =>
385                match TcpListener::bind(socket_addr) {
386                    Ok(listener) => *state = SocketState::Listening(listener),
387                    Err(e) => return this.set_last_error_and_return_i32(e),
388                },
389            SocketState::Initial => {
390                throw_unsup_format!(
391                    "listen: listening on a socket which isn't bound is unsupported"
392                )
393            }
394            SocketState::Listening(_) => {
395                throw_unsup_format!("listen: listening on a socket multiple times is unsupported")
396            }
397            SocketState::Connecting(_) | SocketState::Connected(_) => {
398                throw_unsup_format!("listen: listening on a connected socket is unsupported")
399            }
400        }
401
402        interp_ok(Scalar::from_i32(0))
403    }
404
405    /// For more information on the arguments see the accept manpage:
406    /// <https://linux.die.net/man/2/accept4>
407    fn accept4(
408        &mut self,
409        socket: &OpTy<'tcx>,
410        address: &OpTy<'tcx>,
411        address_len: &OpTy<'tcx>,
412        flags: Option<&OpTy<'tcx>>,
413        // Location where the output scalar is written to.
414        dest: &MPlaceTy<'tcx>,
415    ) -> InterpResult<'tcx> {
416        let this = self.eval_context_mut();
417
418        let socket = this.read_scalar(socket)?.to_i32()?;
419        let address_ptr = this.read_pointer(address)?;
420        let address_len_ptr = this.read_pointer(address_len)?;
421        let mut flags =
422            if let Some(flags) = flags { this.read_scalar(flags)?.to_i32()? } else { 0 };
423
424        // Get the file handle
425        let Some(fd) = this.machine.fds.get(socket) else {
426            return this.set_last_error_and_return(LibcError("EBADF"), dest);
427        };
428
429        let Some(socket) = fd.downcast::<Socket>() else {
430            // Man page specifies to return ENOTSOCK if `fd` is not a socket.
431            return this.set_last_error_and_return(LibcError("ENOTSOCK"), dest);
432        };
433
434        assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");
435
436        if !matches!(*socket.state.borrow(), SocketState::Listening(_)) {
437            throw_unsup_format!(
438                "accept4: accepting incoming connections is only allowed when socket is listening"
439            )
440        };
441
442        let mut is_client_sock_nonblock = false;
443
444        // Interpret the flag. Every flag we recognize is "subtracted" from `flags`, so
445        // if there is anything left at the end, that's an unsupported flag.
446        if matches!(
447            this.tcx.sess.target.os,
448            Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
449        ) {
450            // SOCK_NONBLOCK and SOCK_CLOEXEC only exist on Linux, Android, FreeBSD,
451            // Solaris, and Illumos targets.
452            let sock_nonblock = this.eval_libc_i32("SOCK_NONBLOCK");
453            let sock_cloexec = this.eval_libc_i32("SOCK_CLOEXEC");
454            if flags & sock_nonblock == sock_nonblock {
455                is_client_sock_nonblock = true;
456                flags &= !sock_nonblock;
457            }
458            if flags & sock_cloexec == sock_cloexec {
459                // We don't support `exec` so we can ignore this.
460                flags &= !sock_cloexec;
461            }
462        }
463
464        if flags != 0 {
465            throw_unsup_format!(
466                "accept4: flag {flags:#x} is unsupported, only SOCK_CLOEXEC \
467                and SOCK_NONBLOCK are allowed",
468            );
469        }
470
471        if socket.is_non_block.get() {
472            throw_unsup_format!("accept4: non-blocking accept is unsupported")
473        }
474
475        // The socket is in blocking mode and thus the accept call should block
476        // until an incoming connection is ready.
477        this.block_for_accept(
478            address_ptr,
479            address_len_ptr,
480            is_client_sock_nonblock,
481            socket,
482            dest.clone(),
483        );
484        interp_ok(())
485    }
486
487    fn connect(
488        &mut self,
489        socket: &OpTy<'tcx>,
490        address: &OpTy<'tcx>,
491        address_len: &OpTy<'tcx>,
492        // Location where the output scalar is written to.
493        dest: &MPlaceTy<'tcx>,
494    ) -> InterpResult<'tcx> {
495        let this = self.eval_context_mut();
496
497        let socket = this.read_scalar(socket)?.to_i32()?;
498        let address = match this.socket_address(address, address_len, "connect")? {
499            Ok(address) => address,
500            Err(e) => return this.set_last_error_and_return(e, dest),
501        };
502
503        // Get the file handle
504        let Some(fd) = this.machine.fds.get(socket) else {
505            return this.set_last_error_and_return(LibcError("EBADF"), dest);
506        };
507
508        let Some(socket) = fd.downcast::<Socket>() else {
509            // Man page specifies to return ENOTSOCK if `fd` is not a socket
510            return this.set_last_error_and_return(LibcError("ENOTSOCK"), dest);
511        };
512
513        assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");
514
515        match &*socket.state.borrow() {
516            SocketState::Initial => { /* fall-through to below */ }
517            // The socket is already in a connecting state.
518            SocketState::Connecting(_) =>
519                return this.set_last_error_and_return(LibcError("EALREADY"), dest),
520            // We don't return EISCONN for already connected sockets, for which we're
521            // sure that the connection is established, since TCP sockets are usually
522            // allowed to be connected multiple times.
523            _ =>
524                throw_unsup_format!(
525                    "connect: connecting is only supported for sockets which are neither \
526                    bound, listening nor already connected"
527                ),
528        }
529
530        // Mio returns a potentially unconnected stream.
531        // We can be ensured that the connection is established when
532        // [`TcpStream::take_err`] and [`TcpStream::peer_addr`] both
533        // don't return errors.
534        // For non-blocking sockets we need to check that for every
535        // [`Interest::WRITEABLE`] event on the stream.
536        match TcpStream::connect(address) {
537            Ok(stream) => *socket.state.borrow_mut() = SocketState::Connecting(stream),
538            Err(e) => return this.set_last_error_and_return(e, dest),
539        };
540
541        if socket.is_non_block.get() {
542            throw_unsup_format!("connect: non-blocking connect is unsupported");
543        }
544
545        // The socket is in blocking mode and thus the connect call should block
546        // until the connection with the server is established.
547        this.block_for_connect(socket, dest.clone());
548        interp_ok(())
549    }
550
551    fn send(
552        &mut self,
553        socket: &OpTy<'tcx>,
554        buffer: &OpTy<'tcx>,
555        length: &OpTy<'tcx>,
556        flags: &OpTy<'tcx>,
557        // Location where the output scalar is written to.
558        dest: &MPlaceTy<'tcx>,
559    ) -> InterpResult<'tcx> {
560        let this = self.eval_context_mut();
561
562        let socket = this.read_scalar(socket)?.to_i32()?;
563        let buffer_ptr = this.read_pointer(buffer)?;
564        let size_layout = this.libc_ty_layout("size_t");
565        let length: usize =
566            this.read_scalar(length)?.to_uint(size_layout.size)?.try_into().unwrap();
567        let mut flags = this.read_scalar(flags)?.to_i32()?;
568
569        // Get the file handle
570        let Some(fd) = this.machine.fds.get(socket) else {
571            return this.set_last_error_and_return(LibcError("EBADF"), dest);
572        };
573
574        let Some(socket) = fd.downcast::<Socket>() else {
575            // Man page specifies to return ENOTSOCK if `fd` is not a socket
576            return this.set_last_error_and_return(LibcError("ENOTSOCK"), dest);
577        };
578
579        if !matches!(&*socket.state.borrow(), SocketState::Connected(_)) {
580            // We can only send with connected sockets. For all other
581            // states we return a not connected error.
582            return this.set_last_error_and_return(LibcError("ENOTCONN"), dest);
583        }
584
585        // Non-deterministically decide to further reduce the length, simulating a partial send.
586        // We avoid reducing the write size to 0: the docs seem to be entirely fine with that,
587        // but the standard library is not (https://github.com/rust-lang/rust/issues/145959).
588        let length = if this.machine.short_fd_operations
589            && length >= 2
590            && this.machine.rng.get_mut().random()
591        {
592            length / 2
593        } else {
594            length
595        };
596
597        // Interpret the flag. Every flag we recognize is "subtracted" from `flags`, so
598        // if there is anything left at the end, that's an unsupported flag.
599        if matches!(
600            this.tcx.sess.target.os,
601            Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
602        ) {
603            // MSG_NOSIGNAL only exists on Linux, Android, FreeBSD,
604            // Solaris, and Illumos targets.
605            let msg_nosignal = this.eval_libc_i32("MSG_NOSIGNAL");
606            if flags & msg_nosignal == msg_nosignal {
607                // This is only needed to ensure that no EPIPE signal is sent when
608                // trying to send into a stream which is no longer connected.
609                // Since we don't support signals, we can ignore this.
610                flags &= !msg_nosignal;
611            }
612        }
613
614        if flags != 0 {
615            throw_unsup_format!(
616                "send: flag {flags:#x} is unsupported, only MSG_NOSIGNAL is allowed",
617            );
618        }
619
620        let dest = dest.clone();
621
622        this.block_for_send(
623            socket,
624            buffer_ptr,
625            length,
626            callback!(@capture<'tcx> {
627                dest: MPlaceTy<'tcx>
628            } |this, result: Result<usize, IoError>| {
629                match result {
630                    Ok(read_size) => {
631                        let read_size: u64 = read_size.try_into().unwrap();
632                        let ssize_layout = this.libc_ty_layout("ssize_t");
633                        this.write_scalar(Scalar::from_int(read_size, ssize_layout.size), &dest)
634                    }
635                    Err(e) => this.set_last_error_and_return(e, &dest)
636                }
637            }),
638        );
639
640        interp_ok(())
641    }
642
643    fn recv(
644        &mut self,
645        socket: &OpTy<'tcx>,
646        buffer: &OpTy<'tcx>,
647        length: &OpTy<'tcx>,
648        flags: &OpTy<'tcx>,
649        // Location where the output scalar is written to.
650        dest: &MPlaceTy<'tcx>,
651    ) -> InterpResult<'tcx> {
652        let this = self.eval_context_mut();
653
654        let socket = this.read_scalar(socket)?.to_i32()?;
655        let buffer_ptr = this.read_pointer(buffer)?;
656        let size_layout = this.libc_ty_layout("size_t");
657        let length: usize =
658            this.read_scalar(length)?.to_uint(size_layout.size)?.try_into().unwrap();
659        let mut flags = this.read_scalar(flags)?.to_i32()?;
660
661        // Get the file handle
662        let Some(fd) = this.machine.fds.get(socket) else {
663            return this.set_last_error_and_return(LibcError("EBADF"), dest);
664        };
665
666        let Some(socket) = fd.downcast::<Socket>() else {
667            // Man page specifies to return ENOTSOCK if `fd` is not a socket
668            return this.set_last_error_and_return(LibcError("ENOTSOCK"), dest);
669        };
670
671        if !matches!(&*socket.state.borrow(), SocketState::Connected(_)) {
672            // We can only receive from connected sockets. For all other
673            // states we return a not connected error.
674            return this.set_last_error_and_return(LibcError("ENOTCONN"), dest);
675        }
676
677        // Non-deterministically decide to further reduce the length, simulating a partial receive.
678        // We don't simulate partial receives for lengths < 2 because the man page states that a
679        // return value of zero can only be returned in some special cases:
680        // "When a stream socket peer has performed an orderly shutdown, the return value will be 0
681        // (the traditional "end-of-file" return). [...] The value 0 may also be returned if the
682        // requested number of bytes to receive from a stream socket was 0."
683        let length = if this.machine.short_fd_operations
684            && length >= 2
685            && this.machine.rng.get_mut().random()
686        {
687            length / 2 // since `length` is at least 2, the result is still at least 1
688        } else {
689            length
690        };
691
692        let mut should_peek = false;
693
694        // Interpret the flag. Every flag we recognize is "subtracted" from `flags`, so
695        // if there is anything left at the end, that's an unsupported flag.
696
697        let msg_peek = this.eval_libc_i32("MSG_PEEK");
698        if flags & msg_peek == msg_peek {
699            should_peek = true;
700            flags &= !msg_peek;
701        }
702
703        if matches!(this.tcx.sess.target.os, Os::Linux | Os::Android | Os::FreeBsd | Os::Illumos) {
704            // MSG_CMSG_CLOEXEC only exists on Linux, Android, FreeBSD,
705            // and Illumos targets.
706            let msg_cmsg_cloexec = this.eval_libc_i32("MSG_CMSG_CLOEXEC");
707            if flags & msg_cmsg_cloexec == msg_cmsg_cloexec {
708                // We don't support `exec` so we can ignore this.
709                flags &= !msg_cmsg_cloexec;
710            }
711        }
712
713        if flags != 0 {
714            throw_unsup_format!(
715                "recv: flag {flags:#x} is unsupported, only MSG_PEEK \
716                and MSG_CMSG_CLOEXEC are allowed",
717            );
718        }
719
720        let dest = dest.clone();
721
722        this.block_for_recv(
723            socket,
724            buffer_ptr,
725            length,
726            should_peek,
727            callback!(@capture<'tcx> {
728                dest: MPlaceTy<'tcx>
729            } |this, result: Result<usize, IoError>| {
730                match result {
731                    Ok(read_size) => {
732                        let read_size: u64 = read_size.try_into().unwrap();
733                        let ssize_layout = this.libc_ty_layout("ssize_t");
734                        this.write_scalar(Scalar::from_int(read_size, ssize_layout.size), &dest)
735                    }
736                    Err(e) => this.set_last_error_and_return(e, &dest)
737                }
738            }),
739        );
740
741        interp_ok(())
742    }
743
744    fn setsockopt(
745        &mut self,
746        socket: &OpTy<'tcx>,
747        level: &OpTy<'tcx>,
748        option_name: &OpTy<'tcx>,
749        option_value: &OpTy<'tcx>,
750        option_len: &OpTy<'tcx>,
751    ) -> InterpResult<'tcx, Scalar> {
752        let this = self.eval_context_mut();
753
754        let socket = this.read_scalar(socket)?.to_i32()?;
755        let level = this.read_scalar(level)?.to_i32()?;
756        let option_name = this.read_scalar(option_name)?.to_i32()?;
757        let socklen_layout = this.libc_ty_layout("socklen_t");
758        let option_len = this.read_scalar(option_len)?.to_int(socklen_layout.size)?;
759
760        // Get the file handle
761        let Some(fd) = this.machine.fds.get(socket) else {
762            return this.set_last_error_and_return_i32(LibcError("EBADF"));
763        };
764
765        let Some(_socket) = fd.downcast::<Socket>() else {
766            // Man page specifies to return ENOTSOCK if `fd` is not a socket.
767            return this.set_last_error_and_return_i32(LibcError("ENOTSOCK"));
768        };
769
770        if level == this.eval_libc_i32("SOL_SOCKET") {
771            let opt_so_reuseaddr = this.eval_libc_i32("SO_REUSEADDR");
772
773            if matches!(this.tcx.sess.target.os, Os::MacOs | Os::FreeBsd | Os::NetBsd) {
774                // SO_NOSIGPIPE only exists on MacOS, FreeBSD, and NetBSD.
775                let opt_so_nosigpipe = this.eval_libc_i32("SO_NOSIGPIPE");
776
777                if option_name == opt_so_nosigpipe {
778                    if option_len != 4 {
779                        // Option value should be C-int which is usually 4 bytes.
780                        return this.set_last_error_and_return_i32(LibcError("EINVAL"));
781                    }
782                    let option_value =
783                        this.deref_pointer_as(option_value, this.machine.layouts.i32)?;
784                    let _val = this.read_scalar(&option_value)?.to_i32()?;
785                    // We entirely ignore this value since we do not support signals anyway.
786
787                    return interp_ok(Scalar::from_i32(0));
788                }
789            }
790
791            if option_name == opt_so_reuseaddr {
792                if option_len != 4 {
793                    // Option value should be C-int which is usually 4 bytes.
794                    return this.set_last_error_and_return_i32(LibcError("EINVAL"));
795                }
796                let option_value = this.deref_pointer_as(option_value, this.machine.layouts.i32)?;
797                let _val = this.read_scalar(&option_value)?.to_i32()?;
798                // We entirely ignore this: std always sets REUSEADDR for us, and in the end it's more of a
799                // hint to bypass some arbitrary timeout anyway.
800                return interp_ok(Scalar::from_i32(0));
801            } else {
802                throw_unsup_format!(
803                    "setsockopt: option {option_name:#x} is unsupported for level SOL_SOCKET",
804                );
805            }
806        }
807
808        throw_unsup_format!(
809            "setsockopt: level {level:#x} is unsupported, only SOL_SOCKET is allowed"
810        );
811    }
812
813    fn getsockname(
814        &mut self,
815        socket: &OpTy<'tcx>,
816        address: &OpTy<'tcx>,
817        address_len: &OpTy<'tcx>,
818    ) -> InterpResult<'tcx, Scalar> {
819        let this = self.eval_context_mut();
820
821        let socket = this.read_scalar(socket)?.to_i32()?;
822        let address_ptr = this.read_pointer(address)?;
823        let address_len_ptr = this.read_pointer(address_len)?;
824
825        // Get the file handle
826        let Some(fd) = this.machine.fds.get(socket) else {
827            return this.set_last_error_and_return_i32(LibcError("EBADF"));
828        };
829
830        let Some(socket) = fd.downcast::<Socket>() else {
831            // Man page specifies to return ENOTSOCK if `fd` is not a socket.
832            return this.set_last_error_and_return_i32(LibcError("ENOTSOCK"));
833        };
834
835        assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");
836
837        let state = socket.state.borrow();
838
839        let address = match &*state {
840            SocketState::Bound(address) => {
841                if address.port() == 0 {
842                    // The socket is bound to a zero-port which means it gets assigned a random
843                    // port. Since we don't yet have an underlying socket, we don't know what this
844                    // random port will be and thus this is unsupported.
845                    throw_unsup_format!(
846                        "getsockname: when the port is 0, getting the socket address before \
847                        calling `listen` or `connect` is unsupported"
848                    )
849                }
850
851                *address
852            }
853            SocketState::Listening(listener) =>
854                match listener.local_addr() {
855                    Ok(address) => address,
856                    Err(e) => return this.set_last_error_and_return_i32(e),
857                },
858            // For non-bound sockets the POSIX manual says the returned address is unspecified.
859            // Often this is 0.0.0.0:0 and thus we set it to this value.
860            _ => SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0)),
861        };
862
863        match this.write_socket_address(&address, address_ptr, address_len_ptr, "getsockname")? {
864            Ok(_) => interp_ok(Scalar::from_i32(0)),
865            Err(e) => this.set_last_error_and_return_i32(e),
866        }
867    }
868
869    fn getpeername(
870        &mut self,
871        socket: &OpTy<'tcx>,
872        address: &OpTy<'tcx>,
873        address_len: &OpTy<'tcx>,
874    ) -> InterpResult<'tcx, Scalar> {
875        let this = self.eval_context_mut();
876
877        let socket = this.read_scalar(socket)?.to_i32()?;
878        let address_ptr = this.read_pointer(address)?;
879        let address_len_ptr = this.read_pointer(address_len)?;
880
881        // Get the file handle
882        let Some(fd) = this.machine.fds.get(socket) else {
883            return this.set_last_error_and_return_i32(LibcError("EBADF"));
884        };
885
886        let Some(socket) = fd.downcast::<Socket>() else {
887            // Man page specifies to return ENOTSOCK if `fd` is not a socket.
888            return this.set_last_error_and_return_i32(LibcError("ENOTSOCK"));
889        };
890
891        assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");
892
893        let state = socket.state.borrow();
894
895        let SocketState::Connected(stream) = &*state else {
896            // We can only read the peer address of connected sockets.
897            return this.set_last_error_and_return_i32(LibcError("ENOTCONN"));
898        };
899
900        let address = match stream.peer_addr() {
901            Ok(address) => address,
902            Err(e) => return this.set_last_error_and_return_i32(e),
903        };
904
905        match this.write_socket_address(&address, address_ptr, address_len_ptr, "getpeername")? {
906            Ok(_) => interp_ok(Scalar::from_i32(0)),
907            Err(e) => this.set_last_error_and_return_i32(e),
908        }
909    }
910}
911
912impl<'tcx> EvalContextPrivExt<'tcx> for crate::MiriInterpCx<'tcx> {}
913trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
914    /// Attempt to turn an address and length operand into a standard library socket address.
915    ///
916    /// Returns an IO error should the address length not match the address family length.
917    fn socket_address(
918        &self,
919        address: &OpTy<'tcx>,
920        address_len: &OpTy<'tcx>,
921        foreign_name: &'static str,
922    ) -> InterpResult<'tcx, Result<SocketAddr, IoError>> {
923        let this = self.eval_context_ref();
924
925        let socklen_layout = this.libc_ty_layout("socklen_t");
926        // We only support address lengths which can be stored in a u64 since the
927        // size of a layout is also stored in a u64.
928        let address_len: u64 =
929            this.read_scalar(address_len)?.to_int(socklen_layout.size)?.try_into().unwrap();
930
931        // Initially, treat address as generic sockaddr just to extract the family field.
932        let sockaddr_layout = this.libc_ty_layout("sockaddr");
933        if address_len < sockaddr_layout.size.bytes() {
934            // Address length should be at least as big as the generic sockaddr
935            return interp_ok(Err(LibcError("EINVAL")));
936        }
937        let address = this.deref_pointer_as(address, sockaddr_layout)?;
938
939        let family_field = this.project_field_named(&address, "sa_family")?;
940        let family_layout = this.libc_ty_layout("sa_family_t");
941        let family = this.read_scalar(&family_field)?.to_int(family_layout.size)?;
942
943        // Depending on the family, decide whether it's IPv4 or IPv6 and use specialized layout
944        // to extract address and port.
945        let socket_addr = if family == this.eval_libc_i32("AF_INET").into() {
946            let sockaddr_in_layout = this.libc_ty_layout("sockaddr_in");
947            if address_len != sockaddr_in_layout.size.bytes() {
948                // Address length should be exactly the length of an IPv4 address.
949                return interp_ok(Err(LibcError("EINVAL")));
950            }
951            let address = address.transmute(sockaddr_in_layout, this)?;
952
953            let port_field = this.project_field_named(&address, "sin_port")?;
954            // Read bytes and treat them as big endian since port is stored in network byte order.
955            let port_bytes: [u8; 2] = this
956                .read_bytes_ptr_strip_provenance(port_field.ptr(), Size::from_bytes(2))?
957                .try_into()
958                .unwrap();
959            let port = u16::from_be_bytes(port_bytes);
960
961            let addr_field = this.project_field_named(&address, "sin_addr")?;
962            let s_addr_field = this.project_field_named(&addr_field, "s_addr")?;
963            // Read bytes and treat them as big endian since address is stored in network byte order.
964            let addr_bytes: [u8; 4] = this
965                .read_bytes_ptr_strip_provenance(s_addr_field.ptr(), Size::from_bytes(4))?
966                .try_into()
967                .unwrap();
968            let addr_bits = u32::from_be_bytes(addr_bytes);
969
970            SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::from_bits(addr_bits), port))
971        } else if family == this.eval_libc_i32("AF_INET6").into() {
972            let sockaddr_in6_layout = this.libc_ty_layout("sockaddr_in6");
973            if address_len != sockaddr_in6_layout.size.bytes() {
974                // Address length should be exactly the length of an IPv6 address.
975                return interp_ok(Err(LibcError("EINVAL")));
976            }
977            // We cannot transmute since the `sockaddr_in6` layout is bigger than the `sockaddr` layout.
978            let address = address.offset(Size::ZERO, sockaddr_in6_layout, this)?;
979
980            let port_field = this.project_field_named(&address, "sin6_port")?;
981            // Read bytes and treat them as big endian since port is stored in network byte order.
982            let port_bytes: [u8; 2] = this
983                .read_bytes_ptr_strip_provenance(port_field.ptr(), Size::from_bytes(2))?
984                .try_into()
985                .unwrap();
986            let port = u16::from_be_bytes(port_bytes);
987
988            let addr_field = this.project_field_named(&address, "sin6_addr")?;
989            let s_addr_field = this
990                .project_field_named(&addr_field, "s6_addr")?
991                .transmute(this.machine.layouts.u128, this)?;
992            // Read bytes and treat them as big endian since address is stored in network byte order.
993            let addr_bytes: [u8; 16] = this
994                .read_bytes_ptr_strip_provenance(s_addr_field.ptr(), Size::from_bytes(16))?
995                .try_into()
996                .unwrap();
997            let addr_bits = u128::from_be_bytes(addr_bytes);
998
999            let flowinfo_field = this.project_field_named(&address, "sin6_flowinfo")?;
1000            // flowinfo doesn't get the big endian treatment as this field is stored in native byte order
1001            // and not in network byte order.
1002            let flowinfo = this.read_scalar(&flowinfo_field)?.to_u32()?;
1003
1004            let scope_id_field = this.project_field_named(&address, "sin6_scope_id")?;
1005            // scope_id doesn't get the big endian treatment as this field is stored in native byte order
1006            // and not in network byte order.
1007            let scope_id = this.read_scalar(&scope_id_field)?.to_u32()?;
1008
1009            SocketAddr::V6(SocketAddrV6::new(
1010                Ipv6Addr::from_bits(addr_bits),
1011                port,
1012                flowinfo,
1013                scope_id,
1014            ))
1015        } else {
1016            // Socket of other types shouldn't be created in a first place and
1017            // thus also no address family of another type should be supported.
1018            throw_unsup_format!(
1019                "{foreign_name}: address family {family:#x} is unsupported, \
1020                only AF_INET and AF_INET6 are allowed"
1021            );
1022        };
1023
1024        interp_ok(Ok(socket_addr))
1025    }
1026
1027    /// Attempt to write a standard library socket address into a pointer.
1028    ///
1029    /// The `address_len_ptr` parameter serves both as input and output parameter.
1030    /// On input, it points to the size of the buffer `address_ptr` points to, and
1031    /// on output it points to the non-truncated size of the written address in the
1032    /// buffer pointed to by `address_ptr`.
1033    ///
1034    /// If the address buffer doesn't fit the whole address, the address is truncated to not
1035    /// overflow the buffer.
1036    fn write_socket_address(
1037        &mut self,
1038        address: &SocketAddr,
1039        address_ptr: Pointer,
1040        address_len_ptr: Pointer,
1041        foreign_name: &'static str,
1042    ) -> InterpResult<'tcx, Result<(), IoError>> {
1043        let this = self.eval_context_mut();
1044
1045        if address_ptr == Pointer::null() || address_len_ptr == Pointer::null() {
1046            // The POSIX man page doesn't account for the cases where the `address_ptr` or
1047            // `address_len_ptr` could be null pointers. Thus, this behavior is undefined!
1048            throw_ub_format!(
1049                "{foreign_name}: writing a socket address but the address or the length pointer is a null pointer"
1050            )
1051        }
1052
1053        let socklen_layout = this.libc_ty_layout("socklen_t");
1054        let address_buffer_len_place = this.ptr_to_mplace(address_len_ptr, socklen_layout);
1055        // We only support buffer lengths which can be stored in a u64 since the
1056        // size of a layout in bytes is also stored in a u64.
1057        let address_buffer_len: u64 = this
1058            .read_scalar(&address_buffer_len_place)?
1059            .to_int(socklen_layout.size)?
1060            .try_into()
1061            .unwrap();
1062
1063        let (address_buffer, address_layout) = match address {
1064            SocketAddr::V4(address) => {
1065                // IPv4 address bytes; already stored in network byte order.
1066                let address_bytes = address.ip().octets();
1067                // Port needs to be manually turned into network byte order.
1068                let port = address.port().to_be();
1069
1070                let sockaddr_in_layout = this.libc_ty_layout("sockaddr_in");
1071                // Allocate new buffer on the stack with the `sockaddr_in` layout.
1072                // We need a temporary buffer as `address_ptr` might not point to a large enough
1073                // buffer, in which case we have to truncate.
1074                let address_buffer = this.allocate(sockaddr_in_layout, MemoryKind::Stack)?;
1075                // Zero the whole buffer as some libc targets have additional fields which we fill
1076                // with zero bytes (just like the standard library does it).
1077                this.write_bytes_ptr(
1078                    address_buffer.ptr(),
1079                    iter::repeat_n(0, address_buffer.layout.size.bytes_usize()),
1080                )?;
1081
1082                let sin_family_field = this.project_field_named(&address_buffer, "sin_family")?;
1083                // We cannot simply write the `AF_INET` scalar into the `sin_family_field` because on most
1084                // systems the field has a layout of 16-bit whilst the scalar has a size of 32-bit.
1085                // Since the `AF_INET` constant is chosen such that it can safely be converted into
1086                // a 16-bit integer, we use the following logic to get a scalar of the right size.
1087                let af_inet = this.eval_libc("AF_INET");
1088                let address_family =
1089                    Scalar::from_int(af_inet.to_int(af_inet.size())?, sin_family_field.layout.size);
1090                this.write_scalar(address_family, &sin_family_field)?;
1091
1092                let sin_port_field = this.project_field_named(&address_buffer, "sin_port")?;
1093                // Write the port in target native endianness bytes as we already converted it
1094                // to big endian above.
1095                this.write_bytes_ptr(sin_port_field.ptr(), port.to_ne_bytes())?;
1096
1097                let sin_addr_field = this.project_field_named(&address_buffer, "sin_addr")?;
1098                let s_addr_field = this.project_field_named(&sin_addr_field, "s_addr")?;
1099                this.write_bytes_ptr(s_addr_field.ptr(), address_bytes)?;
1100
1101                (address_buffer, sockaddr_in_layout)
1102            }
1103            SocketAddr::V6(address) => {
1104                // IPv6 address bytes; already stored in network byte order.
1105                let address_bytes = address.ip().octets();
1106                // Port needs to be manually turned into network byte order.
1107                let port = address.port().to_be();
1108                // Flowinfo is stored in native byte order.
1109                let flowinfo = address.flowinfo();
1110                // Scope id is stored in native byte order.
1111                let scope_id = address.scope_id();
1112
1113                let sockaddr_in6_layout = this.libc_ty_layout("sockaddr_in6");
1114                // Allocate new buffer on the stack with the `sockaddr_in6` layout.
1115                // We need a temporary buffer as `address_ptr` might not point to a large enough
1116                // buffer, in which case we have to truncate.
1117                let address_buffer = this.allocate(sockaddr_in6_layout, MemoryKind::Stack)?;
1118                // Zero the whole buffer as some libc targets have additional fields which we fill
1119                // with zero bytes (just like the standard library does it).
1120                this.write_bytes_ptr(
1121                    address_buffer.ptr(),
1122                    iter::repeat_n(0, address_buffer.layout.size.bytes_usize()),
1123                )?;
1124
1125                let sin6_family_field = this.project_field_named(&address_buffer, "sin6_family")?;
1126                // We cannot simply write the `AF_INET6` scalar into the `sin6_family_field` because on most
1127                // systems the field has a layout of 16-bit whilst the scalar has a size of 32-bit.
1128                // Since the `AF_INET6` constant is chosen such that it can safely be converted into
1129                // a 16-bit integer, we use the following logic to get a scalar of the right size.
1130                let af_inet6 = this.eval_libc("AF_INET6");
1131                let address_family = Scalar::from_int(
1132                    af_inet6.to_int(af_inet6.size())?,
1133                    sin6_family_field.layout.size,
1134                );
1135                this.write_scalar(address_family, &sin6_family_field)?;
1136
1137                let sin6_port_field = this.project_field_named(&address_buffer, "sin6_port")?;
1138                // Write the port in target native endianness bytes as we already converted it
1139                // to big endian above.
1140                this.write_bytes_ptr(sin6_port_field.ptr(), port.to_ne_bytes())?;
1141
1142                let sin6_flowinfo_field =
1143                    this.project_field_named(&address_buffer, "sin6_flowinfo")?;
1144                this.write_scalar(Scalar::from_u32(flowinfo), &sin6_flowinfo_field)?;
1145
1146                let sin6_scope_id_field =
1147                    this.project_field_named(&address_buffer, "sin6_scope_id")?;
1148                this.write_scalar(Scalar::from_u32(scope_id), &sin6_scope_id_field)?;
1149
1150                let sin6_addr_field = this.project_field_named(&address_buffer, "sin6_addr")?;
1151                let s6_addr_field = this.project_field_named(&sin6_addr_field, "s6_addr")?;
1152                this.write_bytes_ptr(s6_addr_field.ptr(), address_bytes)?;
1153
1154                (address_buffer, sockaddr_in6_layout)
1155            }
1156        };
1157
1158        // Copy the truncated address into the pointer pointed to by `address_ptr`.
1159        this.mem_copy(
1160            address_buffer.ptr(),
1161            address_ptr,
1162            // Truncate the address to fit the provided buffer.
1163            address_layout.size.min(Size::from_bytes(address_buffer_len)),
1164            // The buffers are guaranteed to not overlap since the `address_buffer`
1165            // was just newly allocated on the stack.
1166            true,
1167        )?;
1168        // Deallocate the address buffer as it was only needed to construct the address and
1169        // copy it into the buffer pointed to by `address_ptr`.
1170        this.deallocate_ptr(address_buffer.ptr(), None, MemoryKind::Stack)?;
1171        // Size of the non-truncated address.
1172        let address_len = address_layout.size.bytes();
1173
1174        this.write_scalar(
1175            Scalar::from_uint(address_len, socklen_layout.size),
1176            &address_buffer_len_place,
1177        )?;
1178
1179        interp_ok(Ok(()))
1180    }
1181
1182    /// Block the thread until there's an incoming connection or an error occurred.
1183    ///
1184    /// This recursively calls itself should the operation still block for some reason.
1185    fn block_for_accept(
1186        &mut self,
1187        address_ptr: Pointer,
1188        address_len_ptr: Pointer,
1189        is_client_sock_nonblock: bool,
1190        socket: FileDescriptionRef<Socket>,
1191        dest: MPlaceTy<'tcx>,
1192    ) {
1193        let this = self.eval_context_mut();
1194        this.block_thread_for_io(
1195            socket.clone(),
1196            Interest::READABLE,
1197            None,
1198            callback!(@capture<'tcx> {
1199                address_ptr: Pointer,
1200                address_len_ptr: Pointer,
1201                is_client_sock_nonblock: bool,
1202                socket: FileDescriptionRef<Socket>,
1203                dest: MPlaceTy<'tcx>,
1204            } |this, kind: UnblockKind| {
1205                assert_eq!(kind, UnblockKind::Ready);
1206
1207                let state = socket.state.borrow();
1208
1209                let SocketState::Listening(listener) = &*state else {
1210                    // We checked that the socket is in listening state before blocking
1211                    // and since there is no outgoing transition from that state this
1212                    // should be unreachable.
1213                    unreachable!()
1214                };
1215
1216                let (stream, addr) = match listener.accept() {
1217                    Ok(peer) => peer,
1218                    Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
1219                        // We need to block the thread again as it would still block.
1220                        drop(state);
1221                        this.block_for_accept(address_ptr, address_len_ptr, is_client_sock_nonblock, socket, dest);
1222                        return interp_ok(())
1223                    },
1224                    Err(e) => return this.set_last_error_and_return(e, &dest),
1225                };
1226
1227                let family = match addr {
1228                    SocketAddr::V4(_) => SocketFamily::IPv4,
1229                    SocketAddr::V6(_) => SocketFamily::IPv6,
1230                };
1231
1232                if address_ptr != Pointer::null() {
1233                    // We only attempt a write if the address pointer is not a null pointer.
1234                    // If the address pointer is a null pointer the user isn't interested in the
1235                    // address and we don't need to write anything.
1236                    if let Err(e) = this.write_socket_address(&addr, address_ptr, address_len_ptr, "accept4")? {
1237                      return this.set_last_error_and_return(e, &dest);
1238                    };
1239                }
1240
1241                let fd = this.machine.fds.new_ref(Socket {
1242                    family,
1243                    state: RefCell::new(SocketState::Connected(stream)),
1244                    is_non_block: Cell::new(is_client_sock_nonblock),
1245                });
1246                let sockfd = this.machine.fds.insert(fd);
1247                // We need to create the scalar using the destination size since
1248                // `syscall(SYS_accept4, ...)` returns a long which doesn't match
1249                // the int returned from the `accept`/`accept4` syscalls.
1250                // See <https://man7.org/linux/man-pages/man2/syscall.2.html>.
1251                this.write_scalar(Scalar::from_int(sockfd, dest.layout.size), &dest)
1252            }),
1253        );
1254    }
1255
1256    /// Block the thread until the stream is connected or an error occurred.
1257    fn block_for_connect(&mut self, socket: FileDescriptionRef<Socket>, dest: MPlaceTy<'tcx>) {
1258        let this = self.eval_context_mut();
1259        this.block_thread_for_io(
1260            socket.clone(),
1261            Interest::WRITABLE,
1262            None,
1263            callback!(@capture<'tcx> {
1264                socket: FileDescriptionRef<Socket>,
1265                dest: MPlaceTy<'tcx>,
1266            } |this, kind: UnblockKind| {
1267                assert_eq!(kind, UnblockKind::Ready);
1268
1269                let mut state = socket.state.borrow_mut();
1270
1271                // We received a "writable" event so `try_set_connected` is safe to call.
1272                match state.try_set_connected() {
1273                    Ok(_) => this.write_scalar(Scalar::from_i32(0), &dest),
1274                     Err(SocketIoError::NotReady) => {
1275                        // We need to block the thread again as the connection is still not yet ready.
1276                        drop(state);
1277                        this.block_for_connect(socket, dest);
1278                        return interp_ok(())
1279                    },
1280                    Err(SocketIoError::Other(e)) => return this.set_last_error_and_return(e, &dest)
1281                }
1282            }),
1283        );
1284    }
1285
1286    /// Block the thread until we can send bytes into the connected socket
1287    /// or an error occurred.
1288    ///
1289    /// This recursively calls itself should the operation still block for some reason.
1290    fn block_for_send(
1291        &mut self,
1292        socket: FileDescriptionRef<Socket>,
1293        buffer_ptr: Pointer,
1294        length: usize,
1295        finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
1296    ) {
1297        let this = self.eval_context_mut();
1298        this.block_thread_for_io(
1299            socket.clone(),
1300            Interest::WRITABLE,
1301            None,
1302            callback!(@capture<'tcx> {
1303                socket: FileDescriptionRef<Socket>,
1304                buffer_ptr: Pointer,
1305                length: usize,
1306                finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
1307            } |this, kind: UnblockKind| {
1308                assert_eq!(kind, UnblockKind::Ready);
1309
1310                let mut state = socket.state.borrow_mut();
1311                let SocketState::Connected(stream) = &mut*state else {
1312                    // We ensured that the socket is connected before blocking.
1313                    unreachable!()
1314                };
1315
1316                // This is a *non-blocking* write.
1317                let result = this.write_to_host(stream, length, buffer_ptr)?;
1318                match result {
1319                    Err(IoError::HostError(e)) if e.kind() == io::ErrorKind::WouldBlock => {
1320                        // We need to block the thread again as it would still block.
1321                        drop(state);
1322                        this.block_for_send(socket, buffer_ptr, length, finish);
1323                        interp_ok(())
1324                    },
1325                    result => finish.call(this, result)
1326                }
1327            }),
1328        );
1329    }
1330
1331    /// Block the thread until we can receive bytes from the connected socket
1332    /// or an error occurred.
1333    ///
1334    /// This recursively calls itself should the operation still block for some reason.
1335    fn block_for_recv(
1336        &mut self,
1337        socket: FileDescriptionRef<Socket>,
1338        buffer_ptr: Pointer,
1339        length: usize,
1340        should_peek: bool,
1341        finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
1342    ) {
1343        let this = self.eval_context_mut();
1344        this.block_thread_for_io(
1345            socket.clone(),
1346            Interest::READABLE,
1347            None,
1348            callback!(@capture<'tcx> {
1349                socket: FileDescriptionRef<Socket>,
1350                buffer_ptr: Pointer,
1351                length: usize,
1352                should_peek: bool,
1353                finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
1354            } |this, kind: UnblockKind| {
1355                assert_eq!(kind, UnblockKind::Ready);
1356
1357                let mut state = socket.state.borrow_mut();
1358                let SocketState::Connected(stream) = &mut*state else {
1359                    // We ensured that the socket is connected before blocking.
1360                    unreachable!()
1361                };
1362
1363                // This is a *non-blocking* read/peek.
1364                let result = this.read_from_host(|buf| {
1365                    if should_peek {
1366                        stream.peek(buf)
1367                    } else {
1368                        stream.read(buf)
1369                    }
1370                }, length, buffer_ptr)?;
1371                match result {
1372                    Err(IoError::HostError(e)) if e.kind() == io::ErrorKind::WouldBlock => {
1373                        // We need to block the thread again as it would still block.
1374                        drop(state);
1375                        this.block_for_recv(socket, buffer_ptr, length, should_peek, finish);
1376                        interp_ok(())
1377                    },
1378                    result => finish.call(this, result)
1379                }
1380            }),
1381        );
1382    }
1383}
1384
1385impl VisitProvenance for FileDescriptionRef<Socket> {
1386    // A socket doesn't contain any references to machine memory
1387    // and thus we don't need to propagate the visit.
1388    fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {}
1389}
1390
1391impl WithSource for FileDescriptionRef<Socket> {
1392    fn with_source(&self, f: &mut dyn FnMut(&mut dyn Source) -> io::Result<()>) -> io::Result<()> {
1393        let mut state = self.state.borrow_mut();
1394        match &mut *state {
1395            SocketState::Listening(listener) => f(listener),
1396            SocketState::Connecting(stream) | SocketState::Connected(stream) => f(stream),
1397            // We never try adding a socket which is not backed by a real socket to the poll registry.
1398            _ => unreachable!(),
1399        }
1400    }
1401}