src/tools/miri/src/shims/unix/socket.rs RUST 1,778 lines View on github.com → Search inside
1use std::cell::{Cell, RefCell};2use std::io::Read;3use std::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, SocketAddrV4, SocketAddrV6};4use std::time::Duration;5use std::{io, iter};67use 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;1415use crate::concurrency::blocking_io::InterestReceiver;16use crate::shims::files::{EvalContextExt as _, FdId, FileDescription, FileDescriptionRef};17use crate::shims::unix::UnixFileDescription;18use crate::*;1920#[derive(Debug, PartialEq)]21enum SocketFamily {22    // IPv4 internet protocols23    IPv4,24    // IPv6 internet protocols25    IPv6,26}2728#[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 able39    /// to ensure the connection is established. This is only reachable40    /// from the [`SocketState::Initial`] state.41    Connecting(TcpStream),42    /// The `connect` syscall has been called on the socket and43    /// we ensured that the connection is established, or44    /// the socket was created by the `accept` syscall.45    /// For a socket created using the `connect` syscall, this is46    /// only reachable from the [`SocketState::Connecting`] state.47    Connected(TcpStream),48}4950#[derive(Debug)]51struct Socket {52    /// Family of the socket, used to ensure socket only binds/connects to address of53    /// 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}6061impl FileDescription for Socket {62    fn name(&self) -> &'static str {63        "socket"64    }6566    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!");7374        interp_ok(Ok(()))75    }7677    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!");8687        let socket = self;8889        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                    }103104                    // Since `read` is the same as `recv` with no flags, we just treat105                    // the `read` as a `recv` here.106107                    if socket.is_non_block.get() {108                        // We have a non-blocking socket and thus don't want to block until109                        // 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 block114                        // 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    }122123    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!");132133        let socket = self;134135        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                    }149150                    // Since `write` is the same as `send` with no flags, we just treat151                    // the `write` as a `send` here.152153                    if socket.is_non_block.get() {154                        // We have a non-blocking socket and thus don't want to block until155                        // 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 block160                        // 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    }168169    fn short_fd_operations(&self) -> bool {170        // Linux de-facto guarantees (or at least, applications like tokio assume [1, 2]) that171        // when a read/write on a streaming socket comes back short, the kernel buffer is172        // 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#L182175        // [2]: https://github.com/tokio-rs/tokio/blob/6c03e03898d71eca976ee1ad8481cf112ae722ba/tokio/src/io/poll_evented.rs#L240176        false177    }178179    fn as_unix<'tcx>(&self, _ecx: &MiriInterpCx<'tcx>) -> &dyn UnixFileDescription {180        self181    }182183    fn get_flags<'tcx>(&self, ecx: &mut MiriInterpCx<'tcx>) -> InterpResult<'tcx, Scalar> {184        let mut flags = ecx.eval_libc_i32("O_RDWR");185186        if self.is_non_block.get() {187            flags |= ecx.eval_libc_i32("O_NONBLOCK");188        }189190        interp_ok(Scalar::from_i32(flags))191    }192193    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");199200        // 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        }207208        // 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        }212213        interp_ok(Scalar::from_i32(0))214    }215}216217impl 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!");225226        let fionbio = ecx.eval_libc("FIONBIO");227228        if op == fionbio {229            // On these OSes, Rust uses the ioctl, so we trust that it is reasonable and controls230            // 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 use236                // 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.os240                );241            }242243            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        }251252        throw_unsup_format!("ioctl: unsupported operation {op:#x} on socket");253    }254}255256impl<'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();267268        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()?;271272        // Reject if isolation is enabled273        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        }277278        let mut is_sock_nonblock = false;279280        // Interpret the flag. Every flag we recognize is "subtracted" from `flags`, so281        // 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::Illumos285        ) {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        }299300        let family = if domain == this.eval_libc_i32("AF_INET") {301            SocketFamily::IPv4302        } else if domain == this.eval_libc_i32("AF_INET6") {303            SocketFamily::IPv6304        } else {305            throw_unsup_format!(306                "socket: domain {:#x} is unsupported, only AF_INET and \307                AF_INET6 are allowed.",308                domain309            );310        };311312        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                flags317            );318        }319        if protocol != 0 && protocol != this.eval_libc_i32("IPPROTO_TCP") {320            throw_unsup_format!(321                "socket: socket protocol {protocol} is unsupported, \322                only IPPROTO_TCP and 0 are allowed"323            );324        }325326        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        });332333        interp_ok(Scalar::from_i32(fds.insert(fd)))334    }335336    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();343344        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        };349350        // Get the file handle351        let Some(fd) = this.machine.fds.get(socket) else {352            return this.set_last_error_and_return_i32(LibcError("EBADF"));353        };354355        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        };359360        assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");361362        let mut state = socket.state.borrow_mut();363364        match *state {365            SocketState::Initial => {366                let address_family = match &address {367                    SocketAddr::V4(_) => SocketFamily::IPv4,368                    SocketAddr::V6(_) => SocketFamily::IPv6,369                };370371                if socket.family != address_family {372                    // Attempted to bind an address from a family that doesn't match373                    // 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 address380                        // 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                }386387                *state = SocketState::Bound(address);388            }389            SocketState::Connecting(_) | SocketState::Connected(_) =>390                throw_unsup_format!(391                    "bind: socket is already connected and binding a392                    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        }400401        interp_ok(Scalar::from_i32(0))402    }403404    fn listen(&mut self, socket: &OpTy<'tcx>, backlog: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {405        let this = self.eval_context_mut();406407        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()?;410411        // Get the file handle412        let Some(fd) = this.machine.fds.get(socket) else {413            return this.set_last_error_and_return_i32(LibcError("EBADF"));414        };415416        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        };420421        assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");422423        let mut state = socket.state.borrow_mut();424425        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        }443444        interp_ok(Scalar::from_i32(0))445    }446447    /// 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();459460        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 };465466        // Get the file handle467        let Some(fd) = this.machine.fds.get(socket) else {468            return this.set_last_error_and_return(LibcError("EBADF"), dest);469        };470471        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        };475476        assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");477478        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        };483484        let mut is_client_sock_nonblock = false;485486        // Interpret the flag. Every flag we recognize is "subtracted" from `flags`, so487        // 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::Illumos491        ) {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        }505506        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        }512513        if socket.is_non_block.get() {514            // We have a non-blocking socket and thus don't want to block until515            // 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 since524                    // `syscall(SYS_accept4, ...)` returns a long which doesn't match525                    // 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 block533            // 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    }544545    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();554555        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        };560561        // Get the file handle562        let Some(fd) = this.machine.fds.get(socket) else {563            return this.set_last_error_and_return(LibcError("EBADF"), dest);564        };565566        let Some(socket) = fd.downcast::<Socket>() else {567            // Man page specifies to return ENOTSOCK if `fd` is not a socket568            return this.set_last_error_and_return(LibcError("ENOTSOCK"), dest);569        };570571        assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");572573        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're579            // sure that the connection is established, since TCP sockets are usually580            // 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        }587588        // Mio returns a potentially unconnected stream.589        // We can be ensured that the connection is established when590        // [`TcpStream::take_err`] and [`TcpStream::peer_addr`] both591        // 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        };597598        if socket.is_non_block.get() {599            // We have a non-blocking socket and thus don't want to block until600            // the connection is established.601602            // Since the [`TcpStream::connect`] function of mio hides the EINPROGRESS603            // we just always return EINPROGRESS and check whether the connection succeeded604            // 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 block608            // until the connection with the server is established.609610            let dest = dest.clone();611612            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    }630631    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();641642        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()?;648649        // Get the file handle650        let Some(fd) = this.machine.fds.get(socket) else {651            return this.set_last_error_and_return(LibcError("EBADF"), dest);652        };653654        let Some(socket) = fd.downcast::<Socket>() else {655            // Man page specifies to return ENOTSOCK if `fd` is not a socket656            return this.set_last_error_and_return(LibcError("ENOTSOCK"), dest);657        };658659        let mut is_op_non_block = false;660661        // Interpret the flag. Every flag we recognize is "subtracted" from `flags`, so662        // 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::Illumos666        ) {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 when673                // 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        }682683        if flags != 0 {684            throw_unsup_format!(685                "send: flag {flags:#x} is unsupported, only MSG_NOSIGNAL and MSG_DONTWAIT are allowed",686            );687        }688689        // If either the operation or the socket is non-blocking, we don't want690        // 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();693694        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                    }710711                    if is_op_non_block || socket.is_non_block.get() {712                        // We have a non-blocking operation or a non-blocking socket and713                        // 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 block720                        // 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    }740741    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();751752        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()?;758759        // Get the file handle760        let Some(fd) = this.machine.fds.get(socket) else {761            return this.set_last_error_and_return(LibcError("EBADF"), dest);762        };763764        let Some(socket) = fd.downcast::<Socket>() else {765            // Man page specifies to return ENOTSOCK if `fd` is not a socket766            return this.set_last_error_and_return(LibcError("ENOTSOCK"), dest);767        };768769        let mut should_peek = false;770        let mut is_op_non_block = false;771772        // Interpret the flag. Every flag we recognize is "subtracted" from `flags`, so773        // if there is anything left at the end, that's an unsupported flag.774775        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        }780781        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        }790791        if matches!(792            this.tcx.sess.target.os,793            Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos794        ) {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        }803804        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        }810811        // If either the operation or the socket is non-blocking, we don't want812        // 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();815816        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                    }832833                    if is_op_non_block || socket.is_non_block.get() {834                        // We have a non-blocking operation or a non-blocking socket and835                        // 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 block842                        // 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    }863864    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();873874        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)?;879880        // Get the file handle881        let Some(fd) = this.machine.fds.get(socket) else {882            return this.set_last_error_and_return_i32(LibcError("EBADF"));883        };884885        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        };889890        if level == this.eval_libc_i32("SOL_SOCKET") {891            let opt_so_reuseaddr = this.eval_libc_i32("SO_REUSEADDR");892893            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");896897                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.906907                    return interp_ok(Scalar::from_i32(0));908                }909            }910911            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 a919                // 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        }927928        throw_unsup_format!(929            "setsockopt: level {level:#x} is unsupported, only SOL_SOCKET is allowed"930        );931    }932933    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();940941        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)?;944945        // Get the file handle946        let Some(fd) = this.machine.fds.get(socket) else {947            return this.set_last_error_and_return_i32(LibcError("EBADF"));948        };949950        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        };954955        assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");956957        let state = socket.state.borrow();958959        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 random963                    // port. Since we don't yet have an underlying socket, we don't know what this964                    // 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                }970971                *address972            }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        };982983        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    }988989    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();998999        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)?;10021003        // Get the file handle1004        let Some(fd) = this.machine.fds.get(socket) else {1005            return this.set_last_error_and_return(LibcError("EBADF"), dest);1006        };10071008        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        };10121013        assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");10141015        let dest = dest.clone();10161017        // It's only safe to call [`TcpStream::peer_addr`] after the socket is connected since1018        // 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                    };10331034                    let SocketState::Connected(stream) = &*socket.state.borrow() else {1035                        unreachable!()1036                    };10371038                    let address = match stream.peer_addr() {1039                        Ok(address) => address,1040                        Err(e) => return this.set_last_error_and_return(e, &dest),1041                    };10421043                    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    }10561057    fn shutdown(&mut self, socket: &OpTy<'tcx>, how: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {1058        let this = self.eval_context_mut();10591060        let socket = this.read_scalar(socket)?.to_i32()?;1061        let how = this.read_scalar(how)?.to_i32()?;10621063        // Get the file handle1064        let Some(fd) = this.machine.fds.get(socket) else {1065            return this.set_last_error_and_return_i32(LibcError("EBADF"));1066        };10671068        let Some(socket) = fd.downcast::<Socket>() else {1069            // Man page specifies to return ENOTSOCK if `fd` is not a socket.1070            return this.set_last_error_and_return_i32(LibcError("ENOTSOCK"));1071        };10721073        assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!");10741075        let state = socket.state.borrow();10761077        let (SocketState::Connecting(stream) | SocketState::Connected(stream)) = &*state else {1078            return this.set_last_error_and_return_i32(LibcError("ENOTCONN"));1079        };10801081        let shut_rd = this.eval_libc_i32("SHUT_RD");1082        let shut_wr = this.eval_libc_i32("SHUT_WR");1083        let shut_rdwr = this.eval_libc_i32("SHUT_RDWR");10841085        let how = match () {1086            _ if how == shut_rd => Shutdown::Read,1087            _ if how == shut_wr => Shutdown::Write,1088            _ if how == shut_rdwr => Shutdown::Both,1089            // An invalid value was passed to `how`.1090            _ => return this.set_last_error_and_return_i32(LibcError("EINVAL")),1091        };10921093        match stream.shutdown(how) {1094            Ok(_) => interp_ok(Scalar::from_i32(0)),1095            Err(e) => this.set_last_error_and_return_i32(e),1096        }1097    }1098}10991100impl<'tcx> EvalContextPrivExt<'tcx> for crate::MiriInterpCx<'tcx> {}1101trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {1102    /// Attempt to turn an address and length operand into a standard library socket address.1103    ///1104    /// Returns an IO error should the address length not match the address family length.1105    fn socket_address(1106        &self,1107        address: &OpTy<'tcx>,1108        address_len: &OpTy<'tcx>,1109        foreign_name: &'static str,1110    ) -> InterpResult<'tcx, Result<SocketAddr, IoError>> {1111        let this = self.eval_context_ref();11121113        let socklen_layout = this.libc_ty_layout("socklen_t");1114        // We only support address lengths which can be stored in a u64 since the1115        // size of a layout is also stored in a u64.1116        let address_len: u64 =1117            this.read_scalar(address_len)?.to_int(socklen_layout.size)?.try_into().unwrap();11181119        // Initially, treat address as generic sockaddr just to extract the family field.1120        let sockaddr_layout = this.libc_ty_layout("sockaddr");1121        if address_len < sockaddr_layout.size.bytes() {1122            // Address length should be at least as big as the generic sockaddr1123            return interp_ok(Err(LibcError("EINVAL")));1124        }1125        let address = this.deref_pointer_as(address, sockaddr_layout)?;11261127        let family_field = this.project_field_named(&address, "sa_family")?;1128        let family_layout = this.libc_ty_layout("sa_family_t");1129        let family = this.read_scalar(&family_field)?.to_int(family_layout.size)?;11301131        // Depending on the family, decide whether it's IPv4 or IPv6 and use specialized layout1132        // to extract address and port.1133        let socket_addr = if family == this.eval_libc_i32("AF_INET").into() {1134            let sockaddr_in_layout = this.libc_ty_layout("sockaddr_in");1135            if address_len != sockaddr_in_layout.size.bytes() {1136                // Address length should be exactly the length of an IPv4 address.1137                return interp_ok(Err(LibcError("EINVAL")));1138            }1139            let address = address.transmute(sockaddr_in_layout, this)?;11401141            let port_field = this.project_field_named(&address, "sin_port")?;1142            // Read bytes and treat them as big endian since port is stored in network byte order.1143            let port_bytes: [u8; 2] = this1144                .read_bytes_ptr_strip_provenance(port_field.ptr(), Size::from_bytes(2))?1145                .try_into()1146                .unwrap();1147            let port = u16::from_be_bytes(port_bytes);11481149            let addr_field = this.project_field_named(&address, "sin_addr")?;1150            let s_addr_field = this.project_field_named(&addr_field, "s_addr")?;1151            // Read bytes and treat them as big endian since address is stored in network byte order.1152            let addr_bytes: [u8; 4] = this1153                .read_bytes_ptr_strip_provenance(s_addr_field.ptr(), Size::from_bytes(4))?1154                .try_into()1155                .unwrap();1156            let addr_bits = u32::from_be_bytes(addr_bytes);11571158            SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::from_bits(addr_bits), port))1159        } else if family == this.eval_libc_i32("AF_INET6").into() {1160            let sockaddr_in6_layout = this.libc_ty_layout("sockaddr_in6");1161            if address_len != sockaddr_in6_layout.size.bytes() {1162                // Address length should be exactly the length of an IPv6 address.1163                return interp_ok(Err(LibcError("EINVAL")));1164            }1165            // We cannot transmute since the `sockaddr_in6` layout is bigger than the `sockaddr` layout.1166            let address = address.offset(Size::ZERO, sockaddr_in6_layout, this)?;11671168            let port_field = this.project_field_named(&address, "sin6_port")?;1169            // Read bytes and treat them as big endian since port is stored in network byte order.1170            let port_bytes: [u8; 2] = this1171                .read_bytes_ptr_strip_provenance(port_field.ptr(), Size::from_bytes(2))?1172                .try_into()1173                .unwrap();1174            let port = u16::from_be_bytes(port_bytes);11751176            let addr_field = this.project_field_named(&address, "sin6_addr")?;1177            let s_addr_field = this1178                .project_field_named(&addr_field, "s6_addr")?1179                .transmute(this.machine.layouts.u128, this)?;1180            // Read bytes and treat them as big endian since address is stored in network byte order.1181            let addr_bytes: [u8; 16] = this1182                .read_bytes_ptr_strip_provenance(s_addr_field.ptr(), Size::from_bytes(16))?1183                .try_into()1184                .unwrap();1185            let addr_bits = u128::from_be_bytes(addr_bytes);11861187            let flowinfo_field = this.project_field_named(&address, "sin6_flowinfo")?;1188            // flowinfo doesn't get the big endian treatment as this field is stored in native byte order1189            // and not in network byte order.1190            let flowinfo = this.read_scalar(&flowinfo_field)?.to_u32()?;11911192            let scope_id_field = this.project_field_named(&address, "sin6_scope_id")?;1193            // scope_id doesn't get the big endian treatment as this field is stored in native byte order1194            // and not in network byte order.1195            let scope_id = this.read_scalar(&scope_id_field)?.to_u32()?;11961197            SocketAddr::V6(SocketAddrV6::new(1198                Ipv6Addr::from_bits(addr_bits),1199                port,1200                flowinfo,1201                scope_id,1202            ))1203        } else {1204            // Socket of other types shouldn't be created in a first place and1205            // thus also no address family of another type should be supported.1206            throw_unsup_format!(1207                "{foreign_name}: address family {family:#x} is unsupported, \1208                only AF_INET and AF_INET6 are allowed"1209            );1210        };12111212        interp_ok(Ok(socket_addr))1213    }12141215    /// Attempt to write a standard library socket address into a pointer.1216    ///1217    /// The `address_len_ptr` parameter serves both as input and output parameter.1218    /// On input, it points to the size of the buffer `address_ptr` points to, and1219    /// on output it points to the non-truncated size of the written address in the1220    /// buffer pointed to by `address_ptr`.1221    ///1222    /// If the address buffer doesn't fit the whole address, the address is truncated to not1223    /// overflow the buffer.1224    fn write_socket_address(1225        &mut self,1226        address: &SocketAddr,1227        address_ptr: Pointer,1228        address_len_ptr: Pointer,1229        foreign_name: &'static str,1230    ) -> InterpResult<'tcx, Result<(), IoError>> {1231        let this = self.eval_context_mut();12321233        if address_ptr == Pointer::null() || address_len_ptr == Pointer::null() {1234            // The POSIX man page doesn't account for the cases where the `address_ptr` or1235            // `address_len_ptr` could be null pointers. Thus, this behavior is undefined!1236            throw_ub_format!(1237                "{foreign_name}: writing a socket address but the address or the length pointer is a null pointer"1238            )1239        }12401241        let socklen_layout = this.libc_ty_layout("socklen_t");1242        let address_buffer_len_place = this.ptr_to_mplace(address_len_ptr, socklen_layout);1243        // We only support buffer lengths which can be stored in a u64 since the1244        // size of a layout in bytes is also stored in a u64.1245        let address_buffer_len: u64 = this1246            .read_scalar(&address_buffer_len_place)?1247            .to_int(socklen_layout.size)?1248            .try_into()1249            .unwrap();12501251        let (address_buffer, address_layout) = match address {1252            SocketAddr::V4(address) => {1253                // IPv4 address bytes; already stored in network byte order.1254                let address_bytes = address.ip().octets();1255                // Port needs to be manually turned into network byte order.1256                let port = address.port().to_be();12571258                let sockaddr_in_layout = this.libc_ty_layout("sockaddr_in");1259                // Allocate new buffer on the stack with the `sockaddr_in` layout.1260                // We need a temporary buffer as `address_ptr` might not point to a large enough1261                // buffer, in which case we have to truncate.1262                let address_buffer = this.allocate(sockaddr_in_layout, MemoryKind::Stack)?;1263                // Zero the whole buffer as some libc targets have additional fields which we fill1264                // with zero bytes (just like the standard library does it).1265                this.write_bytes_ptr(1266                    address_buffer.ptr(),1267                    iter::repeat_n(0, address_buffer.layout.size.bytes_usize()),1268                )?;12691270                let sin_family_field = this.project_field_named(&address_buffer, "sin_family")?;1271                // We cannot simply write the `AF_INET` scalar into the `sin_family_field` because on most1272                // systems the field has a layout of 16-bit whilst the scalar has a size of 32-bit.1273                // Since the `AF_INET` constant is chosen such that it can safely be converted into1274                // a 16-bit integer, we use the following logic to get a scalar of the right size.1275                let af_inet = this.eval_libc("AF_INET");1276                let address_family =1277                    Scalar::from_int(af_inet.to_int(af_inet.size())?, sin_family_field.layout.size);1278                this.write_scalar(address_family, &sin_family_field)?;12791280                let sin_port_field = this.project_field_named(&address_buffer, "sin_port")?;1281                // Write the port in target native endianness bytes as we already converted it1282                // to big endian above.1283                this.write_bytes_ptr(sin_port_field.ptr(), port.to_ne_bytes())?;12841285                let sin_addr_field = this.project_field_named(&address_buffer, "sin_addr")?;1286                let s_addr_field = this.project_field_named(&sin_addr_field, "s_addr")?;1287                this.write_bytes_ptr(s_addr_field.ptr(), address_bytes)?;12881289                (address_buffer, sockaddr_in_layout)1290            }1291            SocketAddr::V6(address) => {1292                // IPv6 address bytes; already stored in network byte order.1293                let address_bytes = address.ip().octets();1294                // Port needs to be manually turned into network byte order.1295                let port = address.port().to_be();1296                // Flowinfo is stored in native byte order.1297                let flowinfo = address.flowinfo();1298                // Scope id is stored in native byte order.1299                let scope_id = address.scope_id();13001301                let sockaddr_in6_layout = this.libc_ty_layout("sockaddr_in6");1302                // Allocate new buffer on the stack with the `sockaddr_in6` layout.1303                // We need a temporary buffer as `address_ptr` might not point to a large enough1304                // buffer, in which case we have to truncate.1305                let address_buffer = this.allocate(sockaddr_in6_layout, MemoryKind::Stack)?;1306                // Zero the whole buffer as some libc targets have additional fields which we fill1307                // with zero bytes (just like the standard library does it).1308                this.write_bytes_ptr(1309                    address_buffer.ptr(),1310                    iter::repeat_n(0, address_buffer.layout.size.bytes_usize()),1311                )?;13121313                let sin6_family_field = this.project_field_named(&address_buffer, "sin6_family")?;1314                // We cannot simply write the `AF_INET6` scalar into the `sin6_family_field` because on most1315                // systems the field has a layout of 16-bit whilst the scalar has a size of 32-bit.1316                // Since the `AF_INET6` constant is chosen such that it can safely be converted into1317                // a 16-bit integer, we use the following logic to get a scalar of the right size.1318                let af_inet6 = this.eval_libc("AF_INET6");1319                let address_family = Scalar::from_int(1320                    af_inet6.to_int(af_inet6.size())?,1321                    sin6_family_field.layout.size,1322                );1323                this.write_scalar(address_family, &sin6_family_field)?;13241325                let sin6_port_field = this.project_field_named(&address_buffer, "sin6_port")?;1326                // Write the port in target native endianness bytes as we already converted it1327                // to big endian above.1328                this.write_bytes_ptr(sin6_port_field.ptr(), port.to_ne_bytes())?;13291330                let sin6_flowinfo_field =1331                    this.project_field_named(&address_buffer, "sin6_flowinfo")?;1332                this.write_scalar(Scalar::from_u32(flowinfo), &sin6_flowinfo_field)?;13331334                let sin6_scope_id_field =1335                    this.project_field_named(&address_buffer, "sin6_scope_id")?;1336                this.write_scalar(Scalar::from_u32(scope_id), &sin6_scope_id_field)?;13371338                let sin6_addr_field = this.project_field_named(&address_buffer, "sin6_addr")?;1339                let s6_addr_field = this.project_field_named(&sin6_addr_field, "s6_addr")?;1340                this.write_bytes_ptr(s6_addr_field.ptr(), address_bytes)?;13411342                (address_buffer, sockaddr_in6_layout)1343            }1344        };13451346        // Copy the truncated address into the pointer pointed to by `address_ptr`.1347        this.mem_copy(1348            address_buffer.ptr(),1349            address_ptr,1350            // Truncate the address to fit the provided buffer.1351            address_layout.size.min(Size::from_bytes(address_buffer_len)),1352            // The buffers are guaranteed to not overlap since the `address_buffer`1353            // was just newly allocated on the stack.1354            true,1355        )?;1356        // Deallocate the address buffer as it was only needed to construct the address and1357        // copy it into the buffer pointed to by `address_ptr`.1358        this.deallocate_ptr(address_buffer.ptr(), None, MemoryKind::Stack)?;1359        // Size of the non-truncated address.1360        let address_len = address_layout.size.bytes();13611362        this.write_scalar(1363            Scalar::from_uint(address_len, socklen_layout.size),1364            &address_buffer_len_place,1365        )?;13661367        interp_ok(Ok(()))1368    }13691370    /// Block the thread until there's an incoming connection or an error occurred.1371    ///1372    /// This recursively calls itself should the operation still block for some reason.1373    ///1374    /// **Note**: This function is only safe to call when having previously ensured1375    /// that the socket is in [`SocketState::Listening`].1376    fn block_for_accept(1377        &mut self,1378        socket: FileDescriptionRef<Socket>,1379        address_ptr: Pointer,1380        address_len_ptr: Pointer,1381        is_client_sock_nonblock: bool,1382        dest: MPlaceTy<'tcx>,1383    ) {1384        let this = self.eval_context_mut();1385        this.block_thread_for_io(1386            socket.clone(),1387            Interest::READABLE,1388            None,1389            callback!(@capture<'tcx> {1390                address_ptr: Pointer,1391                address_len_ptr: Pointer,1392                is_client_sock_nonblock: bool,1393                socket: FileDescriptionRef<Socket>,1394                dest: MPlaceTy<'tcx>,1395            } |this, kind: UnblockKind| {1396                assert_eq!(kind, UnblockKind::Ready);13971398                match this.try_non_block_accept(&socket, address_ptr, address_len_ptr, is_client_sock_nonblock)? {1399                    Ok(sockfd) => {1400                        // We need to create the scalar using the destination size since1401                        // `syscall(SYS_accept4, ...)` returns a long which doesn't match1402                        // the int returned from the `accept`/`accept4` syscalls.1403                        // See <https://man7.org/linux/man-pages/man2/syscall.2.html>.1404                        this.write_scalar(Scalar::from_int(sockfd, dest.layout.size), &dest)1405                    },1406                    Err(IoError::HostError(e)) if e.kind() == io::ErrorKind::WouldBlock => {1407                        // We need to block the thread again as it would still block.1408                        this.block_for_accept(socket, address_ptr, address_len_ptr, is_client_sock_nonblock, dest);1409                        interp_ok(())1410                    }1411                    Err(e) => this.set_last_error_and_return(e, &dest),1412                }1413            }),1414        );1415    }14161417    /// Attempt to accept an incoming connection on the listening socket in a1418    /// non-blocking manner.1419    ///1420    /// **Note**: This function is only safe to call when having previously ensured1421    /// that the socket is in [`SocketState::Listening`].1422    fn try_non_block_accept(1423        &mut self,1424        socket: &FileDescriptionRef<Socket>,1425        address_ptr: Pointer,1426        address_len_ptr: Pointer,1427        is_client_sock_nonblock: bool,1428    ) -> InterpResult<'tcx, Result<i32, IoError>> {1429        let this = self.eval_context_mut();14301431        let state = socket.state.borrow();1432        let SocketState::Listening(listener) = &*state else {1433            panic!(1434                "try_non_block_accept must only be called when socket is in `SocketState::Listening`"1435            )1436        };14371438        let (stream, addr) = match listener.accept() {1439            Ok(peer) => peer,1440            Err(e) => return interp_ok(Err(IoError::HostError(e))),1441        };14421443        let family = match addr {1444            SocketAddr::V4(_) => SocketFamily::IPv4,1445            SocketAddr::V6(_) => SocketFamily::IPv6,1446        };14471448        if address_ptr != Pointer::null() {1449            // We only attempt a write if the address pointer is not a null pointer.1450            // If the address pointer is a null pointer the user isn't interested in the1451            // address and we don't need to write anything.1452            if let Err(e) =1453                this.write_socket_address(&addr, address_ptr, address_len_ptr, "accept4")?1454            {1455                return interp_ok(Err(e));1456            };1457        }14581459        let fd = this.machine.fds.new_ref(Socket {1460            family,1461            state: RefCell::new(SocketState::Connected(stream)),1462            is_non_block: Cell::new(is_client_sock_nonblock),1463        });1464        let sockfd = this.machine.fds.insert(fd);1465        interp_ok(Ok(sockfd))1466    }14671468    /// Block the thread until we can send bytes into the connected socket1469    /// or an error occurred.1470    ///1471    /// This recursively calls itself should the operation still block for some reason.1472    ///1473    /// **Note**: This function is only safe to call when having previously ensured1474    /// that the socket is in [`SocketState::Connected`].1475    fn block_for_send(1476        &mut self,1477        socket: FileDescriptionRef<Socket>,1478        buffer_ptr: Pointer,1479        length: usize,1480        finish: DynMachineCallback<'tcx, Result<usize, IoError>>,1481    ) {1482        let this = self.eval_context_mut();1483        this.block_thread_for_io(1484            socket.clone(),1485            Interest::WRITABLE,1486            None,1487            callback!(@capture<'tcx> {1488                socket: FileDescriptionRef<Socket>,1489                buffer_ptr: Pointer,1490                length: usize,1491                finish: DynMachineCallback<'tcx, Result<usize, IoError>>,1492            } |this, kind: UnblockKind| {1493                assert_eq!(kind, UnblockKind::Ready);14941495                match this.try_non_block_send(&socket, buffer_ptr, length)? {1496                    Err(IoError::HostError(e)) if e.kind() == io::ErrorKind::WouldBlock => {1497                        this.block_for_send(socket, buffer_ptr, length, finish);1498                        interp_ok(())1499                    },1500                    result => finish.call(this, result)1501                }1502            }),1503        );1504    }15051506    /// Attempt to send bytes into the connected socket in a non-blocking manner.1507    ///1508    /// **Note**: This function is only safe to call when having previously ensured1509    /// that the socket is in [`SocketState::Connected`].1510    fn try_non_block_send(1511        &mut self,1512        socket: &FileDescriptionRef<Socket>,1513        buffer_ptr: Pointer,1514        length: usize,1515    ) -> InterpResult<'tcx, Result<usize, IoError>> {1516        let this = self.eval_context_mut();15171518        let SocketState::Connected(stream) = &mut *socket.state.borrow_mut() else {1519            panic!("try_non_block_send must only be called when the socket is connected")1520        };15211522        // This is a *non-blocking* write.1523        let result = this.write_to_host(stream, length, buffer_ptr)?;1524        // FIXME: When the host does a short write, we should emit an epoll edge -- at least for targets for which tokio assumes no short writes:1525        // <https://github.com/tokio-rs/tokio/blob/6c03e03898d71eca976ee1ad8481cf112ae722ba/tokio/src/io/poll_evented.rs#L240>1526        match result {1527            Err(IoError::HostError(e)) if e.kind() == io::ErrorKind::NotConnected => {1528                // On Windows hosts, `send` can return WSAENOTCONN where EAGAIN or EWOULDBLOCK1529                // would be returned on UNIX-like systems. We thus remap this error to an EWOULDBLOCK.1530                interp_ok(Err(IoError::HostError(io::ErrorKind::WouldBlock.into())))1531            }1532            Err(IoError::HostError(e))1533                if cfg!(windows)1534                    && matches!(e.raw_os_error(), Some(/* WSAESHUTDOWN error code */ 10058)) =>1535            {1536                // FIXME: This is a temporary workaround for handling WSAESHUTDOWN errors1537                // on Windows. A discussion on how those errors should be handled can be found here:1538                // <https://rust-lang.zulipchat.com/#narrow/channel/219381-t-libs/topic/WSAESHUTDOWN.20error.20on.20Windows/near/591883531>1539                interp_ok(Err(IoError::HostError(io::ErrorKind::BrokenPipe.into())))1540            }1541            result => interp_ok(result),1542        }1543    }15441545    /// Block the thread until we can receive bytes from the connected socket1546    /// or an error occurred.1547    ///1548    /// This recursively calls itself should the operation still block for some reason.1549    ///1550    /// **Note**: This function is only safe to call when having previously ensured1551    /// that the socket is in [`SocketState::Connected`].1552    fn block_for_recv(1553        &mut self,1554        socket: FileDescriptionRef<Socket>,1555        buffer_ptr: Pointer,1556        length: usize,1557        should_peek: bool,1558        finish: DynMachineCallback<'tcx, Result<usize, IoError>>,1559    ) {1560        let this = self.eval_context_mut();1561        this.block_thread_for_io(1562            socket.clone(),1563            Interest::READABLE,1564            None,1565            callback!(@capture<'tcx> {1566                socket: FileDescriptionRef<Socket>,1567                buffer_ptr: Pointer,1568                length: usize,1569                should_peek: bool,1570                finish: DynMachineCallback<'tcx, Result<usize, IoError>>,1571            } |this, kind: UnblockKind| {1572                assert_eq!(kind, UnblockKind::Ready);15731574                match this.try_non_block_recv(&socket, buffer_ptr, length, should_peek)? {1575                    Err(IoError::HostError(e)) if e.kind() == io::ErrorKind::WouldBlock => {1576                        // We need to block the thread again as it would still block.1577                        this.block_for_recv(socket, buffer_ptr, length, should_peek, finish);1578                        interp_ok(())1579                    },1580                    result => finish.call(this, result)1581                }1582            }),1583        );1584    }15851586    /// Attempt to receive bytes from the connected socket in a non-blocking manner.1587    ///1588    /// **Note**: This function is only safe to call when having previously ensured1589    /// that the socket is in [`SocketState::Connected`].1590    fn try_non_block_recv(1591        &mut self,1592        socket: &FileDescriptionRef<Socket>,1593        buffer_ptr: Pointer,1594        length: usize,1595        should_peek: bool,1596    ) -> InterpResult<'tcx, Result<usize, IoError>> {1597        let this = self.eval_context_mut();15981599        let SocketState::Connected(stream) = &mut *socket.state.borrow_mut() else {1600            panic!("try_non_block_recv must only be called when the socket is connected")1601        };16021603        // This is a *non-blocking* read/peek.1604        let result = this.read_from_host(1605            |buf| {1606                if should_peek { stream.peek(buf) } else { stream.read(buf) }1607            },1608            length,1609            buffer_ptr,1610        )?;1611        // FIXME: When the host does a short read, we should emit an epoll edge -- at least for targets for which tokio assumes no short reads:1612        // <https://github.com/tokio-rs/tokio/blob/6c03e03898d71eca976ee1ad8481cf112ae722ba/tokio/src/io/poll_evented.rs#L182>1613        match result {1614            Err(IoError::HostError(e)) if e.kind() == io::ErrorKind::NotConnected => {1615                // On Windows hosts, `recv` can return WSAENOTCONN where EAGAIN or EWOULDBLOCK1616                // would be returned on UNIX-like systems. We thus remap this error to an EWOULDBLOCK.1617                interp_ok(Err(IoError::HostError(io::ErrorKind::WouldBlock.into())))1618            }1619            result => interp_ok(result),1620        }1621    }16221623    // Execute the provided callback function when the socket is either in1624    // [`SocketState::Connected`] or an error occurred.1625    /// If the socket is currently neither in the [`SocketState::Connecting`] nor1626    /// the [`SocketState::Connecting`] state, an ENOTCONN error is returned.1627    /// When the callback function is called with `Ok(_)`, then we're guaranteed1628    /// that the socket is in the [`SocketState::Connected`] state.1629    ///1630    /// This function can optionally also block until either an error occurred or1631    /// the socket reached the [`SocketState::Connected`] state.1632    fn ensure_connected(1633        &mut self,1634        socket: FileDescriptionRef<Socket>,1635        should_wait: bool,1636        foreign_name: &'static str,1637        action: DynMachineCallback<'tcx, Result<(), ()>>,1638    ) -> InterpResult<'tcx> {1639        let this = self.eval_context_mut();16401641        let state = socket.state.borrow();1642        match &*state {1643            SocketState::Connecting(_) => { /* fall-through to below */ }1644            SocketState::Connected(_) => {1645                drop(state);1646                return action.call(this, Ok(()));1647            }1648            _ => {1649                drop(state);1650                return action.call(this, Err(()));1651            }1652        };16531654        drop(state);16551656        // We're currently connecting. Since the underlying mio socket is non-blocking,1657        // the only way to determine whether we are done connecting is by polling.1658        // If we should wait until the connection is established, the timeout is `None`.1659        // Otherwise, we use a zero duration timeout, i.e. we return immediately1660        // (but we still go through the scheduler once -- which is fine).1661        let timeout = if should_wait {1662            None1663        } else {1664            Some((TimeoutClock::Monotonic, TimeoutAnchor::Absolute, Duration::ZERO))1665        };16661667        this.block_thread_for_io(1668            socket.clone(),1669            Interest::WRITABLE,1670            timeout,1671            callback!(1672                @capture<'tcx> {1673                    socket: FileDescriptionRef<Socket>,1674                    should_wait: bool,1675                    foreign_name: &'static str,1676                    action: DynMachineCallback<'tcx, Result<(), ()>>,1677                } |this, kind: UnblockKind| {1678                    if UnblockKind::TimedOut == kind {1679                        // We can only time out when `should_wait` is false.1680                        // This then means that the socket is not yet connected.1681                        assert!(!should_wait);1682                        this.machine.blocking_io.deregister(socket.id(), InterestReceiver::UnblockThread(this.active_thread()));1683                        return action.call(this, Err(()))1684                    }16851686                    // The thread woke up because it's ready, indicating a writeable or error event.16871688                    let mut state = socket.state.borrow_mut();1689                    let stream = match &*state {1690                        SocketState::Connecting(stream) => stream,1691                        SocketState::Connected(_) => {1692                            drop(state);1693                            // This can happen because we blocked the thread:1694                            // maybe another thread "upgraded" the connection in the meantime.1695                            return action.call(this, Ok(()))1696                        },1697                        _ => {1698                            drop(state);1699                            // We ensured that we only block when we're currently connecting.1700                            // Since this thread just got rescheduled, it could be that another1701                            // thread realized that the connection failed and we're thus in1702                            // an "invalid state".1703                            return action.call(this, Err(()))1704                        }1705                    };17061707                    // Manually check whether there were any errors since calling `connect`.1708                    if let Ok(Some(_)) = stream.take_error() {1709                        // There was an error during connecting and thus we1710                        // return ENOTCONN. It's the program's responsibility1711                        // to read SO_ERROR itself.1712                        //1713                        // Go back to initial state since the only way of getting into the1714                        // `Connecting` state is from the `Initial` state and at this point1715                        // we know that the connection won't be established anymore.1716                        //1717                        // FIXME: We're currently just dropping the error information. Eventually1718                        // we'll have to store it so that it can be recovered by the user.1719                        *state = SocketState::Initial;1720                        drop(state);1721                        return action.call(this, Err(()))1722                    }17231724                    // There was no error during connecting. We still need to ensure that1725                    // the wakeup wasn't spurious. We do this by attempting to read the1726                    // peer address of the socket (following the advice given by mio):1727                    // <https://docs.rs/mio/latest/mio/net/struct.TcpStream.html#notes>17281729                    match stream.peer_addr() {1730                        Ok(_) => { /* fall-through to below */},1731                        Err(e) if matches!(e.kind(), io::ErrorKind::NotConnected | io::ErrorKind::InProgress) => {1732                            // We received a spurious wakeup from the OS. This should be considered an OS bug:1733                            // <https://github.com/tokio-rs/mio/issues/1942#issuecomment-4169378308>1734                            panic!("{foreign_name}: received writable event from OS but socket is not yet connected")1735                        },1736                        Err(_) => {1737                            // For all other errors the socket is connected. Since we're not interested in the1738                            // peer address and only want to know whether the socket is connected, we can ignore1739                            // the error and continue.1740                        }1741                    }17421743                    // The connection is established.17441745                    // Temporarily use dummy state to take ownership of the stream.1746                    let SocketState::Connecting(stream) = std::mem::replace(&mut*state, SocketState::Initial) else {1747                        // At the start of the function we ensured that we're currently connecting.1748                        unreachable!()1749                    };1750                    *state = SocketState::Connected(stream);1751                    drop(state);1752                    action.call(this, Ok(()))1753                }1754            ),1755        );17561757        interp_ok(())1758    }1759}17601761impl VisitProvenance for FileDescriptionRef<Socket> {1762    // A socket doesn't contain any references to machine memory1763    // and thus we don't need to propagate the visit.1764    fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {}1765}17661767impl WithSource for Socket {1768    fn with_source(&self, f: &mut dyn FnMut(&mut dyn Source) -> io::Result<()>) -> io::Result<()> {1769        let mut state = self.state.borrow_mut();1770        match &mut *state {1771            SocketState::Listening(listener) => f(listener),1772            SocketState::Connecting(stream) | SocketState::Connected(stream) => f(stream),1773            // We never try adding a socket which is not backed by a real socket to the poll registry.1774            _ => unreachable!(),1775        }1776    }1777}

