diff --git a/Cargo.lock b/Cargo.lock index 0d541973ac5ec..89d29381d994e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3422,9 +3422,9 @@ dependencies = [ [[package]] name = "rustc-build-sysroot" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eec3905e8201688412f6f4b1f6c86d38b3ee6578f59ba85f41330a3af61e8365" +checksum = "569d545953ee9a1ab9d3e9112e961739ff0cfd50bce06b126937395940be69c9" dependencies = [ "anyhow", "rustc_version", diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index 9129562efe1d7..d97fa71413540 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -125,7 +125,7 @@ jobs: # Deliberately skipping `./.github/workflows/setup` as we do our own setup - name: Add cache for cargo id: cache - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: | # Taken from . @@ -231,7 +231,7 @@ jobs: exit ${exitcode} fi - # Store merge commit message + # Store merge commit message git log -1 --pretty=%B > message.txt # Format changes diff --git a/src/tools/miri/.github/workflows/setup/action.yml b/src/tools/miri/.github/workflows/setup/action.yml index 9110e6947f490..a6c591154a94d 100644 --- a/src/tools/miri/.github/workflows/setup/action.yml +++ b/src/tools/miri/.github/workflows/setup/action.yml @@ -20,7 +20,7 @@ runs: # over time). - name: Add cache for cargo id: cache - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: | # Taken from . diff --git a/src/tools/miri/cargo-miri/Cargo.lock b/src/tools/miri/cargo-miri/Cargo.lock index 977728f63499a..f9f6e110c43c1 100644 --- a/src/tools/miri/cargo-miri/Cargo.lock +++ b/src/tools/miri/cargo-miri/Cargo.lock @@ -230,9 +230,9 @@ dependencies = [ [[package]] name = "rustc-build-sysroot" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eec3905e8201688412f6f4b1f6c86d38b3ee6578f59ba85f41330a3af61e8365" +checksum = "569d545953ee9a1ab9d3e9112e961739ff0cfd50bce06b126937395940be69c9" dependencies = [ "anyhow", "rustc_version", diff --git a/src/tools/miri/cargo-miri/Cargo.toml b/src/tools/miri/cargo-miri/Cargo.toml index 568bb29f49f88..dd7e4c12d3a44 100644 --- a/src/tools/miri/cargo-miri/Cargo.toml +++ b/src/tools/miri/cargo-miri/Cargo.toml @@ -18,7 +18,7 @@ directories = "6" rustc_version = "0.4" serde_json = "1.0.40" cargo_metadata = "0.23" -rustc-build-sysroot = "0.5.12" +rustc-build-sysroot = "0.5.13" # Enable some feature flags that dev-dependencies need but dependencies # do not. This makes `./miri install` after `./miri build` faster. diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 85571d95f742d..af66f0a4d9eab 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -116458d0a5ae01cd517cabd2d1aee7f5457018ab +55e86c996809902e8bbad512cfb4d2c18be446d9 diff --git a/src/tools/miri/src/shims/files.rs b/src/tools/miri/src/shims/files.rs index 694a5922b7990..29088ed67135a 100644 --- a/src/tools/miri/src/shims/files.rs +++ b/src/tools/miri/src/shims/files.rs @@ -1,7 +1,7 @@ use std::any::Any; use std::collections::BTreeMap; use std::fs::{File, Metadata}; -use std::io::{ErrorKind, IsTerminal, Seek, SeekFrom, Write}; +use std::io::{ErrorKind, IsTerminal, Read, Seek, SeekFrom, Write}; use std::marker::CoercePointee; use std::ops::Deref; use std::rc::{Rc, Weak}; @@ -245,7 +245,8 @@ impl FileDescription for io::Stdin { helpers::isolation_abort_error("`read` from stdin")?; } - let result = ecx.read_from_host(&*self, len, ptr)?; + let mut stdin = &*self; + let result = ecx.read_from_host(|buf| stdin.read(buf), len, ptr)?; finish.call(ecx, result) } @@ -356,7 +357,8 @@ impl FileDescription for FileHandle { ) -> InterpResult<'tcx> { assert!(communicate_allowed, "isolation should have prevented even opening a file"); - let result = ecx.read_from_host(&self.file, len, ptr)?; + let mut file = &self.file; + let result = ecx.read_from_host(|buf| file.read(buf), len, ptr)?; finish.call(ecx, result) } @@ -576,14 +578,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// and return whether that worked. fn read_from_host( &mut self, - mut file: impl io::Read, + mut read_cb: impl FnMut(&mut [u8]) -> io::Result, len: usize, ptr: Pointer, ) -> InterpResult<'tcx, Result> { let this = self.eval_context_mut(); let mut bytes = vec![0; len]; - let result = file.read(&mut bytes); + let result = read_cb(&mut bytes); match result { Ok(read_size) => { // If reading to `bytes` did not fail, we write those bytes to the buffer. @@ -596,7 +598,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } - /// Write data to a host `Write` type, withthe bytes taken from machine memory. + /// Write data to a host `Write` type, with the bytes taken from machine memory. fn write_to_host( &mut self, mut file: impl io::Write, diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index 3651bc171adcd..2b366b699065d 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -612,6 +612,24 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )?; this.connect(socket, address, address_len, dest)?; } + "send" => { + let [socket, buffer, length, flags] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *const _, libc::size_t, i32) -> libc::ssize_t), + link_name, + abi, + args, + )?; + this.send(socket, buffer, length, flags, dest)?; + } + "recv" => { + let [socket, buffer, length, flags] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *mut _, libc::size_t, i32) -> libc::ssize_t), + link_name, + abi, + args, + )?; + this.recv(socket, buffer, length, flags, dest)?; + } "setsockopt" => { let [socket, level, option_name, option_value, option_len] = this.check_shim_sig( shim_sig!(extern "C" fn(i32, i32, i32, *const _, libc::socklen_t) -> i32), diff --git a/src/tools/miri/src/shims/unix/socket.rs b/src/tools/miri/src/shims/unix/socket.rs index 8f2ab69261ce7..bbdf6fc54b2cf 100644 --- a/src/tools/miri/src/shims/unix/socket.rs +++ b/src/tools/miri/src/shims/unix/socket.rs @@ -1,16 +1,18 @@ use std::cell::{Cell, RefCell}; +use std::io::Read; use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; use std::{io, iter}; use mio::Interest; use mio::event::Source; use mio::net::{TcpListener, TcpStream}; +use rand::Rng; use rustc_abi::Size; use rustc_const_eval::interpret::{InterpResult, interp_ok}; use rustc_middle::throw_unsup_format; use rustc_target::spec::Os; -use crate::shims::files::{FdId, FileDescription, FileDescriptionRef}; +use crate::shims::files::{EvalContextExt as _, FdId, FileDescription, FileDescriptionRef}; use crate::{OpTy, Scalar, *}; #[derive(Debug, PartialEq)] @@ -131,15 +133,65 @@ impl FileDescription for Socket { fn destroy<'tcx>( self, _self_id: FdId, - _communicate_allowed: bool, + communicate_allowed: bool, _ecx: &mut MiriInterpCx<'tcx>, - ) -> InterpResult<'tcx, std::io::Result<()>> - where - Self: Sized, - { + ) -> InterpResult<'tcx, std::io::Result<()>> { + assert!(communicate_allowed, "cannot have `Socket` with isolation enabled!"); + interp_ok(Ok(())) } + fn read<'tcx>( + self: FileDescriptionRef, + communicate_allowed: bool, + ptr: Pointer, + len: usize, + ecx: &mut MiriInterpCx<'tcx>, + finish: DynMachineCallback<'tcx, Result>, + ) -> InterpResult<'tcx> { + assert!(communicate_allowed, "cannot have `Socket` with isolation enabled!"); + + if !matches!(&*self.state.borrow(), SocketState::Connected(_)) { + // We can only receive from connected sockets. For all other + // states we return a not connected error. + return finish.call(ecx, Err(LibcError("ENOTCONN"))); + } + + // Since `read` is the same as `recv` with no flags, we just treat + // the `read` as a `recv` here. + ecx.block_for_recv(self, ptr, len, /* should_peek */ false, finish); + + interp_ok(()) + } + + fn write<'tcx>( + self: FileDescriptionRef, + communicate_allowed: bool, + ptr: Pointer, + len: usize, + ecx: &mut MiriInterpCx<'tcx>, + finish: DynMachineCallback<'tcx, Result>, + ) -> InterpResult<'tcx> { + assert!(communicate_allowed, "cannot have `Socket` with isolation enabled!"); + + if !matches!(&*self.state.borrow(), SocketState::Connected(_)) { + // We can only send with connected sockets. For all other + // states we return a not connected error. + return finish.call(ecx, Err(LibcError("ENOTCONN"))); + } + + // Since `write` is the same as `send` with no flags, we just treat + // the `write` as a `send` here. + ecx.block_for_send(self, ptr, len, finish); + + interp_ok(()) + } + + fn short_fd_operations(&self) -> bool { + // Short accesses on TCP sockets are realistic and expected to happen. + true + } + fn get_flags<'tcx>(&self, ecx: &mut MiriInterpCx<'tcx>) -> InterpResult<'tcx, Scalar> { let mut flags = ecx.eval_libc_i32("O_RDWR"); @@ -190,7 +242,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos ) { // SOCK_NONBLOCK and SOCK_CLOEXEC only exist on Linux, Android, FreeBSD, - // Solaris, and Illumos targets + // Solaris, and Illumos targets. let sock_nonblock = this.eval_libc_i32("SOCK_NONBLOCK"); let sock_cloexec = this.eval_libc_i32("SOCK_CLOEXEC"); if flags & sock_nonblock == sock_nonblock { @@ -396,7 +448,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos ) { // SOCK_NONBLOCK and SOCK_CLOEXEC only exist on Linux, Android, FreeBSD, - // Solaris, and Illumos targets + // Solaris, and Illumos targets. let sock_nonblock = this.eval_libc_i32("SOCK_NONBLOCK"); let sock_cloexec = this.eval_libc_i32("SOCK_CLOEXEC"); if flags & sock_nonblock == sock_nonblock { @@ -496,6 +548,199 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { interp_ok(()) } + fn send( + &mut self, + socket: &OpTy<'tcx>, + buffer: &OpTy<'tcx>, + length: &OpTy<'tcx>, + flags: &OpTy<'tcx>, + // Location where the output scalar is written to. + dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + + let socket = this.read_scalar(socket)?.to_i32()?; + let buffer_ptr = this.read_pointer(buffer)?; + let size_layout = this.libc_ty_layout("size_t"); + let length: usize = + this.read_scalar(length)?.to_uint(size_layout.size)?.try_into().unwrap(); + let mut flags = this.read_scalar(flags)?.to_i32()?; + + // Get the file handle + let Some(fd) = this.machine.fds.get(socket) else { + return this.set_last_error_and_return(LibcError("EBADF"), dest); + }; + + let Some(socket) = fd.downcast::() else { + // Man page specifies to return ENOTSOCK if `fd` is not a socket + return this.set_last_error_and_return(LibcError("ENOTSOCK"), dest); + }; + + if !matches!(&*socket.state.borrow(), SocketState::Connected(_)) { + // We can only send with connected sockets. For all other + // states we return a not connected error. + return this.set_last_error_and_return(LibcError("ENOTCONN"), dest); + } + + // Non-deterministically decide to further reduce the length, simulating a partial send. + // We avoid reducing the write size to 0: the docs seem to be entirely fine with that, + // but the standard library is not (https://github.com/rust-lang/rust/issues/145959). + let length = if this.machine.short_fd_operations + && length >= 2 + && this.machine.rng.get_mut().random() + { + length / 2 + } else { + length + }; + + // Interpret the flag. Every flag we recognize is "subtracted" from `flags`, so + // if there is anything left at the end, that's an unsupported flag. + if matches!( + this.tcx.sess.target.os, + Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos + ) { + // MSG_NOSIGNAL only exists on Linux, Android, FreeBSD, + // Solaris, and Illumos targets. + let msg_nosignal = this.eval_libc_i32("MSG_NOSIGNAL"); + if flags & msg_nosignal == msg_nosignal { + // This is only needed to ensure that no EPIPE signal is sent when + // trying to send into a stream which is no longer connected. + // Since we don't support signals, we can ignore this. + flags &= !msg_nosignal; + } + } + + if flags != 0 { + throw_unsup_format!( + "send: flag {flags:#x} is unsupported, only MSG_NOSIGNAL is allowed", + ); + } + + let dest = dest.clone(); + + this.block_for_send( + socket, + buffer_ptr, + length, + callback!(@capture<'tcx> { + dest: MPlaceTy<'tcx> + } |this, result: Result| { + match result { + Ok(read_size) => { + let read_size: u64 = read_size.try_into().unwrap(); + let ssize_layout = this.libc_ty_layout("ssize_t"); + this.write_scalar(Scalar::from_int(read_size, ssize_layout.size), &dest) + } + Err(e) => this.set_last_error_and_return(e, &dest) + } + }), + ); + + interp_ok(()) + } + + fn recv( + &mut self, + socket: &OpTy<'tcx>, + buffer: &OpTy<'tcx>, + length: &OpTy<'tcx>, + flags: &OpTy<'tcx>, + // Location where the output scalar is written to. + dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + + let socket = this.read_scalar(socket)?.to_i32()?; + let buffer_ptr = this.read_pointer(buffer)?; + let size_layout = this.libc_ty_layout("size_t"); + let length: usize = + this.read_scalar(length)?.to_uint(size_layout.size)?.try_into().unwrap(); + let mut flags = this.read_scalar(flags)?.to_i32()?; + + // Get the file handle + let Some(fd) = this.machine.fds.get(socket) else { + return this.set_last_error_and_return(LibcError("EBADF"), dest); + }; + + let Some(socket) = fd.downcast::() else { + // Man page specifies to return ENOTSOCK if `fd` is not a socket + return this.set_last_error_and_return(LibcError("ENOTSOCK"), dest); + }; + + if !matches!(&*socket.state.borrow(), SocketState::Connected(_)) { + // We can only receive from connected sockets. For all other + // states we return a not connected error. + return this.set_last_error_and_return(LibcError("ENOTCONN"), dest); + } + + // Non-deterministically decide to further reduce the length, simulating a partial receive. + // We don't simulate partial receives for lengths < 2 because the man page states that a + // return value of zero can only be returned in some special cases: + // "When a stream socket peer has performed an orderly shutdown, the return value will be 0 + // (the traditional "end-of-file" return). [...] The value 0 may also be returned if the + // requested number of bytes to receive from a stream socket was 0." + let length = if this.machine.short_fd_operations + && length >= 2 + && this.machine.rng.get_mut().random() + { + length / 2 // since `length` is at least 2, the result is still at least 1 + } else { + length + }; + + let mut should_peek = false; + + // Interpret the flag. Every flag we recognize is "subtracted" from `flags`, so + // if there is anything left at the end, that's an unsupported flag. + + let msg_peek = this.eval_libc_i32("MSG_PEEK"); + if flags & msg_peek == msg_peek { + should_peek = true; + flags &= !msg_peek; + } + + if matches!(this.tcx.sess.target.os, Os::Linux | Os::Android | Os::FreeBsd | Os::Illumos) { + // MSG_CMSG_CLOEXEC only exists on Linux, Android, FreeBSD, + // and Illumos targets. + let msg_cmsg_cloexec = this.eval_libc_i32("MSG_CMSG_CLOEXEC"); + if flags & msg_cmsg_cloexec == msg_cmsg_cloexec { + // We don't support `exec` so we can ignore this. + flags &= !msg_cmsg_cloexec; + } + } + + if flags != 0 { + throw_unsup_format!( + "recv: flag {flags:#x} is unsupported, only MSG_PEEK \ + and MSG_CMSG_CLOEXEC are allowed", + ); + } + + let dest = dest.clone(); + + this.block_for_recv( + socket, + buffer_ptr, + length, + should_peek, + callback!(@capture<'tcx> { + dest: MPlaceTy<'tcx> + } |this, result: Result| { + match result { + Ok(read_size) => { + let read_size: u64 = read_size.try_into().unwrap(); + let ssize_layout = this.libc_ty_layout("ssize_t"); + this.write_scalar(Scalar::from_int(read_size, ssize_layout.size), &dest) + } + Err(e) => this.set_last_error_and_return(e, &dest) + } + }), + ); + + interp_ok(()) + } + fn setsockopt( &mut self, socket: &OpTy<'tcx>, @@ -1037,6 +1282,104 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }), ); } + + /// Block the thread until we can send bytes into the connected socket + /// or an error occurred. + /// + /// This recursively calls itself should the operation still block for some reason. + fn block_for_send( + &mut self, + socket: FileDescriptionRef, + buffer_ptr: Pointer, + length: usize, + finish: DynMachineCallback<'tcx, Result>, + ) { + let this = self.eval_context_mut(); + this.block_thread_for_io( + socket.clone(), + Interest::WRITABLE, + None, + callback!(@capture<'tcx> { + socket: FileDescriptionRef, + buffer_ptr: Pointer, + length: usize, + finish: DynMachineCallback<'tcx, Result>, + } |this, kind: UnblockKind| { + assert_eq!(kind, UnblockKind::Ready); + + let mut state = socket.state.borrow_mut(); + let SocketState::Connected(stream) = &mut*state else { + // We ensured that the socket is connected before blocking. + unreachable!() + }; + + // This is a *non-blocking* write. + let result = this.write_to_host(stream, length, buffer_ptr)?; + match result { + Err(IoError::HostError(e)) if e.kind() == io::ErrorKind::WouldBlock => { + // We need to block the thread again as it would still block. + drop(state); + this.block_for_send(socket, buffer_ptr, length, finish); + interp_ok(()) + }, + result => finish.call(this, result) + } + }), + ); + } + + /// Block the thread until we can receive bytes from the connected socket + /// or an error occurred. + /// + /// This recursively calls itself should the operation still block for some reason. + fn block_for_recv( + &mut self, + socket: FileDescriptionRef, + buffer_ptr: Pointer, + length: usize, + should_peek: bool, + finish: DynMachineCallback<'tcx, Result>, + ) { + let this = self.eval_context_mut(); + this.block_thread_for_io( + socket.clone(), + Interest::READABLE, + None, + callback!(@capture<'tcx> { + socket: FileDescriptionRef, + buffer_ptr: Pointer, + length: usize, + should_peek: bool, + finish: DynMachineCallback<'tcx, Result>, + } |this, kind: UnblockKind| { + assert_eq!(kind, UnblockKind::Ready); + + let mut state = socket.state.borrow_mut(); + let SocketState::Connected(stream) = &mut*state else { + // We ensured that the socket is connected before blocking. + unreachable!() + }; + + // This is a *non-blocking* read/peek. + let result = this.read_from_host(|buf| { + if should_peek { + stream.peek(buf) + } else { + stream.read(buf) + } + }, length, buffer_ptr)?; + match result { + Err(IoError::HostError(e)) if e.kind() == io::ErrorKind::WouldBlock => { + // We need to block the thread again as it would still block. + drop(state); + this.block_for_recv(socket, buffer_ptr, length, should_peek, finish); + interp_ok(()) + }, + result => finish.call(this, result) + } + }), + ); + } } impl VisitProvenance for FileDescriptionRef { diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs index 58a612102b180..1b65dea982464 100644 --- a/src/tools/miri/src/shims/unix/sync.rs +++ b/src/tools/miri/src/shims/unix/sync.rs @@ -6,16 +6,20 @@ use crate::*; /// Do a bytewise comparison of the two places. This is used to check if /// a synchronization primitive matches its static initializer value. +/// +/// `prefix`, if set, indicates that only the first N bytes should be compared. fn bytewise_equal<'tcx>( ecx: &MiriInterpCx<'tcx>, left: &MPlaceTy<'tcx>, right: &MPlaceTy<'tcx>, + prefix: Option, ) -> InterpResult<'tcx, bool> { let size = left.layout.size; assert_eq!(size, right.layout.size); + let cmp_size = prefix.map(Size::from_bytes).unwrap_or(size); - let left_bytes = ecx.read_bytes_ptr_strip_provenance(left.ptr(), size)?; - let right_bytes = ecx.read_bytes_ptr_strip_provenance(right.ptr(), size)?; + let left_bytes = ecx.read_bytes_ptr_strip_provenance(left.ptr(), cmp_size)?; + let right_bytes = ecx.read_bytes_ptr_strip_provenance(right.ptr(), cmp_size)?; interp_ok(left_bytes == right_bytes) } @@ -208,8 +212,15 @@ fn mutex_kind_from_static_initializer<'tcx>( ecx: &MiriInterpCx<'tcx>, mutex: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, MutexKind> { + let prefix = match &ecx.tcx.sess.target.os { + // On android, there's a 4-byte `value` header followed by "padding", and some versions + // of libc leave that uninitialized. Only check the `value` bytes. + Os::Android => Some(4), + _ => None, + }; + let is_initializer = |name| bytewise_equal(ecx, mutex, &ecx.eval_path(&["libc", name]), prefix); + // All the static initializers recognized here *must* be checked in `mutex_init_offset`! - let is_initializer = |name| bytewise_equal(ecx, mutex, &ecx.eval_path(&["libc", name])); // PTHREAD_MUTEX_INITIALIZER is recognized on all targets. if is_initializer("PTHREAD_MUTEX_INITIALIZER")? { @@ -292,10 +303,17 @@ where PTHREAD_UNINIT, PTHREAD_INIT, |ecx| { + let prefix = match &ecx.tcx.sess.target.os { + // On android, there's a 4-byte `value` header followed by "padding", and some + // versions of libc leave that uninitialized. Only check the `value` bytes. + Os::Android => Some(4), + _ => None, + }; if !bytewise_equal( ecx, &rwlock, &ecx.eval_path(&["libc", "PTHREAD_RWLOCK_INITIALIZER"]), + prefix, )? { throw_ub_format!( "`pthread_rwlock_t` was not properly initialized at this location, or it got overwritten" @@ -419,10 +437,17 @@ where PTHREAD_UNINIT, PTHREAD_INIT, |ecx| { + let prefix = match &ecx.tcx.sess.target.os { + // On android, there's a 4-byte `value` header followed by "padding", and some + // versions of libc leave that uninitialized. Only check the `value` bytes. + Os::Android => Some(4), + _ => None, + }; if !bytewise_equal( ecx, &cond, &ecx.eval_path(&["libc", "PTHREAD_COND_INITIALIZER"]), + prefix, )? { throw_ub_format!( "`pthread_cond_t` was not properly initialized at this location, or it got overwritten" diff --git a/src/tools/miri/src/shims/unix/unnamed_socket.rs b/src/tools/miri/src/shims/unix/unnamed_socket.rs index ea34f72feee5b..74d6abf7b63d4 100644 --- a/src/tools/miri/src/shims/unix/unnamed_socket.rs +++ b/src/tools/miri/src/shims/unix/unnamed_socket.rs @@ -4,8 +4,7 @@ use std::cell::{Cell, OnceCell, RefCell}; use std::collections::VecDeque; -use std::io; -use std::io::ErrorKind; +use std::io::{self, ErrorKind, Read}; use rustc_target::spec::Os; @@ -346,7 +345,7 @@ fn anonsocket_read<'tcx>( // Do full read / partial read based on the space available. // Conveniently, `read` exists on `VecDeque` and has exactly the desired behavior. - let read_size = ecx.read_from_host(&mut readbuf.buf, len, ptr)?.unwrap(); + let read_size = ecx.read_from_host(|buf| readbuf.buf.read(buf), len, ptr)?.unwrap(); let readbuf_now_empty = readbuf.buf.is_empty(); // Need to drop before others can access the readbuf again. diff --git a/src/tools/miri/tests/deps/Cargo.lock b/src/tools/miri/tests/deps/Cargo.lock index f54628e810425..2a1468d55ad0d 100644 --- a/src/tools/miri/tests/deps/Cargo.lock +++ b/src/tools/miri/tests/deps/Cargo.lock @@ -3,46 +3,22 @@ version = 4 [[package]] -name = "addr2line" -version = "0.24.2" +name = "anyhow" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - -[[package]] -name = "backtrace" -version = "0.3.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-targets 0.52.6", -] +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "bitflags" -version = "2.9.1" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "bytes" @@ -52,18 +28,24 @@ checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "cfg-if" -version = "1.0.1" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -72,11 +54,17 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "futures" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" dependencies = [ "futures-channel", "futures-core", @@ -88,9 +76,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", "futures-sink", @@ -98,21 +86,21 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-io" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] name = "futures-macro" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", @@ -121,28 +109,28 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] name = "futures-task" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-util" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-core", "futures-macro", "futures-sink", "futures-task", "pin-project-lite", - "pin-utils", + "slab", ] [[package]] @@ -158,9 +146,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", "js-sys", @@ -171,21 +159,49 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", - "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "r-efi 5.3.0", + "wasip2", ] [[package]] -name = "gimli" -version = "0.31.1" +name = "getrandom" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "wasip2", + "wasip3", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" @@ -194,68 +210,78 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] -name = "io-uring" -version = "0.7.8" +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "indexmap" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ - "bitflags", - "cfg-if", - "libc", + "equivalent", + "hashbrown 0.16.1", + "serde", + "serde_core", ] +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9" dependencies = [ "once_cell", "wasm-bindgen", ] +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "libc" -version = "0.2.174" +version = "0.2.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" [[package]] name = "linux-raw-sys" -version = "0.9.4" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "log" -version = "0.4.27" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "memchr" -version = "2.7.5" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" - -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", -] +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "mio" -version = "1.0.4" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" dependencies = [ "libc", "wasi 0.11.1+wasi-snapshot-preview1", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -265,8 +291,8 @@ dependencies = [ "cfg-if", "futures", "getrandom 0.1.16", - "getrandom 0.2.16", - "getrandom 0.3.3", + "getrandom 0.2.17", + "getrandom 0.3.4", "libc", "num_cpus", "page_size", @@ -285,20 +311,11 @@ dependencies = [ "libc", ] -[[package]] -name = "object" -version = "0.36.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" -dependencies = [ - "memchr", -] - [[package]] name = "once_cell" -version = "1.21.3" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "page_size" @@ -312,30 +329,34 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] -name = "pin-utils" -version = "0.1.0" +name = "prettyplease" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.40" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] @@ -347,54 +368,109 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] -name = "rustc-demangle" -version = "0.1.25" +name = "r-efi" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rustix" -version = "1.0.8" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", - "windows-sys 0.60.2", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", ] [[package]] name = "signal-hook-registry" -version = "1.4.5" +version = "1.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" dependencies = [ + "errno", "libc", ] [[package]] name = "slab" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "socket2" -version = "0.5.10" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] name = "syn" -version = "2.0.104" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -403,41 +479,38 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.20.0" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.3.3", + "getrandom 0.4.2", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "tokio" -version = "1.46.1" +version = "1.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17" +checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" dependencies = [ - "backtrace", "bytes", - "io-uring", "libc", "mio", "pin-project-lite", "signal-hook-registry", - "slab", "socket2", "tokio-macros", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" dependencies = [ "proc-macro2", "quote", @@ -446,9 +519,15 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.18" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "wasi" @@ -463,44 +542,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] -name = "wasi" -version = "0.14.2+wasi-0.2.4" +name = "wasip2" +version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" dependencies = [ - "wit-bindgen-rt", + "wit-bindgen", ] [[package]] -name = "wasm-bindgen" -version = "0.2.100" +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ - "cfg-if", - "once_cell", - "wasm-bindgen-macro", + "wit-bindgen", ] [[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" +name = "wasm-bindgen" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0" dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn", + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -508,26 +584,60 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2" dependencies = [ + "bumpalo", "proc-macro2", "quote", "syn", - "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b" dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + [[package]] name = "winapi" version = "0.3.9" @@ -551,22 +661,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" +name = "windows-link" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-sys" @@ -574,142 +672,173 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.2", + "windows-targets", ] [[package]] -name = "windows-targets" -version = "0.52.6" +name = "windows-sys" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", + "windows-link", ] [[package]] name = "windows-targets" -version = "0.53.2" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", + "windows-link", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" [[package]] name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" [[package]] name = "windows_i686_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" [[package]] name = "windows_i686_gnullvm" -version = "0.52.6" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" [[package]] -name = "windows_i686_gnullvm" -version = "0.53.0" +name = "windows_i686_msvc" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" [[package]] -name = "windows_i686_msvc" -version = "0.52.6" +name = "windows_x86_64_gnu" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" [[package]] -name = "windows_i686_msvc" -version = "0.53.0" +name = "windows_x86_64_gnullvm" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" [[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" +name = "windows_x86_64_msvc" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] -name = "windows_x86_64_gnu" -version = "0.53.0" +name = "wit-bindgen" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] [[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" +name = "wit-bindgen-core" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] [[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.0" +name = "wit-bindgen-rust" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] [[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" +name = "wit-bindgen-rust-macro" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] [[package]] -name = "windows_x86_64_msvc" -version = "0.53.0" +name = "wit-component" +version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] [[package]] -name = "wit-bindgen-rt" -version = "0.39.0" +name = "wit-parser" +version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" dependencies = [ - "bitflags", + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", ] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socket.rs b/src/tools/miri/tests/pass-dep/libc/libc-socket.rs index 8dd00e60200a5..35830d9bf0622 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socket.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socket.rs @@ -5,12 +5,17 @@ #[path = "../../utils/libc.rs"] mod libc_utils; -use std::io::{self, ErrorKind}; +#[path = "../../utils/mod.rs"] +mod utils; + +use std::io::ErrorKind; +use std::thread; use std::time::Duration; -#[allow(unused)] -use std::{mem::MaybeUninit, thread}; use libc_utils::*; +use utils::check_nondet; + +const TEST_BYTES: &[u8] = b"these are some test bytes!"; fn main() { test_socket_close(); @@ -29,10 +34,12 @@ fn main() { } test_bind_ipv4_invalid_addr_len(); test_bind_ipv6(); - test_listen(); test_accept_connect(); + test_send_peek_recv(); + test_partial_send_recv(); + test_write_read(); test_getsockname_ipv4(); test_getsockname_ipv4_random_port(); @@ -53,7 +60,7 @@ fn test_socket_close() { fn test_bind_ipv4() { let sockfd = unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; - let addr = net::ipv4_sock_addr(net::IPV4_LOCALHOST, 0); + let addr = net::sock_addr_ipv4(net::IPV4_LOCALHOST, 0); unsafe { errno_check(libc::bind( sockfd, @@ -67,8 +74,8 @@ fn test_bind_ipv4() { fn test_bind_ipv4_reuseaddr() { let sockfd = unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; - let addr = net::ipv4_sock_addr(net::IPV4_LOCALHOST, 0); - setsockopt(sockfd, libc::SOL_SOCKET, libc::SO_REUSEADDR, 1 as libc::c_int).unwrap(); + let addr = net::sock_addr_ipv4(net::IPV4_LOCALHOST, 0); + net::setsockopt(sockfd, libc::SOL_SOCKET, libc::SO_REUSEADDR, 1 as libc::c_int).unwrap(); unsafe { errno_check(libc::bind( sockfd, @@ -84,9 +91,9 @@ fn test_set_reuseaddr_invalid_len() { unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; // Value should be of type `libc::c_int` which has size 4 bytes. // By providing a u64 of size 8 bytes we trigger an invalid length error. - let err = setsockopt(sockfd, libc::SOL_SOCKET, libc::SO_REUSEADDR, 1u64).unwrap_err(); + let err = net::setsockopt(sockfd, libc::SOL_SOCKET, libc::SO_REUSEADDR, 1u64).unwrap_err(); assert_eq!(err.kind(), ErrorKind::InvalidInput); - // check that it is the right kind of `InvalidInput` + // Check that it is the right kind of `InvalidInput`. assert_eq!(err.raw_os_error(), Some(libc::EINVAL)); } @@ -101,8 +108,8 @@ fn test_set_reuseaddr_invalid_len() { fn test_bind_ipv4_nosigpipe() { let sockfd = unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; - let addr = net::ipv4_sock_addr(net::IPV4_LOCALHOST, 0); - setsockopt(sockfd, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1 as libc::c_int).unwrap(); + let addr = net::sock_addr_ipv4(net::IPV4_LOCALHOST, 0); + net::setsockopt(sockfd, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1 as libc::c_int).unwrap(); unsafe { errno_check(libc::bind( sockfd, @@ -124,7 +131,7 @@ fn test_set_nosigpipe_invalid_len() { unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; // Value should be of type `libc::c_int` which has size 4 bytes. // By providing a u64 of size 8 bytes we trigger an invalid length error. - let err = setsockopt(sockfd, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1u64).unwrap_err(); + let err = net::setsockopt(sockfd, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1u64).unwrap_err(); assert_eq!(err.kind(), ErrorKind::InvalidInput); // Check that it is the right kind of `InvalidInput`. assert_eq!(err.raw_os_error(), Some(libc::EINVAL)); @@ -135,7 +142,7 @@ fn test_set_nosigpipe_invalid_len() { fn test_bind_ipv4_invalid_addr_len() { let sockfd = unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; - let addr = net::ipv4_sock_addr(net::IPV4_LOCALHOST, 0); + let addr = net::sock_addr_ipv4(net::IPV4_LOCALHOST, 0); let err = unsafe { errno_result(libc::bind( sockfd, @@ -153,7 +160,7 @@ fn test_bind_ipv4_invalid_addr_len() { fn test_bind_ipv6() { let sockfd = unsafe { errno_result(libc::socket(libc::AF_INET6, libc::SOCK_STREAM, 0)).unwrap() }; - let addr = net::ipv6_sock_addr(net::IPV6_LOCALHOST, 0); + let addr = net::sock_addr_ipv6(net::IPV6_LOCALHOST, 0); unsafe { errno_check(libc::bind( sockfd, @@ -166,7 +173,7 @@ fn test_bind_ipv6() { fn test_listen() { let sockfd = unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; - let addr = net::ipv4_sock_addr(net::IPV4_LOCALHOST, 0); + let addr = net::sock_addr_ipv4(net::IPV4_LOCALHOST, 0); unsafe { errno_check(libc::bind( sockfd, @@ -186,44 +193,19 @@ fn test_listen() { /// - Connecting when the server is already accepting /// - Accepting when there is already an incoming connection fn test_accept_connect() { - let server_sockfd = - unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; + let (server_sockfd, addr) = net::make_listener_ipv4(0).unwrap(); let client_sockfd = unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; - let addr = net::ipv4_sock_addr(net::IPV4_LOCALHOST, 0); - unsafe { - errno_check(libc::bind( - server_sockfd, - (&addr as *const libc::sockaddr_in).cast::(), - size_of::() as libc::socklen_t, - )); - } - - unsafe { - errno_check(libc::listen(server_sockfd, 16)); - } - - // Retrieve actual listener address because we used a randomized port. - let (_, server_addr) = - sockname(|storage, len| unsafe { libc::getsockname(server_sockfd, storage, len) }).unwrap(); - - let LibcSocketAddr::V4(addr) = server_addr else { - // We bound an IPv4 address so we also expect - // an IPv4 address to be returned. - panic!() - }; // Spawn the server thread. let server_thread = thread::spawn(move || { - let (_peerfd, _peer_addr) = - sockname(|storage, len| unsafe { libc::accept(server_sockfd, storage, len) }).unwrap(); + net::accept_ipv4(server_sockfd).unwrap(); // Yield back to the client thread to test whether calling `connect` first also // works. thread::sleep(Duration::from_millis(10)); - let (_peerfd, _peer_addr) = - sockname(|storage, len| unsafe { libc::accept(server_sockfd, storage, len) }).unwrap(); + net::accept_ipv4(server_sockfd).unwrap(); }); // Yield to server thread to ensure `accept` is called before we try @@ -231,13 +213,7 @@ fn test_accept_connect() { thread::sleep(Duration::from_millis(10)); // Test connecting to an already accepting server. - unsafe { - errno_check(libc::connect( - client_sockfd, - (&addr as *const libc::sockaddr_in).cast::(), - size_of::() as libc::socklen_t, - )); - } + net::connect_ipv4(client_sockfd, addr); // Server thread should now be in its `sleep`. // Test connecting when there is no actively ongoing `accept`. @@ -245,13 +221,154 @@ fn test_accept_connect() { let client_sockfd = unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; - unsafe { - errno_check(libc::connect( + net::connect_ipv4(client_sockfd, addr); + + server_thread.join().unwrap(); +} + +/// Test sending bytes into a connected stream and then peeking and receiving +/// them from the other end. +/// We especially want to test that the peeking doesn't remove the bytes from +/// the queue. +fn test_send_peek_recv() { + let (server_sockfd, addr) = net::make_listener_ipv4(0).unwrap(); + let client_sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; + + // Spawn the server thread. + let server_thread = thread::spawn(move || { + let (peerfd, _) = net::accept_ipv4(server_sockfd).unwrap(); + + // Write the bytes into the stream. + let bytes_written = unsafe { + errno_result(libc_utils::net::send_all( + peerfd, + TEST_BYTES.as_ptr().cast(), + TEST_BYTES.len(), + 0, + )) + .unwrap() + }; + assert_eq!(bytes_written as usize, TEST_BYTES.len()); + }); + + net::connect_ipv4(client_sockfd, addr); + + let mut buffer = [0; TEST_BYTES.len()]; + let bytes_read = unsafe { + errno_result(libc::recv( client_sockfd, - (&addr as *const libc::sockaddr_in).cast::(), - size_of::() as libc::socklen_t, - )); - } + buffer.as_mut_ptr().cast(), + buffer.len(), + libc::MSG_PEEK, + )) + .unwrap() + } as usize; + + // We cannot have "peek all" since peeks don't remove the bytes from the buffer. + // Thus, we only check that the bytes we read are a prefix of the bytes we wrote. + assert_eq!(&buffer[..bytes_read], &TEST_BYTES[..bytes_read]); + + // Since the bytes aren't removed from the queue by peeking, we should be + // able to read the same bytes again into a new buffer. + + let mut buffer = [0; TEST_BYTES.len()]; + let bytes_read = unsafe { + errno_result(libc_utils::net::recv_all( + client_sockfd, + buffer.as_mut_ptr().cast(), + buffer.len(), + 0, + )) + .unwrap() + }; + + assert_eq!(bytes_read as usize, TEST_BYTES.len()); + assert_eq!(&buffer, TEST_BYTES); + + server_thread.join().unwrap(); +} + +/// Test that we actually do partial sends and partial receives for sockets. +fn test_partial_send_recv() { + let (server_sockfd, addr) = net::make_listener_ipv4(0).unwrap(); + let client_sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; + + // Spawn the server thread. + let server_thread = thread::spawn(move || { + let (peerfd, _) = net::accept_ipv4(server_sockfd).unwrap(); + + // Yield back to client to test that we do incomplete writes. + thread::sleep(Duration::from_millis(10)); + + // We know the buffer contains enough bytes to test incomplete reads. + + // Ensure we sometimes do incomplete reads. + check_nondet(|| { + let mut buffer = [0u8; 4]; + let bytes_read = + unsafe { errno_result(libc::read(peerfd, buffer.as_mut_ptr().cast(), 4)).unwrap() }; + bytes_read == 4 + }); + }); + + net::connect_ipv4(client_sockfd, addr); + + // Ensure we sometimes do incomplete writes. + check_nondet(|| { + let bytes_written = + unsafe { errno_result(libc::write(client_sockfd, [0; 4].as_ptr().cast(), 4)).unwrap() }; + bytes_written == 4 + }); + + let buffer = [0u8; 100_000]; + // Write a lot of bytes into the socket such that we can test + // incomplete reads. + let bytes_written = unsafe { + errno_result(libc_utils::write_all(client_sockfd, buffer.as_ptr().cast(), buffer.len())) + .unwrap() + }; + assert_eq!(bytes_written as usize, buffer.len()); + + server_thread.join().unwrap(); +} + +/// Test writing bytes into a connected stream and then reading them +/// from the other end. +/// We want to test this because `write` and `read` should be the same as +/// `send` and `recv` with zero flags. +fn test_write_read() { + let (server_sockfd, addr) = net::make_listener_ipv4(0).unwrap(); + let client_sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; + + // Spawn the server thread. + let server_thread = thread::spawn(move || { + let (peerfd, _) = net::accept_ipv4(server_sockfd).unwrap(); + + // Write some bytes into the stream. + let bytes_written = unsafe { + errno_result(libc_utils::write_all( + peerfd, + TEST_BYTES.as_ptr().cast(), + TEST_BYTES.len(), + )) + .unwrap() + }; + assert_eq!(bytes_written as usize, TEST_BYTES.len()); + }); + + net::connect_ipv4(client_sockfd, addr); + + let mut buffer = [0; TEST_BYTES.len()]; + let bytes_read = unsafe { + errno_result(libc_utils::read_all(client_sockfd, buffer.as_mut_ptr().cast(), buffer.len())) + .unwrap() + }; + + assert_eq!(bytes_read as usize, TEST_BYTES.len()); + assert_eq!(&buffer, TEST_BYTES); server_thread.join().unwrap(); } @@ -262,7 +379,7 @@ fn test_accept_connect() { fn test_getsockname_ipv4() { let sockfd = unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; - let addr = net::ipv4_sock_addr(net::IPV4_LOCALHOST, 6789); + let addr = net::sock_addr_ipv4(net::IPV4_LOCALHOST, 6789); unsafe { errno_check(libc::bind( sockfd, @@ -276,13 +393,8 @@ fn test_getsockname_ipv4() { } let (_, sock_addr) = - sockname(|storage, len| unsafe { libc::getsockname(sockfd, storage, len) }).unwrap(); - - let LibcSocketAddr::V4(sock_addr) = sock_addr else { - // We bound an IPv4 address so we also expect - // an IPv4 address to be returned. - panic!() - }; + net::sockname_ipv4(|storage, len| unsafe { libc::getsockname(sockfd, storage, len) }) + .unwrap(); assert_eq!(addr.sin_family, sock_addr.sin_family); assert_eq!(addr.sin_port, sock_addr.sin_port); @@ -297,7 +409,7 @@ fn test_getsockname_ipv4_random_port() { let sockfd = unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; // Use zero-port to let the OS choose a free port to bind to. - let addr = net::ipv4_sock_addr(net::IPV4_LOCALHOST, 0); + let addr = net::sock_addr_ipv4(net::IPV4_LOCALHOST, 0); unsafe { errno_check(libc::bind( sockfd, @@ -311,13 +423,9 @@ fn test_getsockname_ipv4_random_port() { } let (_, sock_addr) = - sockname(|storage, len| unsafe { libc::getsockname(sockfd, storage, len) }).unwrap(); + net::sockname_ipv4(|storage, len| unsafe { libc::getsockname(sockfd, storage, len) }) + .unwrap(); - let LibcSocketAddr::V4(sock_addr) = sock_addr else { - // We bound an IPv4 address so we also expect - // an IPv4 address to be returned. - panic!() - }; assert_eq!(addr.sin_family, sock_addr.sin_family); // The bound port must not be the zero port. assert!(sock_addr.sin_port > 0); @@ -331,15 +439,11 @@ fn test_getsockname_ipv4_unbound() { unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; let (_, sock_addr) = - sockname(|storage, len| unsafe { libc::getsockname(sockfd, storage, len) }).unwrap(); + net::sockname_ipv4(|storage, len| unsafe { libc::getsockname(sockfd, storage, len) }) + .unwrap(); // Libc representation of an unspecified IPv4 address with zero port. - let addr = net::ipv4_sock_addr([0, 0, 0, 0], 0); - let LibcSocketAddr::V4(sock_addr) = sock_addr else { - // We bound an IPv4 address so we also expect - // an IPv4 address to be returned. - panic!() - }; + let addr = net::sock_addr_ipv4([0, 0, 0, 0], 0); assert_eq!(addr.sin_family, sock_addr.sin_family); assert_eq!(addr.sin_port, sock_addr.sin_port); @@ -352,7 +456,7 @@ fn test_getsockname_ipv4_unbound() { fn test_getsockname_ipv6() { let sockfd = unsafe { errno_result(libc::socket(libc::AF_INET6, libc::SOCK_STREAM, 0)).unwrap() }; - let addr = net::ipv6_sock_addr(net::IPV6_LOCALHOST, 1234); + let addr = net::sock_addr_ipv6(net::IPV6_LOCALHOST, 1234); unsafe { errno_check(libc::bind( sockfd, @@ -366,13 +470,8 @@ fn test_getsockname_ipv6() { } let (_, sock_addr) = - sockname(|storage, len| unsafe { libc::getsockname(sockfd, storage, len) }).unwrap(); - - let LibcSocketAddr::V6(sock_addr) = sock_addr else { - // We bound an IPv6 address so we also expect - // an IPv6 address to be returned. - panic!() - }; + net::sockname_ipv6(|storage, len| unsafe { libc::getsockname(sockfd, storage, len) }) + .unwrap(); assert_eq!(addr.sin6_family, sock_addr.sin6_family); assert_eq!(addr.sin6_port, sock_addr.sin6_port); @@ -385,56 +484,19 @@ fn test_getsockname_ipv6() { /// For a connected socket, the `getpeername` syscall should /// return the same address as the socket was connected to. fn test_getpeername_ipv4() { - let server_sockfd = - unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; + let (server_sockfd, addr) = net::make_listener_ipv4(0).unwrap(); let client_sockfd = unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; - let addr = net::ipv4_sock_addr(net::IPV4_LOCALHOST, 0); - unsafe { - errno_check(libc::bind( - server_sockfd, - (&addr as *const libc::sockaddr_in).cast::(), - size_of::() as libc::socklen_t, - )); - } - - unsafe { - errno_check(libc::listen(server_sockfd, 16)); - } - - // Retrieve actual listener address because we used a randomized port. - let (_, server_addr) = - sockname(|storage, len| unsafe { libc::getsockname(server_sockfd, storage, len) }).unwrap(); - - let LibcSocketAddr::V4(addr) = server_addr else { - // We bound an IPv4 address so we also expect - // an IPv4 address to be returned. - panic!() - }; // Spawn the server thread. - let server_thread = thread::spawn(move || { - let (_peerfd, _peer_addr) = - sockname(|storage, len| unsafe { libc::accept(server_sockfd, storage, len) }).unwrap(); - }); - - // Test connecting to an already accepting server. - unsafe { - errno_check(libc::connect( - client_sockfd, - (&addr as *const libc::sockaddr_in).cast::(), - size_of::() as libc::socklen_t, - )); - } + let server_thread = thread::spawn(move || net::accept_ipv4(server_sockfd).unwrap()); - let (_, peer_addr) = - sockname(|storage, len| unsafe { libc::getpeername(client_sockfd, storage, len) }).unwrap(); + net::connect_ipv4(client_sockfd, addr); - let LibcSocketAddr::V4(peer_addr) = peer_addr else { - // We connected to an IPv4 address so we also expect - // an IPv4 address to be returned. - panic!() - }; + let (_, peer_addr) = net::sockname_ipv4(|storage, len| unsafe { + libc::getpeername(client_sockfd, storage, len) + }) + .unwrap(); assert_eq!(addr.sin_family, peer_addr.sin_family); assert_eq!(addr.sin_port, peer_addr.sin_port); @@ -447,56 +509,19 @@ fn test_getpeername_ipv4() { /// For a connected socket, the `getpeername` syscall should /// return the same address as the socket was connected to. fn test_getpeername_ipv6() { - let server_sockfd = - unsafe { errno_result(libc::socket(libc::AF_INET6, libc::SOCK_STREAM, 0)).unwrap() }; + let (server_sockfd, addr) = net::make_listener_ipv6(0).unwrap(); let client_sockfd = unsafe { errno_result(libc::socket(libc::AF_INET6, libc::SOCK_STREAM, 0)).unwrap() }; - let addr = net::ipv6_sock_addr(net::IPV6_LOCALHOST, 0); - unsafe { - errno_check(libc::bind( - server_sockfd, - (&addr as *const libc::sockaddr_in6).cast::(), - size_of::() as libc::socklen_t, - )); - } - - unsafe { - errno_check(libc::listen(server_sockfd, 16)); - } - - // Retrieve actual listener address because we used a randomized port. - let (_, server_addr) = - sockname(|storage, len| unsafe { libc::getsockname(server_sockfd, storage, len) }).unwrap(); - - let LibcSocketAddr::V6(addr) = server_addr else { - // We bound an IPv6 address so we also expect - // an IPv6 address to be returned. - panic!() - }; // Spawn the server thread. - let server_thread = thread::spawn(move || { - let (_peerfd, _peer_addr) = - sockname(|storage, len| unsafe { libc::accept(server_sockfd, storage, len) }).unwrap(); - }); + let server_thread = thread::spawn(move || net::accept_ipv6(server_sockfd).unwrap()); - // Test connecting to an already accepting server. - unsafe { - errno_check(libc::connect( - client_sockfd, - (&addr as *const libc::sockaddr_in6).cast::(), - size_of::() as libc::socklen_t, - )); - } + net::connect_ipv6(client_sockfd, addr); - let (_, peer_addr) = - sockname(|storage, len| unsafe { libc::getpeername(client_sockfd, storage, len) }).unwrap(); - - let LibcSocketAddr::V6(peer_addr) = peer_addr else { - // We connected to an IPv6 address so we also expect - // an IPv6 address to be returned. - panic!() - }; + let (_, peer_addr) = net::sockname_ipv6(|storage, len| unsafe { + libc::getpeername(client_sockfd, storage, len) + }) + .unwrap(); assert_eq!(addr.sin6_family, peer_addr.sin6_family); assert_eq!(addr.sin6_port, peer_addr.sin6_port); @@ -506,65 +531,3 @@ fn test_getpeername_ipv6() { server_thread.join().unwrap(); } - -/// Set a socket option. It's the caller's responsibility to ensure that `T` is -/// associated with the given socket option. -/// -/// This function is directly copied from the standard library implementation -/// for sockets on UNIX targets. -fn setsockopt( - sockfd: i32, - level: libc::c_int, - option_name: libc::c_int, - option_value: T, -) -> io::Result<()> { - let option_len = size_of::() as libc::socklen_t; - - errno_result(unsafe { - libc::setsockopt( - sockfd, - level, - option_name, - (&raw const option_value) as *const _, - option_len, - ) - })?; - Ok(()) -} - -enum LibcSocketAddr { - V4(libc::sockaddr_in), - V6(libc::sockaddr_in6), -} - -/// Wraps a call to a platform function that returns a socket address. -/// This is very much the same as the function with the same name in the -/// standard library implementation. -/// Returns a tuple containing the actual return value of the performed -/// syscall and the written address of it. -fn sockname(f: F) -> io::Result<(libc::c_int, LibcSocketAddr)> -where - F: FnOnce(*mut libc::sockaddr, *mut libc::socklen_t) -> libc::c_int, -{ - let mut storage = MaybeUninit::::zeroed(); - let mut len = size_of::() as libc::socklen_t; - let value = errno_result(f(storage.as_mut_ptr().cast(), &mut len))?; - // SAFETY: - // The caller guarantees that the storage has been successfully initialized - // and its size written to `len` if `f` returns a success. - let address = unsafe { - match (*storage.as_ptr()).ss_family as libc::c_int { - libc::AF_INET => { - assert!(len as usize >= size_of::()); - LibcSocketAddr::V4(*(storage.as_ptr() as *const _ as *const libc::sockaddr_in)) - } - libc::AF_INET6 => { - assert!(len as usize >= size_of::()); - LibcSocketAddr::V6(*(storage.as_ptr() as *const _ as *const libc::sockaddr_in6)) - } - _ => return Err(io::Error::new(ErrorKind::InvalidInput, "invalid argument")), - } - }; - - Ok((value, address)) -} diff --git a/src/tools/miri/tests/pass/shims/socket.rs b/src/tools/miri/tests/pass/shims/socket.rs index 852397a356916..ec2cc76700540 100644 --- a/src/tools/miri/tests/pass/shims/socket.rs +++ b/src/tools/miri/tests/pass/shims/socket.rs @@ -1,13 +1,18 @@ //@ignore-target: windows # No libc socket on Windows //@compile-flags: -Zmiri-disable-isolation +use std::io::{Read, Write}; use std::net::{TcpListener, TcpStream}; use std::thread; +const TEST_BYTES: &[u8] = b"these are some test bytes!"; + fn main() { test_create_ipv4_listener(); test_create_ipv6_listener(); test_accept_and_connect(); + test_read_write(); + test_peek(); test_peer_addr(); } @@ -36,6 +41,60 @@ fn test_accept_and_connect() { handle.join().unwrap(); } +/// Test reading and writing into two connected sockets and ensuring +/// that the other side receives the correct bytes. +fn test_read_write() { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + // Get local address with randomized port to know where + // we need to connect to. + let address = listener.local_addr().unwrap(); + + let handle = thread::spawn(move || { + let (mut stream, _addr) = listener.accept().unwrap(); + stream.write_all(TEST_BYTES).unwrap(); + }); + + let mut stream = TcpStream::connect(address).unwrap(); + + let mut buffer = [0; TEST_BYTES.len()]; + stream.read_exact(&mut buffer).unwrap(); + assert_eq!(&buffer, TEST_BYTES); + + handle.join().unwrap(); +} + +/// Test that peeking on a receiving socket doesn't remove the bytes +/// from the queue. +fn test_peek() { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + // Get local address with randomized port to know where + // we need to connect to. + let address = listener.local_addr().unwrap(); + + let handle = thread::spawn(move || { + let (mut stream, _addr) = listener.accept().unwrap(); + stream.write_all(TEST_BYTES).unwrap(); + }); + + let mut stream = TcpStream::connect(address).unwrap(); + + let mut buffer = [0; TEST_BYTES.len()]; + let bytes_peeked = stream.peek(&mut buffer).unwrap(); + // Since we have short accesses, and there is nothing like an "peek exact", + // we can only ensure that we peeked at most as many bytes as we wrote into + // the buffer and that the peeked bytes match the ones we wrote. + assert!(bytes_peeked <= TEST_BYTES.len()); + assert_eq!(&buffer[0..bytes_peeked], &TEST_BYTES[0..bytes_peeked]); + + // Since the peek shouldn't remove the bytes from the queue, + // we should be able to read them again. + let mut buffer = [0; TEST_BYTES.len()]; + stream.read_exact(&mut buffer).unwrap(); + assert_eq!(&buffer, TEST_BYTES); + + handle.join().unwrap(); +} + /// Test whether the [`TcpStream::peer_addr`] of a connected socket /// is the same address as the one the stream was connected to. fn test_peer_addr() { diff --git a/src/tools/miri/tests/utils/libc.rs b/src/tools/miri/tests/utils/libc.rs index ef22d3af5425e..866b576edffd3 100644 --- a/src/tools/miri/tests/utils/libc.rs +++ b/src/tools/miri/tests/utils/libc.rs @@ -159,13 +159,17 @@ pub mod epoll { } pub mod net { + use std::io; + + use super::{errno_check, errno_result}; + /// IPv4 localhost address bytes pub const IPV4_LOCALHOST: [u8; 4] = [127, 0, 0, 1]; /// IPv6 localhost address bytes pub const IPV6_LOCALHOST: [u8; 16] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]; /// Create a libc representation of an IPv4 address given the address bytes and a port. - pub fn ipv4_sock_addr(addr_bytes: [u8; 4], port: u16) -> libc::sockaddr_in { + pub fn sock_addr_ipv4(addr_bytes: [u8; 4], port: u16) -> libc::sockaddr_in { libc::sockaddr_in { sin_family: libc::AF_INET as libc::sa_family_t, sin_port: port.to_be(), @@ -181,13 +185,13 @@ pub mod net { /// Create a libc representation of an IPv6 address given the address bytes and a port. /// /// This method sets `flowinfo` and `scope_id` to 0. - pub fn ipv6_sock_addr(addr_bytes: [u8; 16], port: u16) -> libc::sockaddr_in6 { - ipv6_sock_addr_full(addr_bytes, port, 0, 0) + pub fn sock_addr_ipv6(addr_bytes: [u8; 16], port: u16) -> libc::sockaddr_in6 { + sock_addr_full_ipv6(addr_bytes, port, 0, 0) } /// Create a libc representation of a full IPv6 address given the address bytes, a port /// as well as a flowinfo and scope id. - pub fn ipv6_sock_addr_full( + pub fn sock_addr_full_ipv6( addr_bytes: [u8; 16], port: u16, flowinfo: u32, @@ -204,4 +208,221 @@ pub mod net { ..unsafe { core::mem::zeroed() } } } + + /// Create an IPv4 TCP socket which listens on a random port at the localhost address. + /// Returns the socket file descriptor and the actual socket address the socket is listening on. + pub fn make_listener_ipv4( + options: libc::c_int, + ) -> io::Result<(libc::c_int, libc::sockaddr_in)> { + let sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM | options, 0))? }; + // Turn address into socket address with a random free port. + let addr = sock_addr_ipv4(IPV4_LOCALHOST, 0); + unsafe { + errno_result(libc::bind( + sockfd, + (&addr as *const libc::sockaddr_in).cast::(), + size_of::() as libc::socklen_t, + ))?; + } + + unsafe { + errno_result(libc::listen(sockfd, 16))?; + } + + // Retrieve actual listener address because we used a randomized port. + let (_, addr_with_port) = + sockname_ipv4(|storage, len| unsafe { libc::getsockname(sockfd, storage, len) })?; + + Ok((sockfd, addr_with_port)) + } + + /// Create an IPv6 TCP socket which listens on a random port at the localhost address. + /// Returns the socket file descriptor and the actual socket address the socket is listening on. + pub fn make_listener_ipv6( + options: libc::c_int, + ) -> io::Result<(libc::c_int, libc::sockaddr_in6)> { + let sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET6, libc::SOCK_STREAM | options, 0))? }; + // Turn address into socket address with a random free port. + let addr = sock_addr_ipv6(IPV6_LOCALHOST, 0); + unsafe { + errno_result(libc::bind( + sockfd, + (&addr as *const libc::sockaddr_in6).cast::(), + size_of::() as libc::socklen_t, + ))?; + } + + unsafe { + errno_result(libc::listen(sockfd, 16))?; + } + + // Retrieve actual listener address because we used a randomized port. + let (_, addr_with_port) = + sockname_ipv6(|storage, len| unsafe { libc::getsockname(sockfd, storage, len) })?; + + Ok((sockfd, addr_with_port)) + } + + /// Accept an incoming IPv4 connection. + pub fn accept_ipv4(sockfd: libc::c_int) -> io::Result<(libc::c_int, libc::sockaddr_in)> { + sockname_ipv4(|storage, len| unsafe { libc::accept(sockfd, storage, len) }) + } + + /// Accept an incoming IPv6 connection. + pub fn accept_ipv6(sockfd: libc::c_int) -> io::Result<(libc::c_int, libc::sockaddr_in6)> { + sockname_ipv6(|storage, len| unsafe { libc::accept(sockfd, storage, len) }) + } + + /// Connect the socket to the specified IPv4 address. + pub fn connect_ipv4(sockfd: libc::c_int, addr: libc::sockaddr_in) { + unsafe { + errno_check(libc::connect( + sockfd, + (&addr as *const libc::sockaddr_in).cast(), + size_of::() as libc::socklen_t, + )); + } + } + + /// Connect the socket to the specified IPv6 address. + pub fn connect_ipv6(sockfd: libc::c_int, addr: libc::sockaddr_in6) { + unsafe { + errno_check(libc::connect( + sockfd, + (&addr as *const libc::sockaddr_in6).cast(), + size_of::() as libc::socklen_t, + )); + } + } + + /// Set a socket option. It's the caller's responsibility to ensure that `T` is + /// associated with the given socket option. + /// + /// This function is directly copied from the standard library implementation + /// for sockets on UNIX targets. + pub fn setsockopt( + sockfd: i32, + level: libc::c_int, + option_name: libc::c_int, + option_value: T, + ) -> io::Result<()> { + let option_len = size_of::() as libc::socklen_t; + + errno_result(unsafe { + libc::setsockopt( + sockfd, + level, + option_name, + (&raw const option_value) as *const _, + option_len, + ) + })?; + Ok(()) + } + + /// Wraps a call to a platform function that returns an IPv4 socket address. + /// Returns a tuple containing the actual return value of the performed + /// syscall and the written address of it. + pub fn sockname_ipv4(f: F) -> io::Result<(libc::c_int, libc::sockaddr_in)> + where + F: FnOnce(*mut libc::sockaddr, *mut libc::socklen_t) -> libc::c_int, + { + let (result, addr) = sockname(f)?; + let LibcSocketAddr::V4(addr) = addr else { panic!("expected IPv4 address") }; + + Ok((result, addr)) + } + + /// Wraps a call to a platform function that returns an IPv6 socket address. + /// Returns a tuple containing the actual return value of the performed + /// syscall and the written address of it. + pub fn sockname_ipv6(f: F) -> io::Result<(libc::c_int, libc::sockaddr_in6)> + where + F: FnOnce(*mut libc::sockaddr, *mut libc::socklen_t) -> libc::c_int, + { + let (result, addr) = sockname(f)?; + let LibcSocketAddr::V6(addr) = addr else { panic!("expected IPv6 address") }; + + Ok((result, addr)) + } + + enum LibcSocketAddr { + V4(libc::sockaddr_in), + V6(libc::sockaddr_in6), + } + + /// Wraps a call to a platform function that returns a socket address. + /// This is very much the same as the function with the same name in the + /// standard library implementation. + /// Returns a tuple containing the actual return value of the performed + /// syscall and the written address of it. + fn sockname(f: F) -> io::Result<(libc::c_int, LibcSocketAddr)> + where + F: FnOnce(*mut libc::sockaddr, *mut libc::socklen_t) -> libc::c_int, + { + let mut storage = std::mem::MaybeUninit::::zeroed(); + let mut len = size_of::() as libc::socklen_t; + let value = errno_result(f(storage.as_mut_ptr().cast(), &mut len))?; + // SAFETY: + // The caller guarantees that the storage has been successfully initialized + // and its size written to `len` if `f` returns a success. + let address = unsafe { + match (*storage.as_ptr()).ss_family as libc::c_int { + libc::AF_INET => { + assert!(len as usize >= size_of::()); + LibcSocketAddr::V4(*(storage.as_ptr() as *const _ as *const libc::sockaddr_in)) + } + libc::AF_INET6 => { + assert!(len as usize >= size_of::()); + LibcSocketAddr::V6(*(storage.as_ptr() as *const _ as *const libc::sockaddr_in6)) + } + _ => return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid argument")), + } + }; + + Ok((value, address)) + } + + pub unsafe fn recv_all( + fd: libc::c_int, + buf: *mut libc::c_void, + count: libc::size_t, + flags: libc::c_int, + ) -> libc::ssize_t { + assert!(count > 0); + let mut read_so_far = 0; + while read_so_far < count { + let res = libc::recv(fd, buf.add(read_so_far), count - read_so_far, flags); + if res < 0 { + return res; + } + if res == 0 { + // EOF + break; + } + read_so_far += res as libc::size_t; + } + return read_so_far as libc::ssize_t; + } + + pub unsafe fn send_all( + fd: libc::c_int, + buf: *const libc::c_void, + count: libc::size_t, + flags: libc::c_int, + ) -> libc::ssize_t { + assert!(count > 0); + let mut written_so_far = 0; + while written_so_far < count { + let res = libc::send(fd, buf.add(written_so_far), count - written_so_far, flags); + if res < 0 { + return res; + } + // Apparently a return value of 0 is just a short write, nothing special (unlike reads). + written_so_far += res as libc::size_t; + } + return written_so_far as libc::ssize_t; + } }