Skip to main content

miri/shims/unix/
socket.rs

1use std::cell::{Cell, RefCell};
2use std::io::Read;
3use std::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, SocketAddrV4, SocketAddrV6};
4use std::time::Duration;
5use std::{io, iter};
6
7use mio::Interest;
8use mio::event::Source;
9use mio::net::{TcpListener, TcpStream};
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::concurrency::blocking_io::InterestReceiver;
16use crate::shims::files::{EvalContextExt as _, FdId, FileDescription, FileDescriptionRef};
17use crate::shims::unix::UnixFileDescription;
18use crate::*;
19
20#[derive(Debug, PartialEq)]
21enum SocketFamily {
22    // IPv4 internet protocols
23    IPv4,
24    // IPv6 internet protocols
25    IPv6,
26}
27
28#[derive(Debug)]
29enum SocketState {
30    /// No syscall after `socket` has been made.
31    Initial,
32    /// The `bind` syscall has been called on the socket.
33    /// This is only reachable from the [`SocketState::Initial`] state.
34    Bound(SocketAddr),
35    /// The `listen` syscall has been called on the socket.
36    /// This is only reachable from the [`SocketState::Bound`] state.
37    Listening(TcpListener),
38    /// The `connect` syscall has been called and we weren't yet able
39    /// to ensure the connection is established. This is only reachable
40    /// from the [`SocketState::Initial`] state.
41    Connecting(TcpStream),
42    /// The `connect` syscall has been called on the socket and
43    /// we ensured that the connection is established, or
44    /// the socket was created by the `accept` syscall.
45    /// For a socket created using the `connect` syscall, this is
46    /// only reachable from the [`SocketState::Connecting`] state.
47    Connected(TcpStream),
48}
49
50#[derive(Debug)]
51struct Socket {
52    /// Family of the socket, used to ensure socket only binds/connects to address of
53    /// same family.
54    family: SocketFamily,
55    /// Current state of the inner socket.
56    state: RefCell<SocketState>,
57    /// Whether this fd is non-blocking or not.
58    is_non_block: Cell<bool>,
59}
60
61impl FileDescription for Socket {
62    fn name(&self) -> &'static str {
63        "socket"
64    }
65
66    fn destroy<'tcx>(
67        self,
68        _self_id: FdId,
69        communicate_allowed: bool,
70        _ecx: &mut MiriInterpCx<'tcx>,
71    ) -> InterpResult<'tcx, std::io::Result<()>> {
72        assert!(communicate_allowed, "cannot have `Socket` with isolation enabled!");
73
74        interp_ok(Ok(()))
75    }
76
77    fn read<'tcx>(
78        self: FileDescriptionRef<Self>,
79        communicate_allowed: bool,
80        ptr: Pointer,
81        len: usize,
82        ecx: &mut MiriInterpCx<'tcx>,
83        finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
84    ) -> InterpResult<'tcx> {
85        assert!(communicate_allowed, "cannot have `Socket` with isolation enabled!");
86
87        let socket = self;
88
89        ecx.ensure_connected(
90            socket.clone(),
91            !socket.is_non_block.get(),
92            "read",
93            callback!(
94                @capture<'tcx> {
95                    socket: FileDescriptionRef<Socket>,
96                    ptr: Pointer,
97                    len: usize,
98                    finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
99                } |this, result: Result<(), ()>| {
100                    if result.is_err() {
101                        return finish.call(this, Err(LibcError("ENOTCONN")))
102                    }
103
104                    // Since `read` is the same as `recv` with no flags, we just treat
105                    // the `read` as a `recv` here.
106
107                    if socket.is_non_block.get() {
108                        // We have a non-blocking socket and thus don't want to block until
109                        // we can read.
110                        let result = this.try_non_block_recv(&socket, ptr, len, /* should_peek */ false)?;
111                        finish.call(this, result)
112                    } else {
113                        // The socket is in blocking mode and thus the read call should block
114                        // until we can read some bytes from the socket.
115                        this.block_for_recv(socket, ptr, len, /* should_peek */ false, finish);
116                        interp_ok(())
117                    }
118                }
119            ),
120        )
121    }
122
123    fn write<'tcx>(
124        self: FileDescriptionRef<Self>,
125        communicate_allowed: bool,
126        ptr: Pointer,
127        len: usize,
128        ecx: &mut MiriInterpCx<'tcx>,
129        finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
130    ) -> InterpResult<'tcx> {
131        assert!(communicate_allowed, "cannot have `Socket` with isolation enabled!");
132
133        let socket = self;
134
135        ecx.ensure_connected(
136            socket.clone(),
137            !socket.is_non_block.get(),
138            "write",
139            callback!(
140                @capture<'tcx> {
141                    socket: FileDescriptionRef<Socket>,
142                    ptr: Pointer,
143                    len: usize,
144                    finish: DynMachineCallback<'tcx, Result<usize, IoError>>
145                } |this, result: Result<(), ()>| {
146                    if result.is_err() {
147                        return finish.call(this, Err(LibcError("ENOTCONN")))
148                    }
149
150                    // Since `write` is the same as `send` with no flags, we just treat
151                    // the `write` as a `send` here.
152
153                    if socket.is_non_block.get() {
154                        // We have a non-blocking socket and thus don't want to block until
155                        // we can write.
156                        let result = this.try_non_block_send(&socket, ptr, len)?;
157                        return finish.call(this, result)
158                    } else {
159                        // The socket is in blocking mode and thus the write call should block
160                        // until we can write some bytes into the socket.
161                        this.block_for_send(socket, ptr, len, finish);
162                        interp_ok(())
163                    }
164                }
165            ),
166        )
167    }
168
169    fn short_fd_operations(&self) -> bool {
170        // Linux de-facto guarantees (or at least, applications like tokio assume [1, 2]) that
171        // when a read/write on a streaming socket comes back short, the kernel buffer is
172        // empty/full. SO we can't do short reads/writes here.
173        //
174        // [1]: https://github.com/tokio-rs/tokio/blob/6c03e03898d71eca976ee1ad8481cf112ae722ba/tokio/src/io/poll_evented.rs#L182
175        // [2]: https://github.com/tokio-rs/tokio/blob/6c03e03898d71eca976ee1ad8481cf112ae722ba/tokio/src/io/poll_evented.rs#L240
176        false
177    }
178
179    fn as_unix<'tcx>(&self, _ecx: &MiriInterpCx<'tcx>) -> &dyn UnixFileDescription {
180        self
181    }
182
183    fn get_flags<'tcx>(&self, ecx: &mut MiriInterpCx<'tcx>) -> InterpResult<'tcx, Scalar> {
184        let mut flags = ecx.eval_libc_i32("O_RDWR");
185
186        if self.is_non_block.get() {
187            flags |= ecx.eval_libc_i32("O_NONBLOCK");
188        }
189
190        interp_ok(Scalar::from_i32(flags))
191    }
192
193    fn set_flags<'tcx>(
194        &self,
195        mut flag: i32,
196        ecx: &mut MiriInterpCx<'tcx>,
197    ) -> InterpResult<'tcx, Scalar> {
198        let o_nonblock = ecx.eval_libc_i32("O_NONBLOCK");
199
200        // O_NONBLOCK flag can be set / unset by user.
201        if flag & o_nonblock == o_nonblock {
202            self.is_non_block.set(true);
203            flag &= !o_nonblock;
204        } else {
205            self.is_non_block.set(false);
206        }
207
208        // Throw error if there is any unsupported flag.
209        if flag != 0 {
210            throw_unsup_format!("fcntl: only O_NONBLOCK is supported for sockets")
211        }
212
213        interp_ok(Scalar::from_i32(0))
214    }
215}
216
217impl UnixFileDescription for Socket {
218    fn ioctl<'tcx>(
219        &self,
220        op: Scalar,
221        arg: Option<&OpTy<'tcx>>,
222        ecx: &mut MiriInterpCx<'tcx>,
223    ) -> InterpResult<'tcx, i32> {
224        assert!(ecx.machine.communicate(), "cannot have `Socket` with isolation enabled!");
225
226        let fionbio = ecx.eval_libc("FIONBIO");
227
228        if op == fionbio {
229            // On these OSes, Rust uses the ioctl, so we trust that it is reasonable and controls
230            // the same internal flag as fcntl.
231            if !matches!(ecx.tcx.sess.target.os, Os::Linux | Os::Android | Os::MacOs | Os::FreeBsd)
232            {
233                // FIONBIO cannot be used to change the blocking mode of a socket on solarish targets:
234                // <https://github.com/rust-lang/rust/commit/dda5c97675b4f5b1f6fdab64606c8a1f21021b0a>
235                // Since there might be more targets which do weird things with this option, we use
236                // an allowlist instead of just denying solarish targets.
237                throw_unsup_format!(
238                    "ioctl: setting FIONBIO on sockets is unsupported on target {}",
239                    ecx.tcx.sess.target.os
240                );
241            }
242
243            let Some(value_ptr) = arg else {
244                throw_ub_format!("ioctl: setting FIONBIO on sockets requires a third argument");
245            };
246            let value = ecx.deref_pointer_as(value_ptr, ecx.machine.layouts.i32)?;
247            let non_block = ecx.read_scalar(&value)?.to_i32()? != 0;
248            self.is_non_block.set(non_block);
249            return interp_ok(0);
250        }
251
252        throw_unsup_format!("ioctl: unsupported operation {op:#x} on socket");
253    }
254}
255
256impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
257pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
258    /// For more information on the arguments see the socket manpage:
259    /// <https://linux.die.net/man/2/socket>
260    fn socket(
261        &mut self,
262        domain: &OpTy<'tcx>,
263        type_: &OpTy<'tcx>,
264        protocol: &OpTy<'tcx>,
265    ) -> InterpResult<'tcx, Scalar> {
266        let this = self.eval_context_mut();
267
268        let domain = this.read_scalar(domain)?.to_i32()?;
269        let mut flags = this.read_scalar(type_)?.to_i32()?;
270        let protocol = this.read_scalar(protocol)?.to_i32()?;
271
272        // Reject if isolation is enabled
273        if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
274            this.reject_in_isolation("`socket`", reject_with)?;
275            return this.set_last_error_and_return_i32(LibcError("EACCES"));
276        }
277
278        let mut is_sock_nonblock = false;
279
280        // Interpret the flag. Every flag we recognize is "subtracted" from `flags`, so
281        // if there is anything left at the end, that's an unsupported flag.
282        if matches!(
283            this.tcx.sess.target.os,
284            Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
285        ) {
286            // SOCK_NONBLOCK and SOCK_CLOEXEC only exist on Linux, Android, FreeBSD,
287            // Solaris, and Illumos targets.
288            let sock_nonblock = this.eval_libc_i32("SOCK_NONBLOCK");
289            let sock_cloexec = this.eval_libc_i32("SOCK_CLOEXEC");
290            if flags & sock_nonblock == sock_nonblock {
291                is_sock_nonblock = true;
292                flags &= !sock_nonblock;
293            }
294            if flags & sock_cloexec == sock_cloexec {
295                // We don't support `exec` so we can ignore this.
296                flags &= !sock_cloexec;
297            }
298        }
299
300        let family = if domain == this.eval_libc_i32("AF_INET") {
301            SocketFamily::IPv4
302        } else if domain == this.eval_libc_i32("AF_INET6") {
303            SocketFamily::IPv6
304        } else {
305            throw_unsup_format!(
306                "socket: domain {:#x} is unsupported, only AF_INET and \
307                AF_INET6 are allowed.",
308                domain
309            );
310        };
311
312        if flags != this.eval_libc_i32("SOCK_STREAM") {
313            throw_unsup_format!(
314                "socket: type {:#x} is unsupported, only SOCK_STREAM, \
315                SOCK_CLOEXEC and SOCK_NONBLOCK are allowed",
316                flags
317            );
318        }
319        if protocol != 0 && protocol != this.eval_libc_i32("IPPROTO_TCP") {
320            throw_unsup_format!(
321                "socket: socket protocol {protocol} is unsupported, \
322                only IPPROTO_TCP and 0 are allowed"
323            );
324        }
325
326        let fds = &mut this.machine.fds;
327        let fd = fds.new_ref(Socket {
328            family,
329            state: RefCell::new(SocketState::Initial),
330            is_non_block: Cell::new(is_sock_nonblock),
331        });
332
333        interp_ok(Scalar::from_i32(fds.insert(fd)))
334    }
335
336    fn bind(
337        &mut self,
338        socket: &OpTy<'tcx>,
339        address: &OpTy<'tcx>,
340        address_len: &OpTy<'tcx>,
341    ) -> InterpResult<'tcx, Scalar> {
342        let this = self.eval_context_mut();
343
344        let socket = this.read_scalar(socket)?.to_i32()?;
345        let address = match this.socket_address(address, address_len, "bind")? {
346            Ok(addr) => addr,
347            Err(e) => return this.set_last_error_and_return_i32(e),
348        };
349
350        // Get the file handle
351        let Some(fd) = this.machine.fds.get(socket) else {
352            return this.set_last_error_and_return_i32(LibcError("EBADF"));
353        };
354
355        let Some(socket) = fd.downcast::<Socket>() else {
356            // Man page specifies to return ENOTSOCK if `fd` is not a socket.
357            return this.set_last_error_and_return_i32(LibcError("ENOTSOCK"));
358        };
359
360        assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");
361
362        let mut state = socket.state.borrow_mut();
363
364        match *state {
365            SocketState::Initial => {
366                let address_family = match &address {
367                    SocketAddr::V4(_) => SocketFamily::IPv4,
368                    SocketAddr::V6(_) => SocketFamily::IPv6,
369                };
370
371                if socket.family != address_family {
372                    // Attempted to bind an address from a family that doesn't match
373                    // the family of the socket.
374                    let err = if matches!(this.tcx.sess.target.os, Os::Linux | Os::Android) {
375                        // Linux man page states that `EINVAL` is used when there is an address family mismatch.
376                        // See <https://man7.org/linux/man-pages/man2/bind.2.html>
377                        LibcError("EINVAL")
378                    } else {
379                        // POSIX man page states that `EAFNOSUPPORT` should be used when there is an address
380                        // family mismatch.
381                        // See <https://man7.org/linux/man-pages/man3/bind.3p.html>
382                        LibcError("EAFNOSUPPORT")
383                    };
384                    return this.set_last_error_and_return_i32(err);
385                }
386
387                *state = SocketState::Bound(address);
388            }
389            SocketState::Connecting(_) | SocketState::Connected(_) =>
390                throw_unsup_format!(
391                    "bind: socket is already connected and binding a
392                    connected socket is unsupported"
393                ),
394            SocketState::Bound(_) | SocketState::Listening(_) =>
395                throw_unsup_format!(
396                    "bind: socket is already bound and binding a socket \
397                    multiple times is unsupported"
398                ),
399        }
400
401        interp_ok(Scalar::from_i32(0))
402    }
403
404    fn listen(&mut self, socket: &OpTy<'tcx>, backlog: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
405        let this = self.eval_context_mut();
406
407        let socket = this.read_scalar(socket)?.to_i32()?;
408        // Since the backlog value is just a performance hint we can ignore it.
409        let _backlog = this.read_scalar(backlog)?.to_i32()?;
410
411        // Get the file handle
412        let Some(fd) = this.machine.fds.get(socket) else {
413            return this.set_last_error_and_return_i32(LibcError("EBADF"));
414        };
415
416        let Some(socket) = fd.downcast::<Socket>() else {
417            // Man page specifies to return ENOTSOCK if `fd` is not a socket.
418            return this.set_last_error_and_return_i32(LibcError("ENOTSOCK"));
419        };
420
421        assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");
422
423        let mut state = socket.state.borrow_mut();
424
425        match *state {
426            SocketState::Bound(socket_addr) =>
427                match TcpListener::bind(socket_addr) {
428                    Ok(listener) => *state = SocketState::Listening(listener),
429                    Err(e) => return this.set_last_error_and_return_i32(e),
430                },
431            SocketState::Initial => {
432                throw_unsup_format!(
433                    "listen: listening on a socket which isn't bound is unsupported"
434                )
435            }
436            SocketState::Listening(_) => {
437                throw_unsup_format!("listen: listening on a socket multiple times is unsupported")
438            }
439            SocketState::Connecting(_) | SocketState::Connected(_) => {
440                throw_unsup_format!("listen: listening on a connected socket is unsupported")
441            }
442        }
443
444        interp_ok(Scalar::from_i32(0))
445    }
446
447    /// For more information on the arguments see the accept manpage:
448    /// <https://linux.die.net/man/2/accept4>
449    fn accept4(
450        &mut self,
451        socket: &OpTy<'tcx>,
452        address: &OpTy<'tcx>,
453        address_len: &OpTy<'tcx>,
454        flags: Option<&OpTy<'tcx>>,
455        // Location where the output scalar is written to.
456        dest: &MPlaceTy<'tcx>,
457    ) -> InterpResult<'tcx> {
458        let this = self.eval_context_mut();
459
460        let socket = this.read_scalar(socket)?.to_i32()?;
461        let address_ptr = this.read_pointer(address)?;
462        let address_len_ptr = this.read_pointer(address_len)?;
463        let mut flags =
464            if let Some(flags) = flags { this.read_scalar(flags)?.to_i32()? } else { 0 };
465
466        // Get the file handle
467        let Some(fd) = this.machine.fds.get(socket) else {
468            return this.set_last_error_and_return(LibcError("EBADF"), dest);
469        };
470
471        let Some(socket) = fd.downcast::<Socket>() else {
472            // Man page specifies to return ENOTSOCK if `fd` is not a socket.
473            return this.set_last_error_and_return(LibcError("ENOTSOCK"), dest);
474        };
475
476        assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");
477
478        if !matches!(*socket.state.borrow(), SocketState::Listening(_)) {
479            throw_unsup_format!(
480                "accept4: accepting incoming connections is only allowed when socket is listening"
481            )
482        };
483
484        let mut is_client_sock_nonblock = false;
485
486        // Interpret the flag. Every flag we recognize is "subtracted" from `flags`, so
487        // if there is anything left at the end, that's an unsupported flag.
488        if matches!(
489            this.tcx.sess.target.os,
490            Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
491        ) {
492            // SOCK_NONBLOCK and SOCK_CLOEXEC only exist on Linux, Android, FreeBSD,
493            // Solaris, and Illumos targets.
494            let sock_nonblock = this.eval_libc_i32("SOCK_NONBLOCK");
495            let sock_cloexec = this.eval_libc_i32("SOCK_CLOEXEC");
496            if flags & sock_nonblock == sock_nonblock {
497                is_client_sock_nonblock = true;
498                flags &= !sock_nonblock;
499            }
500            if flags & sock_cloexec == sock_cloexec {
501                // We don't support `exec` so we can ignore this.
502                flags &= !sock_cloexec;
503            }
504        }
505
506        if flags != 0 {
507            throw_unsup_format!(
508                "accept4: flag {flags:#x} is unsupported, only SOCK_CLOEXEC \
509                and SOCK_NONBLOCK are allowed",
510            );
511        }
512
513        if socket.is_non_block.get() {
514            // We have a non-blocking socket and thus don't want to block until
515            // we can accept an incoming connection.
516            match this.try_non_block_accept(
517                &socket,
518                address_ptr,
519                address_len_ptr,
520                is_client_sock_nonblock,
521            )? {
522                Ok(sockfd) => {
523                    // We need to create the scalar using the destination size since
524                    // `syscall(SYS_accept4, ...)` returns a long which doesn't match
525                    // the int returned from the `accept`/`accept4` syscalls.
526                    // See <https://man7.org/linux/man-pages/man2/syscall.2.html>.
527                    this.write_scalar(Scalar::from_int(sockfd, dest.layout.size), dest)
528                }
529                Err(e) => this.set_last_error_and_return(e, dest),
530            }
531        } else {
532            // The socket is in blocking mode and thus the accept call should block
533            // until an incoming connection is ready.
534            this.block_for_accept(
535                socket,
536                address_ptr,
537                address_len_ptr,
538                is_client_sock_nonblock,
539                dest.clone(),
540            );
541            interp_ok(())
542        }
543    }
544
545    fn connect(
546        &mut self,
547        socket: &OpTy<'tcx>,
548        address: &OpTy<'tcx>,
549        address_len: &OpTy<'tcx>,
550        // Location where the output scalar is written to.
551        dest: &MPlaceTy<'tcx>,
552    ) -> InterpResult<'tcx> {
553        let this = self.eval_context_mut();
554
555        let socket = this.read_scalar(socket)?.to_i32()?;
556        let address = match this.socket_address(address, address_len, "connect")? {
557            Ok(address) => address,
558            Err(e) => return this.set_last_error_and_return(e, dest),
559        };
560
561        // Get the file handle
562        let Some(fd) = this.machine.fds.get(socket) else {
563            return this.set_last_error_and_return(LibcError("EBADF"), dest);
564        };
565
566        let Some(socket) = fd.downcast::<Socket>() else {
567            // Man page specifies to return ENOTSOCK if `fd` is not a socket
568            return this.set_last_error_and_return(LibcError("ENOTSOCK"), dest);
569        };
570
571        assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");
572
573        match &*socket.state.borrow() {
574            SocketState::Initial => { /* fall-through to below */ }
575            // The socket is already in a connecting state.
576            SocketState::Connecting(_) =>
577                return this.set_last_error_and_return(LibcError("EALREADY"), dest),
578            // We don't return EISCONN for already connected sockets, for which we're
579            // sure that the connection is established, since TCP sockets are usually
580            // allowed to be connected multiple times.
581            _ =>
582                throw_unsup_format!(
583                    "connect: connecting is only supported for sockets which are neither \
584                    bound, listening nor already connected"
585                ),
586        }
587
588        // Mio returns a potentially unconnected stream.
589        // We can be ensured that the connection is established when
590        // [`TcpStream::take_err`] and [`TcpStream::peer_addr`] both
591        // don't return an error after receiving an [`Interest::WRITEABLE`]
592        // event on the stream.
593        match TcpStream::connect(address) {
594            Ok(stream) => *socket.state.borrow_mut() = SocketState::Connecting(stream),
595            Err(e) => return this.set_last_error_and_return(e, dest),
596        };
597
598        if socket.is_non_block.get() {
599            // We have a non-blocking socket and thus don't want to block until
600            // the connection is established.
601
602            // Since the [`TcpStream::connect`] function of mio hides the EINPROGRESS
603            // we just always return EINPROGRESS and check whether the connection succeeded
604            // once we want to use the connected socket.
605            this.set_last_error_and_return(LibcError("EINPROGRESS"), dest)
606        } else {
607            // The socket is in blocking mode and thus the connect call should block
608            // until the connection with the server is established.
609
610            let dest = dest.clone();
611
612            this.ensure_connected(
613                socket,
614                /* should_wait */ true,
615                "connect",
616                callback!(
617                    @capture<'tcx> {
618                        dest: MPlaceTy<'tcx>
619                    } |this, result: Result<(), ()>| {
620                        if result.is_err() {
621                            this.set_last_error_and_return(LibcError("ENOTCONN"), &dest)
622                        } else {
623                            this.write_scalar(Scalar::from_i32(0), &dest)
624                        }
625                    }
626                ),
627            )
628        }
629    }
630
631    fn send(
632        &mut self,
633        socket: &OpTy<'tcx>,
634        buffer: &OpTy<'tcx>,
635        length: &OpTy<'tcx>,
636        flags: &OpTy<'tcx>,
637        // Location where the output scalar is written to.
638        dest: &MPlaceTy<'tcx>,
639    ) -> InterpResult<'tcx> {
640        let this = self.eval_context_mut();
641
642        let socket = this.read_scalar(socket)?.to_i32()?;
643        let buffer_ptr = this.read_pointer(buffer)?;
644        let size_layout = this.libc_ty_layout("size_t");
645        let length: usize =
646            this.read_scalar(length)?.to_uint(size_layout.size)?.try_into().unwrap();
647        let mut flags = this.read_scalar(flags)?.to_i32()?;
648
649        // Get the file handle
650        let Some(fd) = this.machine.fds.get(socket) else {
651            return this.set_last_error_and_return(LibcError("EBADF"), dest);
652        };
653
654        let Some(socket) = fd.downcast::<Socket>() else {
655            // Man page specifies to return ENOTSOCK if `fd` is not a socket
656            return this.set_last_error_and_return(LibcError("ENOTSOCK"), dest);
657        };
658
659        let mut is_op_non_block = false;
660
661        // Interpret the flag. Every flag we recognize is "subtracted" from `flags`, so
662        // if there is anything left at the end, that's an unsupported flag.
663        if matches!(
664            this.tcx.sess.target.os,
665            Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
666        ) {
667            // MSG_NOSIGNAL and MSG_DONTWAIT only exist on Linux, Android, FreeBSD,
668            // Solaris, and Illumos targets.
669            let msg_nosignal = this.eval_libc_i32("MSG_NOSIGNAL");
670            let msg_dontwait = this.eval_libc_i32("MSG_DONTWAIT");
671            if flags & msg_nosignal == msg_nosignal {
672                // This is only needed to ensure that no EPIPE signal is sent when
673                // trying to send into a stream which is no longer connected.
674                // Since we don't support signals, we can ignore this.
675                flags &= !msg_nosignal;
676            }
677            if flags & msg_dontwait == msg_dontwait {
678                flags &= !msg_dontwait;
679                is_op_non_block = true;
680            }
681        }
682
683        if flags != 0 {
684            throw_unsup_format!(
685                "send: flag {flags:#x} is unsupported, only MSG_NOSIGNAL and MSG_DONTWAIT are allowed",
686            );
687        }
688
689        // If either the operation or the socket is non-blocking, we don't want
690        // to wait until the connection is established.
691        let should_wait = !is_op_non_block && !socket.is_non_block.get();
692        let dest = dest.clone();
693
694        this.ensure_connected(
695            socket.clone(),
696            should_wait,
697            "send",
698            callback!(
699                @capture<'tcx> {
700                    socket: FileDescriptionRef<Socket>,
701                    flags: i32,
702                    buffer_ptr: Pointer,
703                    length: usize,
704                    is_op_non_block: bool,
705                    dest: MPlaceTy<'tcx>,
706                } |this, result: Result<(), ()>| {
707                    if result.is_err() {
708                        return this.set_last_error_and_return(LibcError("ENOTCONN"), &dest)
709                    }
710
711                    if is_op_non_block || socket.is_non_block.get() {
712                        // We have a non-blocking operation or a non-blocking socket and
713                        // thus don't want to block until we can send.
714                        match this.try_non_block_send(&socket, buffer_ptr, length)? {
715                            Ok(size) => this.write_scalar(Scalar::from_target_isize(size.try_into().unwrap(), this), &dest),
716                            Err(e) => this.set_last_error_and_return(e, &dest),
717                        }
718                    } else {
719                        // The socket is in blocking mode and thus the send call should block
720                        // until we can send some bytes into the socket.
721                        this.block_for_send(
722                            socket,
723                            buffer_ptr,
724                            length,
725                            callback!(@capture<'tcx> {
726                                dest: MPlaceTy<'tcx>
727                            } |this, result: Result<usize, IoError>| {
728                                match result {
729                                    Ok(size) => this.write_scalar(Scalar::from_target_isize(size.try_into().unwrap(), this), &dest),
730                                    Err(e) => this.set_last_error_and_return(e, &dest)
731                                }
732                            }),
733                        );
734                        interp_ok(())
735                    }
736                }
737            ),
738        )
739    }
740
741    fn recv(
742        &mut self,
743        socket: &OpTy<'tcx>,
744        buffer: &OpTy<'tcx>,
745        length: &OpTy<'tcx>,
746        flags: &OpTy<'tcx>,
747        // Location where the output scalar is written to.
748        dest: &MPlaceTy<'tcx>,
749    ) -> InterpResult<'tcx> {
750        let this = self.eval_context_mut();
751
752        let socket = this.read_scalar(socket)?.to_i32()?;
753        let buffer_ptr = this.read_pointer(buffer)?;
754        let size_layout = this.libc_ty_layout("size_t");
755        let length: usize =
756            this.read_scalar(length)?.to_uint(size_layout.size)?.try_into().unwrap();
757        let mut flags = this.read_scalar(flags)?.to_i32()?;
758
759        // Get the file handle
760        let Some(fd) = this.machine.fds.get(socket) else {
761            return this.set_last_error_and_return(LibcError("EBADF"), dest);
762        };
763
764        let Some(socket) = fd.downcast::<Socket>() else {
765            // Man page specifies to return ENOTSOCK if `fd` is not a socket
766            return this.set_last_error_and_return(LibcError("ENOTSOCK"), dest);
767        };
768
769        let mut should_peek = false;
770        let mut is_op_non_block = false;
771
772        // Interpret the flag. Every flag we recognize is "subtracted" from `flags`, so
773        // if there is anything left at the end, that's an unsupported flag.
774
775        let msg_peek = this.eval_libc_i32("MSG_PEEK");
776        if flags & msg_peek == msg_peek {
777            should_peek = true;
778            flags &= !msg_peek;
779        }
780
781        if matches!(this.tcx.sess.target.os, Os::Linux | Os::Android | Os::FreeBsd | Os::Illumos) {
782            // MSG_CMSG_CLOEXEC only exists on Linux, Android, FreeBSD,
783            // and Illumos targets.
784            let msg_cmsg_cloexec = this.eval_libc_i32("MSG_CMSG_CLOEXEC");
785            if flags & msg_cmsg_cloexec == msg_cmsg_cloexec {
786                // We don't support `exec` so we can ignore this.
787                flags &= !msg_cmsg_cloexec;
788            }
789        }
790
791        if matches!(
792            this.tcx.sess.target.os,
793            Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
794        ) {
795            // MSG_DONTWAIT only exists on Linux, Android, FreeBSD,
796            // Solaris, and Illumos targets.
797            let msg_dontwait = this.eval_libc_i32("MSG_DONTWAIT");
798            if flags & msg_dontwait == msg_dontwait {
799                flags &= !msg_dontwait;
800                is_op_non_block = true;
801            }
802        }
803
804        if flags != 0 {
805            throw_unsup_format!(
806                "recv: flag {flags:#x} is unsupported, only MSG_PEEK, MSG_DONTWAIT \
807                and MSG_CMSG_CLOEXEC are allowed",
808            );
809        }
810
811        // If either the operation or the socket is non-blocking, we don't want
812        // to wait until the connection is established.
813        let should_wait = !is_op_non_block && !socket.is_non_block.get();
814        let dest = dest.clone();
815
816        this.ensure_connected(
817            socket.clone(),
818            should_wait,
819            "recv",
820            callback!(
821                @capture<'tcx> {
822                    socket: FileDescriptionRef<Socket>,
823                    buffer_ptr: Pointer,
824                    length: usize,
825                    should_peek: bool,
826                    is_op_non_block: bool,
827                    dest: MPlaceTy<'tcx>,
828                } |this, result: Result<(), ()>| {
829                    if result.is_err() {
830                        return this.set_last_error_and_return(LibcError("ENOTCONN"), &dest)
831                    }
832
833                    if is_op_non_block || socket.is_non_block.get() {
834                        // We have a non-blocking operation or a non-blocking socket and
835                        // thus don't want to block until we can receive.
836                        match this.try_non_block_recv(&socket, buffer_ptr, length, should_peek)? {
837                            Ok(size) => this.write_scalar(Scalar::from_target_isize(size.try_into().unwrap(), this), &dest),
838                            Err(e) => this.set_last_error_and_return(e, &dest),
839                        }
840                    } else {
841                        // The socket is in blocking mode and thus the receive call should block
842                        // until we can receive some bytes from the socket.
843                        this.block_for_recv(
844                            socket,
845                            buffer_ptr,
846                            length,
847                            should_peek,
848                            callback!(@capture<'tcx> {
849                                dest: MPlaceTy<'tcx>
850                            } |this, result: Result<usize, IoError>| {
851                                match result {
852                                    Ok(size) => this.write_scalar(Scalar::from_target_isize(size.try_into().unwrap(), this), &dest),
853                                    Err(e) => this.set_last_error_and_return(e, &dest)
854                                }
855                            }),
856                        );
857                        interp_ok(())
858                    }
859                }
860            ),
861        )
862    }
863
864    fn setsockopt(
865        &mut self,
866        socket: &OpTy<'tcx>,
867        level: &OpTy<'tcx>,
868        option_name: &OpTy<'tcx>,
869        option_value: &OpTy<'tcx>,
870        option_len: &OpTy<'tcx>,
871    ) -> InterpResult<'tcx, Scalar> {
872        let this = self.eval_context_mut();
873
874        let socket = this.read_scalar(socket)?.to_i32()?;
875        let level = this.read_scalar(level)?.to_i32()?;
876        let option_name = this.read_scalar(option_name)?.to_i32()?;
877        let socklen_layout = this.libc_ty_layout("socklen_t");
878        let option_len = this.read_scalar(option_len)?.to_int(socklen_layout.size)?;
879
880        // Get the file handle
881        let Some(fd) = this.machine.fds.get(socket) else {
882            return this.set_last_error_and_return_i32(LibcError("EBADF"));
883        };
884
885        let Some(_socket) = fd.downcast::<Socket>() else {
886            // Man page specifies to return ENOTSOCK if `fd` is not a socket.
887            return this.set_last_error_and_return_i32(LibcError("ENOTSOCK"));
888        };
889
890        if level == this.eval_libc_i32("SOL_SOCKET") {
891            let opt_so_reuseaddr = this.eval_libc_i32("SO_REUSEADDR");
892
893            if matches!(this.tcx.sess.target.os, Os::MacOs | Os::FreeBsd | Os::NetBsd) {
894                // SO_NOSIGPIPE only exists on MacOS, FreeBSD, and NetBSD.
895                let opt_so_nosigpipe = this.eval_libc_i32("SO_NOSIGPIPE");
896
897                if option_name == opt_so_nosigpipe {
898                    if option_len != 4 {
899                        // Option value should be C-int which is usually 4 bytes.
900                        return this.set_last_error_and_return_i32(LibcError("EINVAL"));
901                    }
902                    let option_value =
903                        this.deref_pointer_as(option_value, this.machine.layouts.i32)?;
904                    let _val = this.read_scalar(&option_value)?.to_i32()?;
905                    // We entirely ignore this value since we do not support signals anyway.
906
907                    return interp_ok(Scalar::from_i32(0));
908                }
909            }
910
911            if option_name == opt_so_reuseaddr {
912                if option_len != 4 {
913                    // Option value should be C-int which is usually 4 bytes.
914                    return this.set_last_error_and_return_i32(LibcError("EINVAL"));
915                }
916                let option_value = this.deref_pointer_as(option_value, this.machine.layouts.i32)?;
917                let _val = this.read_scalar(&option_value)?.to_i32()?;
918                // We entirely ignore this: std always sets REUSEADDR for us, and in the end it's more of a
919                // hint to bypass some arbitrary timeout anyway.
920                return interp_ok(Scalar::from_i32(0));
921            } else {
922                throw_unsup_format!(
923                    "setsockopt: option {option_name:#x} is unsupported for level SOL_SOCKET",
924                );
925            }
926        }
927
928        throw_unsup_format!(
929            "setsockopt: level {level:#x} is unsupported, only SOL_SOCKET is allowed"
930        );
931    }
932
933    fn getsockname(
934        &mut self,
935        socket: &OpTy<'tcx>,
936        address: &OpTy<'tcx>,
937        address_len: &OpTy<'tcx>,
938    ) -> InterpResult<'tcx, Scalar> {
939        let this = self.eval_context_mut();
940
941        let socket = this.read_scalar(socket)?.to_i32()?;
942        let address_ptr = this.read_pointer(address)?;
943        let address_len_ptr = this.read_pointer(address_len)?;
944
945        // Get the file handle
946        let Some(fd) = this.machine.fds.get(socket) else {
947            return this.set_last_error_and_return_i32(LibcError("EBADF"));
948        };
949
950        let Some(socket) = fd.downcast::<Socket>() else {
951            // Man page specifies to return ENOTSOCK if `fd` is not a socket.
952            return this.set_last_error_and_return_i32(LibcError("ENOTSOCK"));
953        };
954
955        assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");
956
957        let state = socket.state.borrow();
958
959        let address = match &*state {
960            SocketState::Bound(address) => {
961                if address.port() == 0 {
962                    // The socket is bound to a zero-port which means it gets assigned a random
963                    // port. Since we don't yet have an underlying socket, we don't know what this
964                    // random port will be and thus this is unsupported.
965                    throw_unsup_format!(
966                        "getsockname: when the port is 0, getting the socket address before \
967                        calling `listen` or `connect` is unsupported"
968                    )
969                }
970
971                *address
972            }
973            SocketState::Listening(listener) =>
974                match listener.local_addr() {
975                    Ok(address) => address,
976                    Err(e) => return this.set_last_error_and_return_i32(e),
977                },
978            // For non-bound sockets the POSIX manual says the returned address is unspecified.
979            // Often this is 0.0.0.0:0 and thus we set it to this value.
980            _ => SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0)),
981        };
982
983        match this.write_socket_address(&address, address_ptr, address_len_ptr, "getsockname")? {
984            Ok(_) => interp_ok(Scalar::from_i32(0)),
985            Err(e) => this.set_last_error_and_return_i32(e),
986        }
987    }
988
989    fn getpeername(
990        &mut self,
991        socket: &OpTy<'tcx>,
992        address: &OpTy<'tcx>,
993        address_len: &OpTy<'tcx>,
994        // Location where the output scalar is written to.
995        dest: &MPlaceTy<'tcx>,
996    ) -> InterpResult<'tcx> {
997        let this = self.eval_context_mut();
998
999        let socket = this.read_scalar(socket)?.to_i32()?;
1000        let address_ptr = this.read_pointer(address)?;
1001        let address_len_ptr = this.read_pointer(address_len)?;
1002
1003        // Get the file handle
1004        let Some(fd) = this.machine.fds.get(socket) else {
1005            return this.set_last_error_and_return(LibcError("EBADF"), dest);
1006        };
1007
1008        let Some(socket) = fd.downcast::<Socket>() else {
1009            // Man page specifies to return ENOTSOCK if `fd` is not a socket.
1010            return this.set_last_error_and_return(LibcError("ENOTSOCK"), dest);
1011        };
1012
1013        assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");
1014
1015        let dest = dest.clone();
1016
1017        // It's only safe to call [`TcpStream::peer_addr`] after the socket is connected since
1018        // UNIX targets should return ENOTCONN when the connection is not yet established.
1019        this.ensure_connected(
1020            socket.clone(),
1021            /* should_wait */ false,
1022            "getpeername",
1023            callback!(
1024                @capture<'tcx> {
1025                    socket: FileDescriptionRef<Socket>,
1026                    address_ptr: Pointer,
1027                    address_len_ptr: Pointer,
1028                    dest: MPlaceTy<'tcx>,
1029                } |this, result: Result<(), ()>| {
1030                    if result.is_err() {
1031                        return this.set_last_error_and_return(LibcError("ENOTCONN"), &dest)
1032                    };
1033
1034                    let SocketState::Connected(stream) = &*socket.state.borrow() else {
1035                        unreachable!()
1036                    };
1037
1038                    let address = match stream.peer_addr() {
1039                        Ok(address) => address,
1040                        Err(e) => return this.set_last_error_and_return(e, &dest),
1041                    };
1042
1043                    match this.write_socket_address(
1044                        &address,
1045                        address_ptr,
1046                        address_len_ptr,
1047                        "getpeername",
1048                    )? {
1049                        Ok(_) => this.write_scalar(Scalar::from_i32(0), &dest),
1050                        Err(e) => this.set_last_error_and_return(e, &dest),
1051                    }
1052                }
1053            ),
1054        )
1055    }
1056
1057    fn shutdown(&mut self, socket: &OpTy<'tcx>, how: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
1058        let this = self.eval_context_mut();
1059
1060        let socket = this.read_scalar(socket)?.to_i32()?;
1061        let how = this.read_scalar(how)?.to_i32()?;
1062
1063        // Get the file handle
1064        let Some(fd) = this.machine.fds.get(socket) else {
1065            return this.set_last_error_and_return_i32(LibcError("EBADF"));
1066        };
1067
1068        let Some(socket) = fd.downcast::<Socket>() else {
1069            // Man page specifies to return ENOTSOCK if `fd` is not a socket.
1070            return this.set_last_error_and_return_i32(LibcError("ENOTSOCK"));
1071        };
1072
1073        assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");
1074
1075        let state = socket.state.borrow();
1076
1077        let (SocketState::Connecting(stream) | SocketState::Connected(stream)) = &*state else {
1078            return this.set_last_error_and_return_i32(LibcError("ENOTCONN"));
1079        };
1080
1081        let shut_rd = this.eval_libc_i32("SHUT_RD");
1082        let shut_wr = this.eval_libc_i32("SHUT_WR");
1083        let shut_rdwr = this.eval_libc_i32("SHUT_RDWR");
1084
1085        let how = match () {
1086            _ if how == shut_rd => Shutdown::Read,
1087            _ if how == shut_wr => Shutdown::Write,
1088            _ if how == shut_rdwr => Shutdown::Both,
1089            // An invalid value was passed to `how`.
1090            _ => return this.set_last_error_and_return_i32(LibcError("EINVAL")),
1091        };
1092
1093        match stream.shutdown(how) {
1094            Ok(_) => interp_ok(Scalar::from_i32(0)),
1095            Err(e) => this.set_last_error_and_return_i32(e),
1096        }
1097    }
1098}
1099
1100impl<'tcx> EvalContextPrivExt<'tcx> for crate::MiriInterpCx<'tcx> {}
1101trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
1102    /// Attempt to turn an address and length operand into a standard library socket address.
1103    ///
1104    /// Returns an IO error should the address length not match the address family length.
1105    fn socket_address(
1106        &self,
1107        address: &OpTy<'tcx>,
1108        address_len: &OpTy<'tcx>,
1109        foreign_name: &'static str,
1110    ) -> InterpResult<'tcx, Result<SocketAddr, IoError>> {
1111        let this = self.eval_context_ref();
1112
1113        let socklen_layout = this.libc_ty_layout("socklen_t");
1114        // We only support address lengths which can be stored in a u64 since the
1115        // size of a layout is also stored in a u64.
1116        let address_len: u64 =
1117            this.read_scalar(address_len)?.to_int(socklen_layout.size)?.try_into().unwrap();
1118
1119        // Initially, treat address as generic sockaddr just to extract the family field.
1120        let sockaddr_layout = this.libc_ty_layout("sockaddr");
1121        if address_len < sockaddr_layout.size.bytes() {
1122            // Address length should be at least as big as the generic sockaddr
1123            return interp_ok(Err(LibcError("EINVAL")));
1124        }
1125        let address = this.deref_pointer_as(address, sockaddr_layout)?;
1126
1127        let family_field = this.project_field_named(&address, "sa_family")?;
1128        let family_layout = this.libc_ty_layout("sa_family_t");
1129        let family = this.read_scalar(&family_field)?.to_int(family_layout.size)?;
1130
1131        // Depending on the family, decide whether it's IPv4 or IPv6 and use specialized layout
1132        // to extract address and port.
1133        let socket_addr = if family == this.eval_libc_i32("AF_INET").into() {
1134            let sockaddr_in_layout = this.libc_ty_layout("sockaddr_in");
1135            if address_len != sockaddr_in_layout.size.bytes() {
1136                // Address length should be exactly the length of an IPv4 address.
1137                return interp_ok(Err(LibcError("EINVAL")));
1138            }
1139            let address = address.transmute(sockaddr_in_layout, this)?;
1140
1141            let port_field = this.project_field_named(&address, "sin_port")?;
1142            // Read bytes and treat them as big endian since port is stored in network byte order.
1143            let port_bytes: [u8; 2] = this
1144                .read_bytes_ptr_strip_provenance(port_field.ptr(), Size::from_bytes(2))?
1145                .try_into()
1146                .unwrap();
1147            let port = u16::from_be_bytes(port_bytes);
1148
1149            let addr_field = this.project_field_named(&address, "sin_addr")?;
1150            let s_addr_field = this.project_field_named(&addr_field, "s_addr")?;
1151            // Read bytes and treat them as big endian since address is stored in network byte order.
1152            let addr_bytes: [u8; 4] = this
1153                .read_bytes_ptr_strip_provenance(s_addr_field.ptr(), Size::from_bytes(4))?
1154                .try_into()
1155                .unwrap();
1156            let addr_bits = u32::from_be_bytes(addr_bytes);
1157
1158            SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::from_bits(addr_bits), port))
1159        } else if family == this.eval_libc_i32("AF_INET6").into() {
1160            let sockaddr_in6_layout = this.libc_ty_layout("sockaddr_in6");
1161            if address_len != sockaddr_in6_layout.size.bytes() {
1162                // Address length should be exactly the length of an IPv6 address.
1163                return interp_ok(Err(LibcError("EINVAL")));
1164            }
1165            // We cannot transmute since the `sockaddr_in6` layout is bigger than the `sockaddr` layout.
1166            let address = address.offset(Size::ZERO, sockaddr_in6_layout, this)?;
1167
1168            let port_field = this.project_field_named(&address, "sin6_port")?;
1169            // Read bytes and treat them as big endian since port is stored in network byte order.
1170            let port_bytes: [u8; 2] = this
1171                .read_bytes_ptr_strip_provenance(port_field.ptr(), Size::from_bytes(2))?
1172                .try_into()
1173                .unwrap();
1174            let port = u16::from_be_bytes(port_bytes);
1175
1176            let addr_field = this.project_field_named(&address, "sin6_addr")?;
1177            let s_addr_field = this
1178                .project_field_named(&addr_field, "s6_addr")?
1179                .transmute(this.machine.layouts.u128, this)?;
1180            // Read bytes and treat them as big endian since address is stored in network byte order.
1181            let addr_bytes: [u8; 16] = this
1182                .read_bytes_ptr_strip_provenance(s_addr_field.ptr(), Size::from_bytes(16))?
1183                .try_into()
1184                .unwrap();
1185            let addr_bits = u128::from_be_bytes(addr_bytes);
1186
1187            let flowinfo_field = this.project_field_named(&address, "sin6_flowinfo")?;
1188            // flowinfo doesn't get the big endian treatment as this field is stored in native byte order
1189            // and not in network byte order.
1190            let flowinfo = this.read_scalar(&flowinfo_field)?.to_u32()?;
1191
1192            let scope_id_field = this.project_field_named(&address, "sin6_scope_id")?;
1193            // scope_id doesn't get the big endian treatment as this field is stored in native byte order
1194            // and not in network byte order.
1195            let scope_id = this.read_scalar(&scope_id_field)?.to_u32()?;
1196
1197            SocketAddr::V6(SocketAddrV6::new(
1198                Ipv6Addr::from_bits(addr_bits),
1199                port,
1200                flowinfo,
1201                scope_id,
1202            ))
1203        } else {
1204            // Socket of other types shouldn't be created in a first place and
1205            // thus also no address family of another type should be supported.
1206            throw_unsup_format!(
1207                "{foreign_name}: address family {family:#x} is unsupported, \
1208                only AF_INET and AF_INET6 are allowed"
1209            );
1210        };
1211
1212        interp_ok(Ok(socket_addr))
1213    }
1214
1215    /// Attempt to write a standard library socket address into a pointer.
1216    ///
1217    /// The `address_len_ptr` parameter serves both as input and output parameter.
1218    /// On input, it points to the size of the buffer `address_ptr` points to, and
1219    /// on output it points to the non-truncated size of the written address in the
1220    /// buffer pointed to by `address_ptr`.
1221    ///
1222    /// If the address buffer doesn't fit the whole address, the address is truncated to not
1223    /// overflow the buffer.
1224    fn write_socket_address(
1225        &mut self,
1226        address: &SocketAddr,
1227        address_ptr: Pointer,
1228        address_len_ptr: Pointer,
1229        foreign_name: &'static str,
1230    ) -> InterpResult<'tcx, Result<(), IoError>> {
1231        let this = self.eval_context_mut();
1232
1233        if address_ptr == Pointer::null() || address_len_ptr == Pointer::null() {
1234            // The POSIX man page doesn't account for the cases where the `address_ptr` or
1235            // `address_len_ptr` could be null pointers. Thus, this behavior is undefined!
1236            throw_ub_format!(
1237                "{foreign_name}: writing a socket address but the address or the length pointer is a null pointer"
1238            )
1239        }
1240
1241        let socklen_layout = this.libc_ty_layout("socklen_t");
1242        let address_buffer_len_place = this.ptr_to_mplace(address_len_ptr, socklen_layout);
1243        // We only support buffer lengths which can be stored in a u64 since the
1244        // size of a layout in bytes is also stored in a u64.
1245        let address_buffer_len: u64 = this
1246            .read_scalar(&address_buffer_len_place)?
1247            .to_int(socklen_layout.size)?
1248            .try_into()
1249            .unwrap();
1250
1251        let (address_buffer, address_layout) = match address {
1252            SocketAddr::V4(address) => {
1253                // IPv4 address bytes; already stored in network byte order.
1254                let address_bytes = address.ip().octets();
1255                // Port needs to be manually turned into network byte order.
1256                let port = address.port().to_be();
1257
1258                let sockaddr_in_layout = this.libc_ty_layout("sockaddr_in");
1259                // Allocate new buffer on the stack with the `sockaddr_in` layout.
1260                // We need a temporary buffer as `address_ptr` might not point to a large enough
1261                // buffer, in which case we have to truncate.
1262                let address_buffer = this.allocate(sockaddr_in_layout, MemoryKind::Stack)?;
1263                // Zero the whole buffer as some libc targets have additional fields which we fill
1264                // with zero bytes (just like the standard library does it).
1265                this.write_bytes_ptr(
1266                    address_buffer.ptr(),
1267                    iter::repeat_n(0, address_buffer.layout.size.bytes_usize()),
1268                )?;
1269
1270                let sin_family_field = this.project_field_named(&address_buffer, "sin_family")?;
1271                // We cannot simply write the `AF_INET` scalar into the `sin_family_field` because on most
1272                // systems the field has a layout of 16-bit whilst the scalar has a size of 32-bit.
1273                // Since the `AF_INET` constant is chosen such that it can safely be converted into
1274                // a 16-bit integer, we use the following logic to get a scalar of the right size.
1275                let af_inet = this.eval_libc("AF_INET");
1276                let address_family =
1277                    Scalar::from_int(af_inet.to_int(af_inet.size())?, sin_family_field.layout.size);
1278                this.write_scalar(address_family, &sin_family_field)?;
1279
1280                let sin_port_field = this.project_field_named(&address_buffer, "sin_port")?;
1281                // Write the port in target native endianness bytes as we already converted it
1282                // to big endian above.
1283                this.write_bytes_ptr(sin_port_field.ptr(), port.to_ne_bytes())?;
1284
1285                let sin_addr_field = this.project_field_named(&address_buffer, "sin_addr")?;
1286                let s_addr_field = this.project_field_named(&sin_addr_field, "s_addr")?;
1287                this.write_bytes_ptr(s_addr_field.ptr(), address_bytes)?;
1288
1289                (address_buffer, sockaddr_in_layout)
1290            }
1291            SocketAddr::V6(address) => {
1292                // IPv6 address bytes; already stored in network byte order.
1293                let address_bytes = address.ip().octets();
1294                // Port needs to be manually turned into network byte order.
1295                let port = address.port().to_be();
1296                // Flowinfo is stored in native byte order.
1297                let flowinfo = address.flowinfo();
1298                // Scope id is stored in native byte order.
1299                let scope_id = address.scope_id();
1300
1301                let sockaddr_in6_layout = this.libc_ty_layout("sockaddr_in6");
1302                // Allocate new buffer on the stack with the `sockaddr_in6` layout.
1303                // We need a temporary buffer as `address_ptr` might not point to a large enough
1304                // buffer, in which case we have to truncate.
1305                let address_buffer = this.allocate(sockaddr_in6_layout, MemoryKind::Stack)?;
1306                // Zero the whole buffer as some libc targets have additional fields which we fill
1307                // with zero bytes (just like the standard library does it).
1308                this.write_bytes_ptr(
1309                    address_buffer.ptr(),
1310                    iter::repeat_n(0, address_buffer.layout.size.bytes_usize()),
1311                )?;
1312
1313                let sin6_family_field = this.project_field_named(&address_buffer, "sin6_family")?;
1314                // We cannot simply write the `AF_INET6` scalar into the `sin6_family_field` because on most
1315                // systems the field has a layout of 16-bit whilst the scalar has a size of 32-bit.
1316                // Since the `AF_INET6` constant is chosen such that it can safely be converted into
1317                // a 16-bit integer, we use the following logic to get a scalar of the right size.
1318                let af_inet6 = this.eval_libc("AF_INET6");
1319                let address_family = Scalar::from_int(
1320                    af_inet6.to_int(af_inet6.size())?,
1321                    sin6_family_field.layout.size,
1322                );
1323                this.write_scalar(address_family, &sin6_family_field)?;
1324
1325                let sin6_port_field = this.project_field_named(&address_buffer, "sin6_port")?;
1326                // Write the port in target native endianness bytes as we already converted it
1327                // to big endian above.
1328                this.write_bytes_ptr(sin6_port_field.ptr(), port.to_ne_bytes())?;
1329
1330                let sin6_flowinfo_field =
1331                    this.project_field_named(&address_buffer, "sin6_flowinfo")?;
1332                this.write_scalar(Scalar::from_u32(flowinfo), &sin6_flowinfo_field)?;
1333
1334                let sin6_scope_id_field =
1335                    this.project_field_named(&address_buffer, "sin6_scope_id")?;
1336                this.write_scalar(Scalar::from_u32(scope_id), &sin6_scope_id_field)?;
1337
1338                let sin6_addr_field = this.project_field_named(&address_buffer, "sin6_addr")?;
1339                let s6_addr_field = this.project_field_named(&sin6_addr_field, "s6_addr")?;
1340                this.write_bytes_ptr(s6_addr_field.ptr(), address_bytes)?;
1341
1342                (address_buffer, sockaddr_in6_layout)
1343            }
1344        };
1345
1346        // Copy the truncated address into the pointer pointed to by `address_ptr`.
1347        this.mem_copy(
1348            address_buffer.ptr(),
1349            address_ptr,
1350            // Truncate the address to fit the provided buffer.
1351            address_layout.size.min(Size::from_bytes(address_buffer_len)),
1352            // The buffers are guaranteed to not overlap since the `address_buffer`
1353            // was just newly allocated on the stack.
1354            true,
1355        )?;
1356        // Deallocate the address buffer as it was only needed to construct the address and
1357        // copy it into the buffer pointed to by `address_ptr`.
1358        this.deallocate_ptr(address_buffer.ptr(), None, MemoryKind::Stack)?;
1359        // Size of the non-truncated address.
1360        let address_len = address_layout.size.bytes();
1361
1362        this.write_scalar(
1363            Scalar::from_uint(address_len, socklen_layout.size),
1364            &address_buffer_len_place,
1365        )?;
1366
1367        interp_ok(Ok(()))
1368    }
1369
1370    /// Block the thread until there's an incoming connection or an error occurred.
1371    ///
1372    /// This recursively calls itself should the operation still block for some reason.
1373    ///
1374    /// **Note**: This function is only safe to call when having previously ensured
1375    /// that the socket is in [`SocketState::Listening`].
1376    fn block_for_accept(
1377        &mut self,
1378        socket: FileDescriptionRef<Socket>,
1379        address_ptr: Pointer,
1380        address_len_ptr: Pointer,
1381        is_client_sock_nonblock: bool,
1382        dest: MPlaceTy<'tcx>,
1383    ) {
1384        let this = self.eval_context_mut();
1385        this.block_thread_for_io(
1386            socket.clone(),
1387            Interest::READABLE,
1388            None,
1389            callback!(@capture<'tcx> {
1390                address_ptr: Pointer,
1391                address_len_ptr: Pointer,
1392                is_client_sock_nonblock: bool,
1393                socket: FileDescriptionRef<Socket>,
1394                dest: MPlaceTy<'tcx>,
1395            } |this, kind: UnblockKind| {
1396                assert_eq!(kind, UnblockKind::Ready);
1397
1398                match this.try_non_block_accept(&socket, address_ptr, address_len_ptr, is_client_sock_nonblock)? {
1399                    Ok(sockfd) => {
1400                        // We need to create the scalar using the destination size since
1401                        // `syscall(SYS_accept4, ...)` returns a long which doesn't match
1402                        // the int returned from the `accept`/`accept4` syscalls.
1403                        // See <https://man7.org/linux/man-pages/man2/syscall.2.html>.
1404                        this.write_scalar(Scalar::from_int(sockfd, dest.layout.size), &dest)
1405                    },
1406                    Err(IoError::HostError(e)) if e.kind() == io::ErrorKind::WouldBlock => {
1407                        // We need to block the thread again as it would still block.
1408                        this.block_for_accept(socket, address_ptr, address_len_ptr, is_client_sock_nonblock, dest);
1409                        interp_ok(())
1410                    }
1411                    Err(e) => this.set_last_error_and_return(e, &dest),
1412                }
1413            }),
1414        );
1415    }
1416
1417    /// Attempt to accept an incoming connection on the listening socket in a
1418    /// non-blocking manner.
1419    ///
1420    /// **Note**: This function is only safe to call when having previously ensured
1421    /// that the socket is in [`SocketState::Listening`].
1422    fn try_non_block_accept(
1423        &mut self,
1424        socket: &FileDescriptionRef<Socket>,
1425        address_ptr: Pointer,
1426        address_len_ptr: Pointer,
1427        is_client_sock_nonblock: bool,
1428    ) -> InterpResult<'tcx, Result<i32, IoError>> {
1429        let this = self.eval_context_mut();
1430
1431        let state = socket.state.borrow();
1432        let SocketState::Listening(listener) = &*state else {
1433            panic!(
1434                "try_non_block_accept must only be called when socket is in `SocketState::Listening`"
1435            )
1436        };
1437
1438        let (stream, addr) = match listener.accept() {
1439            Ok(peer) => peer,
1440            Err(e) => return interp_ok(Err(IoError::HostError(e))),
1441        };
1442
1443        let family = match addr {
1444            SocketAddr::V4(_) => SocketFamily::IPv4,
1445            SocketAddr::V6(_) => SocketFamily::IPv6,
1446        };
1447
1448        if address_ptr != Pointer::null() {
1449            // We only attempt a write if the address pointer is not a null pointer.
1450            // If the address pointer is a null pointer the user isn't interested in the
1451            // address and we don't need to write anything.
1452            if let Err(e) =
1453                this.write_socket_address(&addr, address_ptr, address_len_ptr, "accept4")?
1454            {
1455                return interp_ok(Err(e));
1456            };
1457        }
1458
1459        let fd = this.machine.fds.new_ref(Socket {
1460            family,
1461            state: RefCell::new(SocketState::Connected(stream)),
1462            is_non_block: Cell::new(is_client_sock_nonblock),
1463        });
1464        let sockfd = this.machine.fds.insert(fd);
1465        interp_ok(Ok(sockfd))
1466    }
1467
1468    /// Block the thread until we can send bytes into the connected socket
1469    /// or an error occurred.
1470    ///
1471    /// This recursively calls itself should the operation still block for some reason.
1472    ///
1473    /// **Note**: This function is only safe to call when having previously ensured
1474    /// that the socket is in [`SocketState::Connected`].
1475    fn block_for_send(
1476        &mut self,
1477        socket: FileDescriptionRef<Socket>,
1478        buffer_ptr: Pointer,
1479        length: usize,
1480        finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
1481    ) {
1482        let this = self.eval_context_mut();
1483        this.block_thread_for_io(
1484            socket.clone(),
1485            Interest::WRITABLE,
1486            None,
1487            callback!(@capture<'tcx> {
1488                socket: FileDescriptionRef<Socket>,
1489                buffer_ptr: Pointer,
1490                length: usize,
1491                finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
1492            } |this, kind: UnblockKind| {
1493                assert_eq!(kind, UnblockKind::Ready);
1494
1495                match this.try_non_block_send(&socket, buffer_ptr, length)? {
1496                    Err(IoError::HostError(e)) if e.kind() == io::ErrorKind::WouldBlock => {
1497                        this.block_for_send(socket, buffer_ptr, length, finish);
1498                        interp_ok(())
1499                    },
1500                    result => finish.call(this, result)
1501                }
1502            }),
1503        );
1504    }
1505
1506    /// Attempt to send bytes into the connected socket in a non-blocking manner.
1507    ///
1508    /// **Note**: This function is only safe to call when having previously ensured
1509    /// that the socket is in [`SocketState::Connected`].
1510    fn try_non_block_send(
1511        &mut self,
1512        socket: &FileDescriptionRef<Socket>,
1513        buffer_ptr: Pointer,
1514        length: usize,
1515    ) -> InterpResult<'tcx, Result<usize, IoError>> {
1516        let this = self.eval_context_mut();
1517
1518        let SocketState::Connected(stream) = &mut *socket.state.borrow_mut() else {
1519            panic!("try_non_block_send must only be called when the socket is connected")
1520        };
1521
1522        // This is a *non-blocking* write.
1523        let result = this.write_to_host(stream, length, buffer_ptr)?;
1524        // FIXME: When the host does a short write, we should emit an epoll edge -- at least for targets for which tokio assumes no short writes:
1525        // <https://github.com/tokio-rs/tokio/blob/6c03e03898d71eca976ee1ad8481cf112ae722ba/tokio/src/io/poll_evented.rs#L240>
1526        match result {
1527            Err(IoError::HostError(e)) if e.kind() == io::ErrorKind::NotConnected => {
1528                // On Windows hosts, `send` can return WSAENOTCONN where EAGAIN or EWOULDBLOCK
1529                // would be returned on UNIX-like systems. We thus remap this error to an EWOULDBLOCK.
1530                interp_ok(Err(IoError::HostError(io::ErrorKind::WouldBlock.into())))
1531            }
1532            Err(IoError::HostError(e))
1533                if cfg!(windows)
1534                    && matches!(e.raw_os_error(), Some(/* WSAESHUTDOWN error code */ 10058)) =>
1535            {
1536                // FIXME: This is a temporary workaround for handling WSAESHUTDOWN errors
1537                // on Windows. A discussion on how those errors should be handled can be found here:
1538                // <https://rust-lang.zulipchat.com/#narrow/channel/219381-t-libs/topic/WSAESHUTDOWN.20error.20on.20Windows/near/591883531>
1539                interp_ok(Err(IoError::HostError(io::ErrorKind::BrokenPipe.into())))
1540            }
1541            result => interp_ok(result),
1542        }
1543    }
1544
1545    /// Block the thread until we can receive bytes from the connected socket
1546    /// or an error occurred.
1547    ///
1548    /// This recursively calls itself should the operation still block for some reason.
1549    ///
1550    /// **Note**: This function is only safe to call when having previously ensured
1551    /// that the socket is in [`SocketState::Connected`].
1552    fn block_for_recv(
1553        &mut self,
1554        socket: FileDescriptionRef<Socket>,
1555        buffer_ptr: Pointer,
1556        length: usize,
1557        should_peek: bool,
1558        finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
1559    ) {
1560        let this = self.eval_context_mut();
1561        this.block_thread_for_io(
1562            socket.clone(),
1563            Interest::READABLE,
1564            None,
1565            callback!(@capture<'tcx> {
1566                socket: FileDescriptionRef<Socket>,
1567                buffer_ptr: Pointer,
1568                length: usize,
1569                should_peek: bool,
1570                finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
1571            } |this, kind: UnblockKind| {
1572                assert_eq!(kind, UnblockKind::Ready);
1573
1574                match this.try_non_block_recv(&socket, buffer_ptr, length, should_peek)? {
1575                    Err(IoError::HostError(e)) if e.kind() == io::ErrorKind::WouldBlock => {
1576                        // We need to block the thread again as it would still block.
1577                        this.block_for_recv(socket, buffer_ptr, length, should_peek, finish);
1578                        interp_ok(())
1579                    },
1580                    result => finish.call(this, result)
1581                }
1582            }),
1583        );
1584    }
1585
1586    /// Attempt to receive bytes from the connected socket in a non-blocking manner.
1587    ///
1588    /// **Note**: This function is only safe to call when having previously ensured
1589    /// that the socket is in [`SocketState::Connected`].
1590    fn try_non_block_recv(
1591        &mut self,
1592        socket: &FileDescriptionRef<Socket>,
1593        buffer_ptr: Pointer,
1594        length: usize,
1595        should_peek: bool,
1596    ) -> InterpResult<'tcx, Result<usize, IoError>> {
1597        let this = self.eval_context_mut();
1598
1599        let SocketState::Connected(stream) = &mut *socket.state.borrow_mut() else {
1600            panic!("try_non_block_recv must only be called when the socket is connected")
1601        };
1602
1603        // This is a *non-blocking* read/peek.
1604        let result = this.read_from_host(
1605            |buf| {
1606                if should_peek { stream.peek(buf) } else { stream.read(buf) }
1607            },
1608            length,
1609            buffer_ptr,
1610        )?;
1611        // FIXME: When the host does a short read, we should emit an epoll edge -- at least for targets for which tokio assumes no short reads:
1612        // <https://github.com/tokio-rs/tokio/blob/6c03e03898d71eca976ee1ad8481cf112ae722ba/tokio/src/io/poll_evented.rs#L182>
1613        match result {
1614            Err(IoError::HostError(e)) if e.kind() == io::ErrorKind::NotConnected => {
1615                // On Windows hosts, `recv` can return WSAENOTCONN where EAGAIN or EWOULDBLOCK
1616                // would be returned on UNIX-like systems. We thus remap this error to an EWOULDBLOCK.
1617                interp_ok(Err(IoError::HostError(io::ErrorKind::WouldBlock.into())))
1618            }
1619            result => interp_ok(result),
1620        }
1621    }
1622
1623    // Execute the provided callback function when the socket is either in
1624    // [`SocketState::Connected`] or an error occurred.
1625    /// If the socket is currently neither in the [`SocketState::Connecting`] nor
1626    /// the [`SocketState::Connecting`] state, an ENOTCONN error is returned.
1627    /// When the callback function is called with `Ok(_)`, then we're guaranteed
1628    /// that the socket is in the [`SocketState::Connected`] state.
1629    ///
1630    /// This function can optionally also block until either an error occurred or
1631    /// the socket reached the [`SocketState::Connected`] state.
1632    fn ensure_connected(
1633        &mut self,
1634        socket: FileDescriptionRef<Socket>,
1635        should_wait: bool,
1636        foreign_name: &'static str,
1637        action: DynMachineCallback<'tcx, Result<(), ()>>,
1638    ) -> InterpResult<'tcx> {
1639        let this = self.eval_context_mut();
1640
1641        let state = socket.state.borrow();
1642        match &*state {
1643            SocketState::Connecting(_) => { /* fall-through to below */ }
1644            SocketState::Connected(_) => {
1645                drop(state);
1646                return action.call(this, Ok(()));
1647            }
1648            _ => {
1649                drop(state);
1650                return action.call(this, Err(()));
1651            }
1652        };
1653
1654        drop(state);
1655
1656        // We're currently connecting. Since the underlying mio socket is non-blocking,
1657        // the only way to determine whether we are done connecting is by polling.
1658        // If we should wait until the connection is established, the timeout is `None`.
1659        // Otherwise, we use a zero duration timeout, i.e. we return immediately
1660        // (but we still go through the scheduler once -- which is fine).
1661        let timeout = if should_wait {
1662            None
1663        } else {
1664            Some((TimeoutClock::Monotonic, TimeoutAnchor::Absolute, Duration::ZERO))
1665        };
1666
1667        this.block_thread_for_io(
1668            socket.clone(),
1669            Interest::WRITABLE,
1670            timeout,
1671            callback!(
1672                @capture<'tcx> {
1673                    socket: FileDescriptionRef<Socket>,
1674                    should_wait: bool,
1675                    foreign_name: &'static str,
1676                    action: DynMachineCallback<'tcx, Result<(), ()>>,
1677                } |this, kind: UnblockKind| {
1678                    if UnblockKind::TimedOut == kind {
1679                        // We can only time out when `should_wait` is false.
1680                        // This then means that the socket is not yet connected.
1681                        assert!(!should_wait);
1682                        this.machine.blocking_io.deregister(socket.id(), InterestReceiver::UnblockThread(this.active_thread()));
1683                        return action.call(this, Err(()))
1684                    }
1685
1686                    // The thread woke up because it's ready, indicating a writeable or error event.
1687
1688                    let mut state = socket.state.borrow_mut();
1689                    let stream = match &*state {
1690                        SocketState::Connecting(stream) => stream,
1691                        SocketState::Connected(_) => {
1692                            drop(state);
1693                            // This can happen because we blocked the thread:
1694                            // maybe another thread "upgraded" the connection in the meantime.
1695                            return action.call(this, Ok(()))
1696                        },
1697                        _ => {
1698                            drop(state);
1699                            // We ensured that we only block when we're currently connecting.
1700                            // Since this thread just got rescheduled, it could be that another
1701                            // thread realized that the connection failed and we're thus in
1702                            // an "invalid state".
1703                            return action.call(this, Err(()))
1704                        }
1705                    };
1706
1707                    // Manually check whether there were any errors since calling `connect`.
1708                    if let Ok(Some(_)) = stream.take_error() {
1709                        // There was an error during connecting and thus we
1710                        // return ENOTCONN. It's the program's responsibility
1711                        // to read SO_ERROR itself.
1712                        //
1713                        // Go back to initial state since the only way of getting into the
1714                        // `Connecting` state is from the `Initial` state and at this point
1715                        // we know that the connection won't be established anymore.
1716                        //
1717                        // FIXME: We're currently just dropping the error information. Eventually
1718                        // we'll have to store it so that it can be recovered by the user.
1719                        *state = SocketState::Initial;
1720                        drop(state);
1721                        return action.call(this, Err(()))
1722                    }
1723
1724                    // There was no error during connecting. We still need to ensure that
1725                    // the wakeup wasn't spurious. We do this by attempting to read the
1726                    // peer address of the socket (following the advice given by mio):
1727                    // <https://docs.rs/mio/latest/mio/net/struct.TcpStream.html#notes>
1728
1729                    match stream.peer_addr() {
1730                        Ok(_) => { /* fall-through to below */},
1731                        Err(e) if matches!(e.kind(), io::ErrorKind::NotConnected | io::ErrorKind::InProgress) => {
1732                            // We received a spurious wakeup from the OS. This should be considered an OS bug:
1733                            // <https://github.com/tokio-rs/mio/issues/1942#issuecomment-4169378308>
1734                            panic!("{foreign_name}: received writable event from OS but socket is not yet connected")
1735                        },
1736                        Err(_) => {
1737                            // For all other errors the socket is connected. Since we're not interested in the
1738                            // peer address and only want to know whether the socket is connected, we can ignore
1739                            // the error and continue.
1740                        }
1741                    }
1742
1743                    // The connection is established.
1744
1745                    // Temporarily use dummy state to take ownership of the stream.
1746                    let SocketState::Connecting(stream) = std::mem::replace(&mut*state, SocketState::Initial) else {
1747                        // At the start of the function we ensured that we're currently connecting.
1748                        unreachable!()
1749                    };
1750                    *state = SocketState::Connected(stream);
1751                    drop(state);
1752                    action.call(this, Ok(()))
1753                }
1754            ),
1755        );
1756
1757        interp_ok(())
1758    }
1759}
1760
1761impl VisitProvenance for FileDescriptionRef<Socket> {
1762    // A socket doesn't contain any references to machine memory
1763    // and thus we don't need to propagate the visit.
1764    fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {}
1765}
1766
1767impl WithSource for Socket {
1768    fn with_source(&self, f: &mut dyn FnMut(&mut dyn Source) -> io::Result<()>) -> io::Result<()> {
1769        let mut state = self.state.borrow_mut();
1770        match &mut *state {
1771            SocketState::Listening(listener) => f(listener),
1772            SocketState::Connecting(stream) | SocketState::Connected(stream) => f(stream),
1773            // We never try adding a socket which is not backed by a real socket to the poll registry.
1774            _ => unreachable!(),
1775        }
1776    }
1777}