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::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 {
320            throw_unsup_format!(
321                "socket: socket protocol {protocol} is unsupported, \
322                only 0 is 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
1058impl<'tcx> EvalContextPrivExt<'tcx> for crate::MiriInterpCx<'tcx> {}
1059trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
1060    /// Attempt to turn an address and length operand into a standard library socket address.
1061    ///
1062    /// Returns an IO error should the address length not match the address family length.
1063    fn socket_address(
1064        &self,
1065        address: &OpTy<'tcx>,
1066        address_len: &OpTy<'tcx>,
1067        foreign_name: &'static str,
1068    ) -> InterpResult<'tcx, Result<SocketAddr, IoError>> {
1069        let this = self.eval_context_ref();
1070
1071        let socklen_layout = this.libc_ty_layout("socklen_t");
1072        // We only support address lengths which can be stored in a u64 since the
1073        // size of a layout is also stored in a u64.
1074        let address_len: u64 =
1075            this.read_scalar(address_len)?.to_int(socklen_layout.size)?.try_into().unwrap();
1076
1077        // Initially, treat address as generic sockaddr just to extract the family field.
1078        let sockaddr_layout = this.libc_ty_layout("sockaddr");
1079        if address_len < sockaddr_layout.size.bytes() {
1080            // Address length should be at least as big as the generic sockaddr
1081            return interp_ok(Err(LibcError("EINVAL")));
1082        }
1083        let address = this.deref_pointer_as(address, sockaddr_layout)?;
1084
1085        let family_field = this.project_field_named(&address, "sa_family")?;
1086        let family_layout = this.libc_ty_layout("sa_family_t");
1087        let family = this.read_scalar(&family_field)?.to_int(family_layout.size)?;
1088
1089        // Depending on the family, decide whether it's IPv4 or IPv6 and use specialized layout
1090        // to extract address and port.
1091        let socket_addr = if family == this.eval_libc_i32("AF_INET").into() {
1092            let sockaddr_in_layout = this.libc_ty_layout("sockaddr_in");
1093            if address_len != sockaddr_in_layout.size.bytes() {
1094                // Address length should be exactly the length of an IPv4 address.
1095                return interp_ok(Err(LibcError("EINVAL")));
1096            }
1097            let address = address.transmute(sockaddr_in_layout, this)?;
1098
1099            let port_field = this.project_field_named(&address, "sin_port")?;
1100            // Read bytes and treat them as big endian since port is stored in network byte order.
1101            let port_bytes: [u8; 2] = this
1102                .read_bytes_ptr_strip_provenance(port_field.ptr(), Size::from_bytes(2))?
1103                .try_into()
1104                .unwrap();
1105            let port = u16::from_be_bytes(port_bytes);
1106
1107            let addr_field = this.project_field_named(&address, "sin_addr")?;
1108            let s_addr_field = this.project_field_named(&addr_field, "s_addr")?;
1109            // Read bytes and treat them as big endian since address is stored in network byte order.
1110            let addr_bytes: [u8; 4] = this
1111                .read_bytes_ptr_strip_provenance(s_addr_field.ptr(), Size::from_bytes(4))?
1112                .try_into()
1113                .unwrap();
1114            let addr_bits = u32::from_be_bytes(addr_bytes);
1115
1116            SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::from_bits(addr_bits), port))
1117        } else if family == this.eval_libc_i32("AF_INET6").into() {
1118            let sockaddr_in6_layout = this.libc_ty_layout("sockaddr_in6");
1119            if address_len != sockaddr_in6_layout.size.bytes() {
1120                // Address length should be exactly the length of an IPv6 address.
1121                return interp_ok(Err(LibcError("EINVAL")));
1122            }
1123            // We cannot transmute since the `sockaddr_in6` layout is bigger than the `sockaddr` layout.
1124            let address = address.offset(Size::ZERO, sockaddr_in6_layout, this)?;
1125
1126            let port_field = this.project_field_named(&address, "sin6_port")?;
1127            // Read bytes and treat them as big endian since port is stored in network byte order.
1128            let port_bytes: [u8; 2] = this
1129                .read_bytes_ptr_strip_provenance(port_field.ptr(), Size::from_bytes(2))?
1130                .try_into()
1131                .unwrap();
1132            let port = u16::from_be_bytes(port_bytes);
1133
1134            let addr_field = this.project_field_named(&address, "sin6_addr")?;
1135            let s_addr_field = this
1136                .project_field_named(&addr_field, "s6_addr")?
1137                .transmute(this.machine.layouts.u128, this)?;
1138            // Read bytes and treat them as big endian since address is stored in network byte order.
1139            let addr_bytes: [u8; 16] = this
1140                .read_bytes_ptr_strip_provenance(s_addr_field.ptr(), Size::from_bytes(16))?
1141                .try_into()
1142                .unwrap();
1143            let addr_bits = u128::from_be_bytes(addr_bytes);
1144
1145            let flowinfo_field = this.project_field_named(&address, "sin6_flowinfo")?;
1146            // flowinfo doesn't get the big endian treatment as this field is stored in native byte order
1147            // and not in network byte order.
1148            let flowinfo = this.read_scalar(&flowinfo_field)?.to_u32()?;
1149
1150            let scope_id_field = this.project_field_named(&address, "sin6_scope_id")?;
1151            // scope_id doesn't get the big endian treatment as this field is stored in native byte order
1152            // and not in network byte order.
1153            let scope_id = this.read_scalar(&scope_id_field)?.to_u32()?;
1154
1155            SocketAddr::V6(SocketAddrV6::new(
1156                Ipv6Addr::from_bits(addr_bits),
1157                port,
1158                flowinfo,
1159                scope_id,
1160            ))
1161        } else {
1162            // Socket of other types shouldn't be created in a first place and
1163            // thus also no address family of another type should be supported.
1164            throw_unsup_format!(
1165                "{foreign_name}: address family {family:#x} is unsupported, \
1166                only AF_INET and AF_INET6 are allowed"
1167            );
1168        };
1169
1170        interp_ok(Ok(socket_addr))
1171    }
1172
1173    /// Attempt to write a standard library socket address into a pointer.
1174    ///
1175    /// The `address_len_ptr` parameter serves both as input and output parameter.
1176    /// On input, it points to the size of the buffer `address_ptr` points to, and
1177    /// on output it points to the non-truncated size of the written address in the
1178    /// buffer pointed to by `address_ptr`.
1179    ///
1180    /// If the address buffer doesn't fit the whole address, the address is truncated to not
1181    /// overflow the buffer.
1182    fn write_socket_address(
1183        &mut self,
1184        address: &SocketAddr,
1185        address_ptr: Pointer,
1186        address_len_ptr: Pointer,
1187        foreign_name: &'static str,
1188    ) -> InterpResult<'tcx, Result<(), IoError>> {
1189        let this = self.eval_context_mut();
1190
1191        if address_ptr == Pointer::null() || address_len_ptr == Pointer::null() {
1192            // The POSIX man page doesn't account for the cases where the `address_ptr` or
1193            // `address_len_ptr` could be null pointers. Thus, this behavior is undefined!
1194            throw_ub_format!(
1195                "{foreign_name}: writing a socket address but the address or the length pointer is a null pointer"
1196            )
1197        }
1198
1199        let socklen_layout = this.libc_ty_layout("socklen_t");
1200        let address_buffer_len_place = this.ptr_to_mplace(address_len_ptr, socklen_layout);
1201        // We only support buffer lengths which can be stored in a u64 since the
1202        // size of a layout in bytes is also stored in a u64.
1203        let address_buffer_len: u64 = this
1204            .read_scalar(&address_buffer_len_place)?
1205            .to_int(socklen_layout.size)?
1206            .try_into()
1207            .unwrap();
1208
1209        let (address_buffer, address_layout) = match address {
1210            SocketAddr::V4(address) => {
1211                // IPv4 address bytes; already stored in network byte order.
1212                let address_bytes = address.ip().octets();
1213                // Port needs to be manually turned into network byte order.
1214                let port = address.port().to_be();
1215
1216                let sockaddr_in_layout = this.libc_ty_layout("sockaddr_in");
1217                // Allocate new buffer on the stack with the `sockaddr_in` layout.
1218                // We need a temporary buffer as `address_ptr` might not point to a large enough
1219                // buffer, in which case we have to truncate.
1220                let address_buffer = this.allocate(sockaddr_in_layout, MemoryKind::Stack)?;
1221                // Zero the whole buffer as some libc targets have additional fields which we fill
1222                // with zero bytes (just like the standard library does it).
1223                this.write_bytes_ptr(
1224                    address_buffer.ptr(),
1225                    iter::repeat_n(0, address_buffer.layout.size.bytes_usize()),
1226                )?;
1227
1228                let sin_family_field = this.project_field_named(&address_buffer, "sin_family")?;
1229                // We cannot simply write the `AF_INET` scalar into the `sin_family_field` because on most
1230                // systems the field has a layout of 16-bit whilst the scalar has a size of 32-bit.
1231                // Since the `AF_INET` constant is chosen such that it can safely be converted into
1232                // a 16-bit integer, we use the following logic to get a scalar of the right size.
1233                let af_inet = this.eval_libc("AF_INET");
1234                let address_family =
1235                    Scalar::from_int(af_inet.to_int(af_inet.size())?, sin_family_field.layout.size);
1236                this.write_scalar(address_family, &sin_family_field)?;
1237
1238                let sin_port_field = this.project_field_named(&address_buffer, "sin_port")?;
1239                // Write the port in target native endianness bytes as we already converted it
1240                // to big endian above.
1241                this.write_bytes_ptr(sin_port_field.ptr(), port.to_ne_bytes())?;
1242
1243                let sin_addr_field = this.project_field_named(&address_buffer, "sin_addr")?;
1244                let s_addr_field = this.project_field_named(&sin_addr_field, "s_addr")?;
1245                this.write_bytes_ptr(s_addr_field.ptr(), address_bytes)?;
1246
1247                (address_buffer, sockaddr_in_layout)
1248            }
1249            SocketAddr::V6(address) => {
1250                // IPv6 address bytes; already stored in network byte order.
1251                let address_bytes = address.ip().octets();
1252                // Port needs to be manually turned into network byte order.
1253                let port = address.port().to_be();
1254                // Flowinfo is stored in native byte order.
1255                let flowinfo = address.flowinfo();
1256                // Scope id is stored in native byte order.
1257                let scope_id = address.scope_id();
1258
1259                let sockaddr_in6_layout = this.libc_ty_layout("sockaddr_in6");
1260                // Allocate new buffer on the stack with the `sockaddr_in6` layout.
1261                // We need a temporary buffer as `address_ptr` might not point to a large enough
1262                // buffer, in which case we have to truncate.
1263                let address_buffer = this.allocate(sockaddr_in6_layout, MemoryKind::Stack)?;
1264                // Zero the whole buffer as some libc targets have additional fields which we fill
1265                // with zero bytes (just like the standard library does it).
1266                this.write_bytes_ptr(
1267                    address_buffer.ptr(),
1268                    iter::repeat_n(0, address_buffer.layout.size.bytes_usize()),
1269                )?;
1270
1271                let sin6_family_field = this.project_field_named(&address_buffer, "sin6_family")?;
1272                // We cannot simply write the `AF_INET6` scalar into the `sin6_family_field` because on most
1273                // systems the field has a layout of 16-bit whilst the scalar has a size of 32-bit.
1274                // Since the `AF_INET6` constant is chosen such that it can safely be converted into
1275                // a 16-bit integer, we use the following logic to get a scalar of the right size.
1276                let af_inet6 = this.eval_libc("AF_INET6");
1277                let address_family = Scalar::from_int(
1278                    af_inet6.to_int(af_inet6.size())?,
1279                    sin6_family_field.layout.size,
1280                );
1281                this.write_scalar(address_family, &sin6_family_field)?;
1282
1283                let sin6_port_field = this.project_field_named(&address_buffer, "sin6_port")?;
1284                // Write the port in target native endianness bytes as we already converted it
1285                // to big endian above.
1286                this.write_bytes_ptr(sin6_port_field.ptr(), port.to_ne_bytes())?;
1287
1288                let sin6_flowinfo_field =
1289                    this.project_field_named(&address_buffer, "sin6_flowinfo")?;
1290                this.write_scalar(Scalar::from_u32(flowinfo), &sin6_flowinfo_field)?;
1291
1292                let sin6_scope_id_field =
1293                    this.project_field_named(&address_buffer, "sin6_scope_id")?;
1294                this.write_scalar(Scalar::from_u32(scope_id), &sin6_scope_id_field)?;
1295
1296                let sin6_addr_field = this.project_field_named(&address_buffer, "sin6_addr")?;
1297                let s6_addr_field = this.project_field_named(&sin6_addr_field, "s6_addr")?;
1298                this.write_bytes_ptr(s6_addr_field.ptr(), address_bytes)?;
1299
1300                (address_buffer, sockaddr_in6_layout)
1301            }
1302        };
1303
1304        // Copy the truncated address into the pointer pointed to by `address_ptr`.
1305        this.mem_copy(
1306            address_buffer.ptr(),
1307            address_ptr,
1308            // Truncate the address to fit the provided buffer.
1309            address_layout.size.min(Size::from_bytes(address_buffer_len)),
1310            // The buffers are guaranteed to not overlap since the `address_buffer`
1311            // was just newly allocated on the stack.
1312            true,
1313        )?;
1314        // Deallocate the address buffer as it was only needed to construct the address and
1315        // copy it into the buffer pointed to by `address_ptr`.
1316        this.deallocate_ptr(address_buffer.ptr(), None, MemoryKind::Stack)?;
1317        // Size of the non-truncated address.
1318        let address_len = address_layout.size.bytes();
1319
1320        this.write_scalar(
1321            Scalar::from_uint(address_len, socklen_layout.size),
1322            &address_buffer_len_place,
1323        )?;
1324
1325        interp_ok(Ok(()))
1326    }
1327
1328    /// Block the thread until there's an incoming connection or an error occurred.
1329    ///
1330    /// This recursively calls itself should the operation still block for some reason.
1331    ///
1332    /// **Note**: This function is only safe to call when having previously ensured
1333    /// that the socket is in [`SocketState::Listening`].
1334    fn block_for_accept(
1335        &mut self,
1336        socket: FileDescriptionRef<Socket>,
1337        address_ptr: Pointer,
1338        address_len_ptr: Pointer,
1339        is_client_sock_nonblock: bool,
1340        dest: MPlaceTy<'tcx>,
1341    ) {
1342        let this = self.eval_context_mut();
1343        this.block_thread_for_io(
1344            socket.clone(),
1345            Interest::READABLE,
1346            None,
1347            callback!(@capture<'tcx> {
1348                address_ptr: Pointer,
1349                address_len_ptr: Pointer,
1350                is_client_sock_nonblock: bool,
1351                socket: FileDescriptionRef<Socket>,
1352                dest: MPlaceTy<'tcx>,
1353            } |this, kind: UnblockKind| {
1354                assert_eq!(kind, UnblockKind::Ready);
1355
1356                match this.try_non_block_accept(&socket, address_ptr, address_len_ptr, is_client_sock_nonblock)? {
1357                    Ok(sockfd) => {
1358                        // We need to create the scalar using the destination size since
1359                        // `syscall(SYS_accept4, ...)` returns a long which doesn't match
1360                        // the int returned from the `accept`/`accept4` syscalls.
1361                        // See <https://man7.org/linux/man-pages/man2/syscall.2.html>.
1362                        this.write_scalar(Scalar::from_int(sockfd, dest.layout.size), &dest)
1363                    },
1364                    Err(IoError::HostError(e)) if e.kind() == io::ErrorKind::WouldBlock => {
1365                        // We need to block the thread again as it would still block.
1366                        this.block_for_accept(socket, address_ptr, address_len_ptr, is_client_sock_nonblock, dest);
1367                        interp_ok(())
1368                    }
1369                    Err(e) => this.set_last_error_and_return(e, &dest),
1370                }
1371            }),
1372        );
1373    }
1374
1375    /// Attempt to accept an incoming connection on the listening socket in a
1376    /// non-blocking manner.
1377    ///
1378    /// **Note**: This function is only safe to call when having previously ensured
1379    /// that the socket is in [`SocketState::Listening`].
1380    fn try_non_block_accept(
1381        &mut self,
1382        socket: &FileDescriptionRef<Socket>,
1383        address_ptr: Pointer,
1384        address_len_ptr: Pointer,
1385        is_client_sock_nonblock: bool,
1386    ) -> InterpResult<'tcx, Result<i32, IoError>> {
1387        let this = self.eval_context_mut();
1388
1389        let state = socket.state.borrow();
1390        let SocketState::Listening(listener) = &*state else {
1391            panic!(
1392                "try_non_block_accept must only be called when socket is in `SocketState::Listening`"
1393            )
1394        };
1395
1396        let (stream, addr) = match listener.accept() {
1397            Ok(peer) => peer,
1398            Err(e) => return interp_ok(Err(IoError::HostError(e))),
1399        };
1400
1401        let family = match addr {
1402            SocketAddr::V4(_) => SocketFamily::IPv4,
1403            SocketAddr::V6(_) => SocketFamily::IPv6,
1404        };
1405
1406        if address_ptr != Pointer::null() {
1407            // We only attempt a write if the address pointer is not a null pointer.
1408            // If the address pointer is a null pointer the user isn't interested in the
1409            // address and we don't need to write anything.
1410            if let Err(e) =
1411                this.write_socket_address(&addr, address_ptr, address_len_ptr, "accept4")?
1412            {
1413                return interp_ok(Err(e));
1414            };
1415        }
1416
1417        let fd = this.machine.fds.new_ref(Socket {
1418            family,
1419            state: RefCell::new(SocketState::Connected(stream)),
1420            is_non_block: Cell::new(is_client_sock_nonblock),
1421        });
1422        let sockfd = this.machine.fds.insert(fd);
1423        interp_ok(Ok(sockfd))
1424    }
1425
1426    /// Block the thread until we can send bytes into the connected socket
1427    /// or an error occurred.
1428    ///
1429    /// This recursively calls itself should the operation still block for some reason.
1430    ///
1431    /// **Note**: This function is only safe to call when having previously ensured
1432    /// that the socket is in [`SocketState::Connected`].
1433    fn block_for_send(
1434        &mut self,
1435        socket: FileDescriptionRef<Socket>,
1436        buffer_ptr: Pointer,
1437        length: usize,
1438        finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
1439    ) {
1440        let this = self.eval_context_mut();
1441        this.block_thread_for_io(
1442            socket.clone(),
1443            Interest::WRITABLE,
1444            None,
1445            callback!(@capture<'tcx> {
1446                socket: FileDescriptionRef<Socket>,
1447                buffer_ptr: Pointer,
1448                length: usize,
1449                finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
1450            } |this, kind: UnblockKind| {
1451                assert_eq!(kind, UnblockKind::Ready);
1452
1453                match this.try_non_block_send(&socket, buffer_ptr, length)? {
1454                    Err(IoError::HostError(e)) if e.kind() == io::ErrorKind::WouldBlock => {
1455                        this.block_for_send(socket, buffer_ptr, length, finish);
1456                        interp_ok(())
1457                    },
1458                    result => finish.call(this, result)
1459                }
1460            }),
1461        );
1462    }
1463
1464    /// Attempt to send bytes into the connected socket in a non-blocking manner.
1465    ///
1466    /// **Note**: This function is only safe to call when having previously ensured
1467    /// that the socket is in [`SocketState::Connected`].
1468    fn try_non_block_send(
1469        &mut self,
1470        socket: &FileDescriptionRef<Socket>,
1471        buffer_ptr: Pointer,
1472        length: usize,
1473    ) -> InterpResult<'tcx, Result<usize, IoError>> {
1474        let this = self.eval_context_mut();
1475
1476        let SocketState::Connected(stream) = &mut *socket.state.borrow_mut() else {
1477            panic!("try_non_block_send must only be called when the socket is connected")
1478        };
1479
1480        // This is a *non-blocking* write.
1481        let result = this.write_to_host(stream, length, buffer_ptr)?;
1482        // 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:
1483        // <https://github.com/tokio-rs/tokio/blob/6c03e03898d71eca976ee1ad8481cf112ae722ba/tokio/src/io/poll_evented.rs#L240>
1484        match result {
1485            Err(IoError::HostError(e)) if e.kind() == io::ErrorKind::NotConnected => {
1486                // On Windows hosts, `send` can return WSAENOTCONN where EAGAIN or EWOULDBLOCK
1487                // would be returned on UNIX-like systems. We thus remap this error to an EWOULDBLOCK.
1488                interp_ok(Err(IoError::HostError(io::ErrorKind::WouldBlock.into())))
1489            }
1490            result => interp_ok(result),
1491        }
1492    }
1493
1494    /// Block the thread until we can receive bytes from the connected socket
1495    /// or an error occurred.
1496    ///
1497    /// This recursively calls itself should the operation still block for some reason.
1498    ///
1499    /// **Note**: This function is only safe to call when having previously ensured
1500    /// that the socket is in [`SocketState::Connected`].
1501    fn block_for_recv(
1502        &mut self,
1503        socket: FileDescriptionRef<Socket>,
1504        buffer_ptr: Pointer,
1505        length: usize,
1506        should_peek: bool,
1507        finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
1508    ) {
1509        let this = self.eval_context_mut();
1510        this.block_thread_for_io(
1511            socket.clone(),
1512            Interest::READABLE,
1513            None,
1514            callback!(@capture<'tcx> {
1515                socket: FileDescriptionRef<Socket>,
1516                buffer_ptr: Pointer,
1517                length: usize,
1518                should_peek: bool,
1519                finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
1520            } |this, kind: UnblockKind| {
1521                assert_eq!(kind, UnblockKind::Ready);
1522
1523                match this.try_non_block_recv(&socket, buffer_ptr, length, should_peek)? {
1524                    Err(IoError::HostError(e)) if e.kind() == io::ErrorKind::WouldBlock => {
1525                        // We need to block the thread again as it would still block.
1526                        this.block_for_recv(socket, buffer_ptr, length, should_peek, finish);
1527                        interp_ok(())
1528                    },
1529                    result => finish.call(this, result)
1530                }
1531            }),
1532        );
1533    }
1534
1535    /// Attempt to receive bytes from the connected socket in a non-blocking manner.
1536    ///
1537    /// **Note**: This function is only safe to call when having previously ensured
1538    /// that the socket is in [`SocketState::Connected`].
1539    fn try_non_block_recv(
1540        &mut self,
1541        socket: &FileDescriptionRef<Socket>,
1542        buffer_ptr: Pointer,
1543        length: usize,
1544        should_peek: bool,
1545    ) -> InterpResult<'tcx, Result<usize, IoError>> {
1546        let this = self.eval_context_mut();
1547
1548        let SocketState::Connected(stream) = &mut *socket.state.borrow_mut() else {
1549            panic!("try_non_block_recv must only be called when the socket is connected")
1550        };
1551
1552        // This is a *non-blocking* read/peek.
1553        let result = this.read_from_host(
1554            |buf| {
1555                if should_peek { stream.peek(buf) } else { stream.read(buf) }
1556            },
1557            length,
1558            buffer_ptr,
1559        )?;
1560        // 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:
1561        // <https://github.com/tokio-rs/tokio/blob/6c03e03898d71eca976ee1ad8481cf112ae722ba/tokio/src/io/poll_evented.rs#L182>
1562        match result {
1563            Err(IoError::HostError(e)) if e.kind() == io::ErrorKind::NotConnected => {
1564                // On Windows hosts, `recv` can return WSAENOTCONN where EAGAIN or EWOULDBLOCK
1565                // would be returned on UNIX-like systems. We thus remap this error to an EWOULDBLOCK.
1566                interp_ok(Err(IoError::HostError(io::ErrorKind::WouldBlock.into())))
1567            }
1568            result => interp_ok(result),
1569        }
1570    }
1571
1572    // Execute the provided callback function when the socket is either in
1573    // [`SocketState::Connected`] or an error occurred.
1574    /// If the socket is currently neither in the [`SocketState::Connecting`] nor
1575    /// the [`SocketState::Connecting`] state, an ENOTCONN error is returned.
1576    /// When the callback function is called with `Ok(_)`, then we're guaranteed
1577    /// that the socket is in the [`SocketState::Connected`] state.
1578    ///
1579    /// This function can optionally also block until either an error occurred or
1580    /// the socket reached the [`SocketState::Connected`] state.
1581    fn ensure_connected(
1582        &mut self,
1583        socket: FileDescriptionRef<Socket>,
1584        should_wait: bool,
1585        foreign_name: &'static str,
1586        action: DynMachineCallback<'tcx, Result<(), ()>>,
1587    ) -> InterpResult<'tcx> {
1588        let this = self.eval_context_mut();
1589
1590        let state = socket.state.borrow();
1591        match &*state {
1592            SocketState::Connecting(_) => { /* fall-through to below */ }
1593            SocketState::Connected(_) => {
1594                drop(state);
1595                return action.call(this, Ok(()));
1596            }
1597            _ => {
1598                drop(state);
1599                return action.call(this, Err(()));
1600            }
1601        };
1602
1603        drop(state);
1604
1605        // We're currently connecting. Since the underlying mio socket is non-blocking,
1606        // the only way to determine whether we are done connecting is by polling.
1607        // If we should wait until the connection is established, the timeout is `None`.
1608        // Otherwise, we use a zero duration timeout, i.e. we return immediately
1609        // (but we still go through the scheduler once -- which is fine).
1610        let timeout = if should_wait {
1611            None
1612        } else {
1613            Some((TimeoutClock::Monotonic, TimeoutAnchor::Absolute, Duration::ZERO))
1614        };
1615
1616        this.block_thread_for_io(
1617            socket.clone(),
1618            Interest::WRITABLE,
1619            timeout,
1620            callback!(
1621                @capture<'tcx> {
1622                    socket: FileDescriptionRef<Socket>,
1623                    should_wait: bool,
1624                    foreign_name: &'static str,
1625                    action: DynMachineCallback<'tcx, Result<(), ()>>,
1626                } |this, kind: UnblockKind| {
1627                    if UnblockKind::TimedOut == kind {
1628                        // We can only time out when `should_wait` is false.
1629                        // This then means that the socket is not yet connected.
1630                        assert!(!should_wait);
1631                        this.machine.blocking_io.deregister(socket.id(), InterestReceiver::UnblockThread(this.active_thread()));
1632                        return action.call(this, Err(()))
1633                    }
1634
1635                    // The thread woke up because it's ready, indicating a writeable or error event.
1636
1637                    let mut state = socket.state.borrow_mut();
1638                    let stream = match &*state {
1639                        SocketState::Connecting(stream) => stream,
1640                        SocketState::Connected(_) => {
1641                            drop(state);
1642                            // This can happen because we blocked the thread:
1643                            // maybe another thread "upgraded" the connection in the meantime.
1644                            return action.call(this, Ok(()))
1645                        },
1646                        _ => {
1647                            drop(state);
1648                            // We ensured that we only block when we're currently connecting.
1649                            // Since this thread just got rescheduled, it could be that another
1650                            // thread realized that the connection failed and we're thus in
1651                            // an "invalid state".
1652                            return action.call(this, Err(()))
1653                        }
1654                    };
1655
1656                    // Manually check whether there were any errors since calling `connect`.
1657                    if let Ok(Some(_)) = stream.take_error() {
1658                        // There was an error during connecting and thus we
1659                        // return ENOTCONN. It's the program's responsibility
1660                        // to read SO_ERROR itself.
1661                        //
1662                        // Go back to initial state since the only way of getting into the
1663                        // `Connecting` state is from the `Initial` state and at this point
1664                        // we know that the connection won't be established anymore.
1665                        //
1666                        // FIXME: We're currently just dropping the error information. Eventually
1667                        // we'll have to store it so that it can be recovered by the user.
1668                        *state = SocketState::Initial;
1669                        drop(state);
1670                        return action.call(this, Err(()))
1671                    }
1672
1673                    // There was no error during connecting. We still need to ensure that
1674                    // the wakeup wasn't spurious. We do this by attempting to read the
1675                    // peer address of the socket (following the advice given by mio):
1676                    // <https://docs.rs/mio/latest/mio/net/struct.TcpStream.html#notes>
1677
1678                    match stream.peer_addr() {
1679                        Ok(_) => { /* fall-through to below */},
1680                        Err(e) if matches!(e.kind(), io::ErrorKind::NotConnected | io::ErrorKind::InProgress) => {
1681                            // We received a spurious wakeup from the OS. This should be considered an OS bug:
1682                            // <https://github.com/tokio-rs/mio/issues/1942#issuecomment-4169378308>
1683                            panic!("{foreign_name}: received writable event from OS but socket is not yet connected")
1684                        },
1685                        Err(_) => {
1686                            // For all other errors the socket is connected. Since we're not interested in the
1687                            // peer address and only want to know whether the socket is connected, we can ignore
1688                            // the error and continue.
1689                        }
1690                    }
1691
1692                    // The connection is established.
1693
1694                    // Temporarily use dummy state to take ownership of the stream.
1695                    let SocketState::Connecting(stream) = std::mem::replace(&mut*state, SocketState::Initial) else {
1696                        // At the start of the function we ensured that we're currently connecting.
1697                        unreachable!()
1698                    };
1699                    *state = SocketState::Connected(stream);
1700                    drop(state);
1701                    action.call(this, Ok(()))
1702                }
1703            ),
1704        );
1705
1706        interp_ok(())
1707    }
1708}
1709
1710impl VisitProvenance for FileDescriptionRef<Socket> {
1711    // A socket doesn't contain any references to machine memory
1712    // and thus we don't need to propagate the visit.
1713    fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {}
1714}
1715
1716impl WithSource for Socket {
1717    fn with_source(&self, f: &mut dyn FnMut(&mut dyn Source) -> io::Result<()>) -> io::Result<()> {
1718        let mut state = self.state.borrow_mut();
1719        match &mut *state {
1720            SocketState::Listening(listener) => f(listener),
1721            SocketState::Connecting(stream) | SocketState::Connected(stream) => f(stream),
1722            // We never try adding a socket which is not backed by a real socket to the poll registry.
1723            _ => unreachable!(),
1724        }
1725    }
1726}