Code quality findings 40

Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
/// This is only reachable from the [`SocketState::Initial`] state.
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
/// This is only reachable from the [`SocketState::Bound`] state.
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
/// from the [`SocketState::Initial`] state.
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
/// only reachable from the [`SocketState::Connecting`] state.
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
// Linux de-facto guarantees (or at least, applications like tokio assume [1, 2]) that
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
// [`TcpStream::take_err`] and [`TcpStream::peer_addr`] both
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
// don't return an error after receiving an [`Interest::WRITEABLE`]
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
// Since the [`TcpStream::connect`] function of mio hides the EINPROGRESS
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
this.read_scalar(length)?.to_uint(size_layout.size)?.try_into().unwrap();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
Ok(size) => this.write_scalar(Scalar::from_target_isize(size.try_into().unwrap(), this), &dest),
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
Ok(size) => this.write_scalar(Scalar::from_target_isize(size.try_into().unwrap(), this), &dest),
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
this.read_scalar(length)?.to_uint(size_layout.size)?.try_into().unwrap();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
Ok(size) => this.write_scalar(Scalar::from_target_isize(size.try_into().unwrap(), this), &dest),
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
Ok(size) => this.write_scalar(Scalar::from_target_isize(size.try_into().unwrap(), this), &dest),
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
// It's only safe to call [`TcpStream::peer_addr`] after the socket is connected since
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
this.read_scalar(address_len)?.to_int(socklen_layout.size)?.try_into().unwrap();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
.unwrap();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
.unwrap();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
.unwrap();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
.unwrap();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
.unwrap();
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
/// that the socket is in [`SocketState::Listening`].
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
/// that the socket is in [`SocketState::Listening`].
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
/// that the socket is in [`SocketState::Connected`].
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
/// that the socket is in [`SocketState::Connected`].
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
/// that the socket is in [`SocketState::Connected`].
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
/// that the socket is in [`SocketState::Connected`].
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
/// If the socket is currently neither in the [`SocketState::Connecting`] nor
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
/// the [`SocketState::Connecting`] state, an ENOTCONN error is returned.
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
/// that the socket is in the [`SocketState::Connected`] state.
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
/// the socket reached the [`SocketState::Connected`] state.
Info: Wildcard imports (`use some::path::*;`) can obscure the origin of names and lead to conflicts. Prefer importing specific items explicitly.
info maintainability wildcard-import
use crate::*;
Info: This standard library function returns a Result. Ensure the Result is handled properly (e.g., using '?', match, if let) rather than potentially panicking with .unwrap() or .expect().
info correctness unhandled-result
match TcpListener::bind(socket_addr) {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match &*socket.state.borrow() {
Info: This standard library function returns a Result. Ensure the Result is handled properly (e.g., using '?', match, if let) rather than potentially panicking with .unwrap() or .expect().
info correctness unhandled-result
match TcpStream::connect(address) {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
let how = match () {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match stream.shutdown(how) {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match &*state {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
let stream = match &*state {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match &mut *state {

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.