Skip to main content

miri/shims/unix/
socket.rs

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