diff --git a/src/shims/unix/foreign_items.rs b/src/shims/unix/foreign_items.rs index cae8e314db..8a58374c68 100644 --- a/src/shims/unix/foreign_items.rs +++ b/src/shims/unix/foreign_items.rs @@ -684,14 +684,28 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { shim_sig!(extern "C" fn(i32, *_, libc::size_t, i32) -> libc::ssize_t), (link_name, abi, args), )?; - this.send(socket, buffer, length, flags, dest)?; + this.send(socket, buffer, length, flags, /* dest_addr */ None, dest)?; + } + "sendto" => { + let [socket, buffer, length, flags, dest_addr, dest_addr_len] = &this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *_, libc::size_t, i32, *libc::sockaddr, libc::socklen_t) -> libc::ssize_t), + (link_name, abi, args), + )?; + this.send(socket, buffer, length, flags, Some((dest_addr, dest_addr_len)), dest)?; } "recv" => { let [socket, buffer, length, flags] = &this.check_shim_sig( shim_sig!(extern "C" fn(i32, *_, libc::size_t, i32) -> libc::ssize_t), (link_name, abi, args), )?; - this.recv(socket, buffer, length, flags, dest)?; + this.recv(socket, buffer, length, flags, /* dest_addr */ None, dest)?; + } + "recvfrom" => { + let [socket, buffer, length, flags, dest_addr, dest_addr_len] = &this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *_, libc::size_t, i32, *libc::sockaddr, *libc::socklen_t) -> libc::ssize_t), + (link_name, abi, args), + )?; + this.recv(socket, buffer, length, flags, Some((dest_addr, dest_addr_len)), dest)?; } "setsockopt" => { let [socket, level, option_name, option_value, option_len] = &this.check_shim_sig( diff --git a/src/shims/unix/socket.rs b/src/shims/unix/socket.rs index 7ce0b0613d..185646c16e 100644 --- a/src/shims/unix/socket.rs +++ b/src/shims/unix/socket.rs @@ -65,12 +65,15 @@ pub trait UnixSocketFileDescription: UnixFileDescription { throw_unsup_format!("cannot connect {}", self.name()); } - /// Receive data on the socket into the given buffer `ptr`. + /// Receive data on the socket into the given buffer `ptr` and return the socket + /// address from where the data originated. /// `len` indicates how many bytes we should try to receive. /// `is_peek` specifies whether the receive removes the bytes from the receive buffer /// ([`false`]) or leaves them in the receive buffer ([`true`]). /// `is_non_block` specifies whether the receive operation is non-blocking. - /// After a successful receive, `finish` should be called with the amount of bytes received. + /// After a successful receive, `finish` should be called with a tuple containing the + /// amount of bytes received and the socket address from where the data originated, should + /// it be provided by the protocol. fn recv<'tcx>( self: FileDescriptionRef, _communicate_allowed: bool, @@ -79,7 +82,7 @@ pub trait UnixSocketFileDescription: UnixFileDescription { _is_peek: bool, _is_non_block: bool, _ecx: &mut MiriInterpCx<'tcx>, - _finish: DynMachineCallback<'tcx, Result>, + _finish: DynMachineCallback<'tcx, Result<(usize, Option), IoError>>, ) -> InterpResult<'tcx> { throw_unsup_format!("cannot receive from {}", self.name()); } @@ -87,6 +90,8 @@ pub trait UnixSocketFileDescription: UnixFileDescription { /// Send data from the given buffer `ptr` into the socket. /// `len` indicates how many bytes we should try to send. /// `is_non_block` specifies whether the send operation is non-blocking. + /// `dest_addr` optionally contains a socket address to which the data + /// should be sent instead of the default destination address. /// After a successful send, `finish` should be called with the amount of bytes sent. fn send<'tcx>( self: FileDescriptionRef, @@ -94,6 +99,7 @@ pub trait UnixSocketFileDescription: UnixFileDescription { _ptr: Pointer, _len: usize, _is_non_block: bool, + _dest_addr: Option, _ecx: &mut MiriInterpCx<'tcx>, _finish: DynMachineCallback<'tcx, Result>, ) -> InterpResult<'tcx> { @@ -429,7 +435,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // We only attempt a write if the address pointer is not a null pointer. // If the address pointer is a null pointer the user isn't interested in the // address and we don't need to write anything. - ecx.write_socket_address(&address, address_ptr, &address_len_place)?; + ecx.write_socket_address(Some(&address), address_ptr, &address_len_place)?; } // We need to create the scalar using the destination size since @@ -492,6 +498,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { buffer: &OpTy<'tcx>, length: &OpTy<'tcx>, flags: &OpTy<'tcx>, + dest_addr: Option<(&OpTy<'tcx>, &OpTy<'tcx>)>, // Location where the output scalar is written to. dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx> { @@ -504,6 +511,33 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.read_scalar(length)?.to_uint(size_layout.size)?.try_into().unwrap(); let mut flags = this.read_scalar(flags)?.to_i32()?; + let dest_addr = match dest_addr { + Some((dest_addr, dest_addr_len)) => { + let dest_addr_ptr = this.read_pointer(dest_addr)?; + let socklen_layout = this.libc_ty_layout("socklen_t"); + let dest_addr_len_int = + this.read_scalar(dest_addr_len)?.to_uint(socklen_layout.size)?; + + if dest_addr_ptr == Pointer::null() || dest_addr_len_int == 0 { + // If the address pointer is a nullptr or the address length is zero we ignore + // the destination address. BSDs return EINVAL when the address length is zero + // and the address pointer is an invalid pointer; that is so far away from what + // POSIX documents for TCP sockets that we do not try to emulate it. + None + } else { + // If they are both non-null/zero, we read the address even if the socket does + // not care about it. This is stricter than what POSIX says (POSIX says the + // destination is ignored for TCP sockets), but most OSes seem to do something + // similar. + match this.read_socket_address(dest_addr, dest_addr_len, "sendto")? { + Ok(dest_addr) => Some(dest_addr), + Err(e) => return this.set_errno_and_return_neg1(e, dest), + } + } + } + None => None, + }; + // Get the file handle let Some(fd) = this.machine.fds.get(socket) else { return this.set_errno_and_return_neg1(LibcError("EBADF"), dest); @@ -550,6 +584,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { buffer_ptr, length, is_non_block, + dest_addr, this, callback!( @capture<'tcx> { @@ -571,6 +606,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { buffer: &OpTy<'tcx>, length: &OpTy<'tcx>, flags: &OpTy<'tcx>, + dest_addr: Option<(&OpTy<'tcx>, &OpTy<'tcx>)>, // Location where the output scalar is written to. dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx> { @@ -583,6 +619,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.read_scalar(length)?.to_uint(size_layout.size)?.try_into().unwrap(); let mut flags = this.read_scalar(flags)?.to_i32()?; + let dest_addr = match dest_addr { + Some((addr, addr_len)) => + Some((this.read_pointer(addr)?, this.deref_pointer(addr_len)?)), + None => None, + }; + // Get the file handle let Some(fd) = this.machine.fds.get(socket) else { return this.set_errno_and_return_neg1(LibcError("EBADF"), dest); @@ -645,13 +687,29 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this, callback!( @capture<'tcx> { + dest_addr: Option<(Pointer, MPlaceTy<'tcx>)>, dest: MPlaceTy<'tcx>, - } |ecx, result: Result| { - match result { - Ok(bytes_sent) => - ecx.write_scalar(Scalar::from_target_usize(bytes_sent.try_into().unwrap(), ecx), &dest), - Err(e) => ecx.set_errno_and_return_neg1(e, &dest) + } |ecx, result: Result<(usize, Option), IoError>| { + let (bytes_received, mut origin_addr_opt) = match result { + Ok(data) => data, + Err(e) => return ecx.set_errno_and_return_neg1(e, &dest) + }; + + if let Some((addr_ptr, addr_len_mplace)) = dest_addr { + if addr_ptr == Pointer::null() { + // When the `addr_ptr` is a null pointer the caller is not interested in + // the socket address. + origin_addr_opt = None; + } + // The recvfrom(3) man page specifies that "[...] the value stored in the object pointed to + // `address` is unspecified [...]" should the protocol not provide the source address of the message. + // In this case we do the same thing as the Linux implementation and simply don't write anything to the + // `addr_ptr` pointer and zero the `addr_len_mplace` place. + ecx.write_socket_address(origin_addr_opt.as_ref(), addr_ptr, &addr_len_mplace)?; } + + ecx.write_scalar(Scalar::from_target_usize(bytes_received.try_into().unwrap(), ecx), &dest) + } ), ) @@ -794,7 +852,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Err(e) => return this.set_errno_and_return_neg1_i32(e), }; - this.write_socket_address(&address, address_ptr, &address_len_place) + this.write_socket_address(Some(&address), address_ptr, &address_len_place) .map(|()| Scalar::from_i32(0)) } @@ -838,11 +896,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; ecx.write_socket_address( - &address, + Some(&address), address_ptr, &address_len_place, )?; - ecx.write_scalar(Scalar::from_i32(0), &dest) + ecx.write_scalar(Scalar::from_i32(0), &dest) } ), ) diff --git a/src/shims/unix/socket_address.rs b/src/shims/unix/socket_address.rs index 411ceaad75..64ec52e2e5 100644 --- a/src/shims/unix/socket_address.rs +++ b/src/shims/unix/socket_address.rs @@ -137,9 +137,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // size of a layout is also stored in a u64. let address_len: u64 = this.read_scalar(address_len)?.to_int(address_len.layout.size)?.try_into().unwrap(); + let address = this.deref_pointer(address)?; + + // Ensure the pointer is actually dereferenceable for that length. + this.check_ptr_access( + address.ptr(), + Size::from_bytes(address_len), + CheckInAllocMsg::MemoryAccess, + )?; // Initially, treat address as generic sockaddr just to extract the family field. - let address = this.deref_pointer(address)?; if address_len < address.layout.size.bytes() { // Address length should be at least as big as the generic sockaddr return interp_ok(Err(LibcError("EINVAL"))); @@ -244,16 +251,31 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// on output it points to the non-truncated size of the written address in the /// buffer pointed to by `address_ptr`. /// + /// If `address` is [`None`] we write an "empty" address (i.e., the `address_len_ptr` + /// is set to zero and the `address_ptr` is left untouched). + /// If `address` is `Some`, both output pointers must be valid! + /// /// If the address buffer doesn't fit the whole address, the address is truncated to not /// overflow the buffer. fn write_socket_address( &mut self, - address: &SocketAddr, + address: Option<&SocketAddr>, address_ptr: Pointer, address_len_place: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); + if address.is_none() && this.ptr_is_null(address_len_place.ptr())? { + // It is okay for the len ptr to be null *if* we have nothing to write. + return interp_ok(()); + } + + // If the len ptr is not null or if we have an address to write, the length must be valid. + // This is intended to implement what POSIX says about the length pointer in `recvfrom``: + // > Either a null pointer, if address is a null pointer, or a pointer to a socklen_t object + // > which on input specifies the length of the supplied sockaddr structure, and on output + // > specifies the length of the sending address. + // We only support buffer lengths which can be stored in a u64 since the // size of a layout in bytes is also stored in a u64. let address_buffer_len: u64 = this @@ -262,6 +284,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { .try_into() .unwrap(); + // Check that the buffer indeed has (at least) the given size. + this.check_ptr_access( + address_ptr, + Size::from_bytes(address_buffer_len), + CheckInAllocMsg::MemoryAccess, + )?; + + let Some(address) = address else { + // We want to write an "empty" socket address. In this case we just set + // the address length to zero. + return this.write_int(0, address_len_place); + }; + let address_buffer = match address { SocketAddr::V4(address) => { // IPv4 address bytes; already stored in network byte order. @@ -446,7 +481,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_int(sockaddr_layout.size.bytes(), &addrlen_mplace)?; let sockaddr_mplace = this.allocate(sockaddr_layout, MiriMemoryKind::Machine.into())?; - this.write_socket_address(&address, sockaddr_mplace.ptr(), &addrlen_mplace)?; + this.write_socket_address(Some(&address), sockaddr_mplace.ptr(), &addrlen_mplace)?; this.write_pointer(sockaddr_mplace.ptr(), &addr_mplace)?; let canonname_mplace = this.project_field_named(&addrinfo_mplace, "ai_canonname")?; diff --git a/src/shims/unix/solarish/foreign_items.rs b/src/shims/unix/solarish/foreign_items.rs index 055d710f2e..3628ec259a 100644 --- a/src/shims/unix/solarish/foreign_items.rs +++ b/src/shims/unix/solarish/foreign_items.rs @@ -132,6 +132,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )?; this.connect(socket, address, address_len, dest)?; } + "__xnet_sendto" => { + let [socket, buffer, length, flags, dest_addr, dest_addr_len] = &this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *_, libc::size_t, i32, *libc::sockaddr, libc::socklen_t) -> libc::ssize_t), + (link_name, abi, args), + )?; + this.send(socket, buffer, length, flags, Some((dest_addr, dest_addr_len)), dest)?; + } "__xnet_getaddrinfo" => { let [node, service, hints, res] = &this.check_shim_sig( shim_sig!(extern "C" fn(*_, *_, *_, **_) -> i32), diff --git a/src/shims/unix/tcp_socket.rs b/src/shims/unix/tcp_socket.rs index 7e24256b6c..69d88f077f 100644 --- a/src/shims/unix/tcp_socket.rs +++ b/src/shims/unix/tcp_socket.rs @@ -170,7 +170,13 @@ impl FileDescription for TcpSocket { /* is_peek */ false, /* is_non_block */ false, ecx, - finish, + callback!( + @capture<'tcx> { + finish: DynMachineCallback<'tcx, Result>, + } |this, result: Result<(usize, Option), IoError>| { + finish.call(this, result.map(|(bytes, _)| bytes)) + } + ), ) } @@ -182,7 +188,15 @@ impl FileDescription for TcpSocket { ecx: &mut MiriInterpCx<'tcx>, finish: DynMachineCallback<'tcx, Result>, ) -> InterpResult<'tcx> { - self.send(communicate_allowed, ptr, len, /* is_non_block */ false, ecx, finish) + self.send( + communicate_allowed, + ptr, + len, + /* is_non_block */ false, + /* dest_addr */ None, + ecx, + finish, + ) } fn short_fd_operations(&self) -> bool { @@ -585,6 +599,9 @@ impl UnixSocketFileDescription for TcpSocket { ptr: Pointer, len: usize, is_non_block: bool, + // For stream-based sockets `send` and `sendto` are equivalent and + // we can thus ignore the destination address. + _dest_addr: Option, ecx: &mut MiriInterpCx<'tcx>, finish: DynMachineCallback<'tcx, Result>, ) -> InterpResult<'tcx> { @@ -634,13 +651,21 @@ impl UnixSocketFileDescription for TcpSocket { is_peek: bool, is_non_block: bool, ecx: &mut MiriInterpCx<'tcx>, - finish: DynMachineCallback<'tcx, Result>, + finish: DynMachineCallback<'tcx, Result<(usize, Option), IoError>>, ) -> InterpResult<'tcx> { assert!(communicate_allowed, "cannot have `TcpSocket` with isolation enabled!"); let is_non_block = is_non_block || self.is_non_block.get(); let deadline = ecx.action_deadline(is_non_block, self.read_timeout.get()); + let finish = callback!( + @capture<'tcx> { + finish: DynMachineCallback<'tcx, Result<(usize, Option), IoError>>, + } |this, result: Result| { + // The source address of the data isn't available for stream-based sockets. + finish.call(this, result.map(|bytes| (bytes, None))) + } + ); let socket = self; ecx.ensure_connected( socket.clone(), diff --git a/src/shims/unix/virtual_socket.rs b/src/shims/unix/virtual_socket.rs index 06c05b5e9f..60f3eb88d6 100644 --- a/src/shims/unix/virtual_socket.rs +++ b/src/shims/unix/virtual_socket.rs @@ -5,6 +5,7 @@ use std::cell::{Cell, OnceCell, RefCell}; use std::collections::VecDeque; use std::io::{self, ErrorKind, Read}; +use std::net::SocketAddr; use std::rc::Rc; use rustc_target::spec::Os; @@ -273,6 +274,9 @@ impl UnixSocketFileDescription for VirtualSocket { ptr: Pointer, len: usize, is_non_block: bool, + // For stream-based sockets `send` and `sendto` are equivalent and + // we can thus ignore the destination address. + _dest_addr: Option, ecx: &mut MiriInterpCx<'tcx>, finish: DynMachineCallback<'tcx, Result>, ) -> InterpResult<'tcx> { @@ -287,12 +291,20 @@ impl UnixSocketFileDescription for VirtualSocket { is_peek: bool, is_non_block: bool, ecx: &mut MiriInterpCx<'tcx>, - finish: DynMachineCallback<'tcx, Result>, + finish: DynMachineCallback<'tcx, Result<(usize, Option), IoError>>, ) -> InterpResult<'tcx> { if is_peek { throw_unsup_format!("socketpair: virtual sockets don't support peeking") } + let finish = callback!( + @capture<'tcx> { + finish: DynMachineCallback<'tcx, Result<(usize, Option), IoError>>, + } |this, result: Result| { + // The source address of the data isn't available for stream-based sockets. + finish.call(this, result.map(|bytes| (bytes, None))) + } + ); ecx.virtual_socket_read(self, ptr, len, is_non_block, finish) } } diff --git a/tests/pass-dep/libc/socket-tcp.rs b/tests/pass-dep/libc/socket-tcp.rs index 6ed06314e7..74b62aab0f 100644 --- a/tests/pass-dep/libc/socket-tcp.rs +++ b/tests/pass-dep/libc/socket-tcp.rs @@ -9,7 +9,7 @@ mod utils; use std::io::ErrorKind; use std::time::{Duration, Instant}; -use std::{ptr, thread}; +use std::{mem, ptr, thread}; use libc_utils::*; @@ -40,6 +40,8 @@ fn main() { test_connect_listening(); test_send_peek_recv(); test_write_read(); + test_recvfrom(); + test_sendto(); test_readv(); test_writev(); @@ -398,6 +400,167 @@ fn test_write_read() { server_thread.join().unwrap(); } +/// Test receiving data from a TCP socket using `recvfrom`. +/// Since TCP is a connection-mode protocol the `address` and `address_len` +/// pointers should be left unchanged. +fn test_recvfrom() { + let (server_sockfd, addr) = net::make_listener_ipv4().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. + + libc_utils::write_all(peerfd, TEST_BYTES).unwrap(); + libc_utils::write_all(peerfd, TEST_BYTES).unwrap(); + }); + + net::connect_ipv4(client_sockfd, addr).unwrap(); + + let mut buffer = [0; TEST_BYTES.len()]; + + // It is legal to make both the addr and addr_len pointers null. + unsafe { + errno_result(libc::recvfrom( + client_sockfd, + buffer.as_mut_ptr().cast(), + buffer.len(), + /* flags */ 0, + ptr::null_mut::(), + ptr::null_mut::(), + )) + .unwrap(); + } + + assert_eq!(&buffer, TEST_BYTES); + + let mut buffer: [u8; 26] = [0; TEST_BYTES.len()]; + + // If address pointer is non-null, `address_len` should be zeroed (but address itself is not accessed). + let mut addr = unsafe { mem::zeroed::() }; + let mut addr_len = mem::size_of_val(&addr) as libc::socklen_t; + unsafe { + errno_result(libc::recvfrom( + client_sockfd, + buffer.as_mut_ptr().cast(), + buffer.len(), + /* flags */ 0, + &mut addr, + &mut addr_len, + )) + .unwrap(); + } + // Ensure the `address_len` pointer gets zeroed. + assert_eq!(addr_len, 0); + + assert_eq!(&buffer, TEST_BYTES); + + server_thread.join().unwrap(); +} + +/// Test sending data into a TCP socket using `sendto`. +/// We provide a nullptr as destination address, which should not be +/// a problem since TCP is a connection-mode protocol and thus the +/// destination address should be ignored. +fn test_sendto() { + let (server_sockfd, addr) = net::make_listener_ipv4().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 using `sendto`. + + if cfg!(miri) || !cfg!(any(target_os = "freebsd", target_vendor = "apple")) { + // Native FreeBSD and macOS return EINVAL when the address pointer is invalid even when + // the address length is zero. However, POSIX says the address pointer should be entirely + // ignored for streaming sockets. We thus skip those native tests. + + // When the length is zero, the invalid address pointer is ignored. We thus provide + // a garbage pointer to ensure this behavior. + unsafe { + libc_utils::write_all_generic( + TEST_BYTES.as_ptr().cast(), + TEST_BYTES.len(), + libc_utils::NoRetry, + |buf, count| { + libc::sendto( + peerfd, + buf, + count, + /* flags */ 0, + ptr::without_provenance_mut::(16), + /* address_len */ 0, + ) + }, + ) + .unwrap(); + } + } + + // When the address pointer and the length are valid, the destination + // address should be ignored since we're using TCP. + let valid_addr = net::sock_addr_ipv4([127, 0, 0, 1], 5555); + unsafe { + libc_utils::write_all_generic( + TEST_BYTES.as_ptr().cast(), + TEST_BYTES.len(), + libc_utils::NoRetry, + |buf, count| { + libc::sendto( + peerfd, + buf, + count, + /* flags */ 0, + (&valid_addr as *const libc::sockaddr_in).cast::(), + size_of::() as libc::socklen_t, + ) + }, + ) + .unwrap(); + } + + if cfg!(miri) || !cfg!(any(target_os = "freebsd", target_vendor = "apple")) { + // On macOS and FreeBSD hosts we get EINVAL when the address pointer is a nullptr. + // This is not specified in their man pages and differs from the behavior of most + // other Unixes. We thus skip this `sendto` on those hosts. + + // When the address pointer is a nullptr it should be ignored. + unsafe { + libc_utils::write_all_generic( + TEST_BYTES.as_ptr().cast(), + TEST_BYTES.len(), + libc_utils::NoRetry, + |buf, count| { + libc::sendto( + peerfd, + buf, + count, + /* flags */ 0, + ptr::null_mut::(), + /* address_len */ 16, + ) + }, + ) + .unwrap(); + } + } + }); + + net::connect_ipv4(client_sockfd, addr).unwrap(); + + let mut buffer = [0; TEST_BYTES.len()]; + libc_utils::read_exact(client_sockfd, &mut buffer).unwrap(); + assert_eq!(&buffer, TEST_BYTES); + + server_thread.join().unwrap(); +} + /// Test vectored reads with multiple buffers on a connected socket. fn test_readv() { let (server_sockfd, addr) = net::make_listener_ipv4().unwrap(); diff --git a/tests/pass-dep/libc/socket.rs b/tests/pass-dep/libc/socket.rs index a8ed32066d..f5ab0b2ccb 100644 --- a/tests/pass-dep/libc/socket.rs +++ b/tests/pass-dep/libc/socket.rs @@ -116,10 +116,12 @@ fn test_bind_ipv4_invalid_addr_len() { // A too big size is also invalid. Some native hosts (e.g. Linux) // allow too big sizes, so we skip this test on native hosts: // + // Make sure the bigger size is still fully in-bounds. + let addrs = [addr; 2]; let err = unsafe { errno_result(libc::bind( sockfd, - (&addr as *const libc::sockaddr_in).cast::(), + addrs.as_ptr().cast::(), (size_of::() + 1) as libc::socklen_t, )) .unwrap_err()