Auto merge of #154832 - JonathanBrouwer:rollup-xJmqF28, r=JonathanBrouwer

Rollup of 2 pull requests

Successful merges:

 - rust-lang/rust#150129 (`BorrowedCursor`: make `init` a boolean)
 - rust-lang/rust#154830 (miri subtree update)
This commit is contained in:
bors
2026-04-05 00:08:46 +00:00
41 changed files with 1464 additions and 752 deletions
+2 -2
View File
@@ -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",
+59 -73
View File
@@ -2,20 +2,18 @@
use crate::fmt::{self, Debug, Formatter};
use crate::mem::{self, MaybeUninit};
use crate::{cmp, ptr};
use crate::ptr;
/// A borrowed byte buffer which is incrementally filled and initialized.
/// A borrowed byte buffer which is incrementally filled.
///
/// This type is a sort of "double cursor". It tracks three regions in the buffer: a region at the beginning of the
/// buffer that has been logically filled with data, a region that has been initialized at some point but not yet
/// logically filled, and a region at the end that is fully uninitialized. The filled region is guaranteed to be a
/// subset of the initialized region.
/// This type makes it safer to work with `MaybeUninit` buffers, such as to read into a buffer
/// without having to initialize it first. It tracks the region of bytes that have been filled and
/// whether the unfilled region was initialized.
///
/// In summary, the contents of the buffer can be visualized as:
/// ```not_rust
/// [ capacity ]
/// [ filled | unfilled ]
/// [ initialized | uninitialized ]
/// [ capacity ]
/// [ filled | unfilled (may be initialized) ]
/// ```
///
/// A `BorrowedBuf` is created around some existing data (or capacity for data) via a unique reference
@@ -30,8 +28,8 @@ pub struct BorrowedBuf<'data> {
buf: &'data mut [MaybeUninit<u8>],
/// The length of `self.buf` which is known to be filled.
filled: usize,
/// The length of `self.buf` which is known to be initialized.
init: usize,
/// Whether the entire unfilled part of `self.buf` has explicitly been initialized.
init: bool,
}
impl Debug for BorrowedBuf<'_> {
@@ -48,24 +46,20 @@ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
impl<'data> From<&'data mut [u8]> for BorrowedBuf<'data> {
#[inline]
fn from(slice: &'data mut [u8]) -> BorrowedBuf<'data> {
let len = slice.len();
BorrowedBuf {
// SAFETY: initialized data never becoming uninitialized is an invariant of BorrowedBuf
buf: unsafe { (slice as *mut [u8]).as_uninit_slice_mut().unwrap() },
buf: unsafe { &mut *(slice as *mut [u8] as *mut [MaybeUninit<u8>]) },
filled: 0,
init: len,
init: true,
}
}
}
/// Creates a new `BorrowedBuf` from an uninitialized buffer.
///
/// Use `set_init` if part of the buffer is known to be already initialized.
impl<'data> From<&'data mut [MaybeUninit<u8>]> for BorrowedBuf<'data> {
#[inline]
fn from(buf: &'data mut [MaybeUninit<u8>]) -> BorrowedBuf<'data> {
BorrowedBuf { buf, filled: 0, init: 0 }
BorrowedBuf { buf, filled: 0, init: false }
}
}
@@ -74,14 +68,13 @@ fn from(buf: &'data mut [MaybeUninit<u8>]) -> BorrowedBuf<'data> {
/// Use `BorrowedCursor::with_unfilled_buf` instead for a safer alternative.
impl<'data> From<BorrowedCursor<'data>> for BorrowedBuf<'data> {
#[inline]
fn from(mut buf: BorrowedCursor<'data>) -> BorrowedBuf<'data> {
let init = buf.init_mut().len();
fn from(buf: BorrowedCursor<'data>) -> BorrowedBuf<'data> {
BorrowedBuf {
// SAFETY: no initialized byte is ever uninitialized as per
// `BorrowedBuf`'s invariant
buf: unsafe { buf.buf.buf.get_unchecked_mut(buf.buf.filled..) },
filled: 0,
init,
init: buf.buf.init,
}
}
}
@@ -100,8 +93,9 @@ pub fn len(&self) -> usize {
}
/// Returns the length of the initialized part of the buffer.
#[unstable(feature = "borrowed_buf_init", issue = "78485")]
#[inline]
pub fn init_len(&self) -> usize {
pub fn is_init(&self) -> bool {
self.init
}
@@ -159,32 +153,29 @@ pub fn unfilled<'this>(&'this mut self) -> BorrowedCursor<'this> {
/// Clears the buffer, resetting the filled region to empty.
///
/// The number of initialized bytes is not changed, and the contents of the buffer are not modified.
/// The contents of the buffer are not modified.
#[inline]
pub fn clear(&mut self) -> &mut Self {
self.filled = 0;
self
}
/// Asserts that the first `n` bytes of the buffer are initialized.
///
/// `BorrowedBuf` assumes that bytes are never de-initialized, so this method does nothing when called with fewer
/// bytes than are already known to be initialized.
/// Asserts that the unfilled part of the buffer is initialized.
///
/// # Safety
///
/// The caller must ensure that the first `n` unfilled bytes of the buffer have already been initialized.
/// All the bytes of the buffer must be initialized.
#[unstable(feature = "borrowed_buf_init", issue = "78485")]
#[inline]
pub unsafe fn set_init(&mut self, n: usize) -> &mut Self {
self.init = cmp::max(self.init, n);
pub unsafe fn set_init(&mut self) -> &mut Self {
self.init = true;
self
}
}
/// A writeable view of the unfilled portion of a [`BorrowedBuf`].
///
/// The unfilled portion consists of an initialized and an uninitialized part; see [`BorrowedBuf`]
/// for details.
/// The unfilled portion may be uninitialized; see [`BorrowedBuf`] for details.
///
/// Data can be written directly to the cursor by using [`append`](BorrowedCursor::append) or
/// indirectly by getting a slice of part or all of the cursor and writing into the slice. In the
@@ -238,21 +229,29 @@ pub fn written(&self) -> usize {
self.buf.filled
}
/// Returns a mutable reference to the initialized portion of the cursor.
/// Returns `true` if the buffer is initialized.
#[unstable(feature = "borrowed_buf_init", issue = "78485")]
#[inline]
pub fn init_mut(&mut self) -> &mut [u8] {
// SAFETY: We only slice the initialized part of the buffer, which is always valid
unsafe {
let buf = self.buf.buf.get_unchecked_mut(self.buf.filled..self.buf.init);
buf.assume_init_mut()
}
pub fn is_init(&self) -> bool {
self.buf.init
}
/// Set the buffer as fully initialized.
///
/// # Safety
///
/// All the bytes of the cursor must be initialized.
#[unstable(feature = "borrowed_buf_init", issue = "78485")]
#[inline]
pub unsafe fn set_init(&mut self) {
self.buf.init = true;
}
/// Returns a mutable reference to the whole cursor.
///
/// # Safety
///
/// The caller must not uninitialize any bytes in the initialized portion of the cursor.
/// The caller must not uninitialize any bytes of the cursor if it is initialized.
#[inline]
pub unsafe fn as_mut(&mut self) -> &mut [MaybeUninit<u8>] {
// SAFETY: always in bounds
@@ -271,10 +270,12 @@ pub unsafe fn as_mut(&mut self) -> &mut [MaybeUninit<u8>] {
/// # Panics
///
/// Panics if there are less than `n` bytes initialized.
#[unstable(feature = "borrowed_buf_init", issue = "78485")]
#[inline]
pub fn advance(&mut self, n: usize) -> &mut Self {
pub fn advance_checked(&mut self, n: usize) -> &mut Self {
// The subtraction cannot underflow by invariant of this type.
assert!(n <= self.buf.init - self.buf.filled);
let init_unfilled = if self.buf.init { self.buf.buf.len() - self.buf.filled } else { 0 };
assert!(n <= init_unfilled);
self.buf.filled += n;
self
@@ -291,40 +292,29 @@ pub fn advance(&mut self, n: usize) -> &mut Self {
/// The caller must ensure that the first `n` bytes of the cursor have been properly
/// initialised.
#[inline]
pub unsafe fn advance_unchecked(&mut self, n: usize) -> &mut Self {
pub unsafe fn advance(&mut self, n: usize) -> &mut Self {
self.buf.filled += n;
self.buf.init = cmp::max(self.buf.init, self.buf.filled);
self
}
/// Initializes all bytes in the cursor.
/// Initializes all bytes in the cursor and returns them.
#[unstable(feature = "borrowed_buf_init", issue = "78485")]
#[inline]
pub fn ensure_init(&mut self) -> &mut Self {
pub fn ensure_init(&mut self) -> &mut [u8] {
// SAFETY: always in bounds and we never uninitialize these bytes.
let uninit = unsafe { self.buf.buf.get_unchecked_mut(self.buf.init..) };
let unfilled = unsafe { self.buf.buf.get_unchecked_mut(self.buf.filled..) };
// SAFETY: 0 is a valid value for MaybeUninit<u8> and the length matches the allocation
// since it is comes from a slice reference.
unsafe {
ptr::write_bytes(uninit.as_mut_ptr(), 0, uninit.len());
if !self.buf.init {
// SAFETY: 0 is a valid value for MaybeUninit<u8> and the length matches the allocation
// since it is comes from a slice reference.
unsafe {
ptr::write_bytes(unfilled.as_mut_ptr(), 0, unfilled.len());
}
self.buf.init = true;
}
self.buf.init = self.buf.capacity();
self
}
/// Asserts that the first `n` unfilled bytes of the cursor are initialized.
///
/// `BorrowedBuf` assumes that bytes are never de-initialized, so this method does nothing when
/// called with fewer bytes than are already known to be initialized.
///
/// # Safety
///
/// The caller must ensure that the first `n` bytes of the buffer have already been initialized.
#[inline]
pub unsafe fn set_init(&mut self, n: usize) -> &mut Self {
self.buf.init = cmp::max(self.buf.init, self.buf.filled + n);
self
// SAFETY: these bytes have just been initialized if they weren't before
unsafe { unfilled.assume_init_mut() }
}
/// Appends data to the cursor, advancing position within its buffer.
@@ -341,10 +331,6 @@ pub fn append(&mut self, buf: &[u8]) {
self.as_mut()[..buf.len()].write_copy_of_slice(buf);
}
// SAFETY: We just added the entire contents of buf to the filled section.
unsafe {
self.set_init(buf.len());
}
self.buf.filled += buf.len();
}
@@ -365,7 +351,7 @@ pub fn with_unfilled_buf<T>(&mut self, f: impl FnOnce(&mut BorrowedBuf<'_>) -> T
// Check that the caller didn't replace the `BorrowedBuf`.
// This is necessary for the safety of the code below: if the check wasn't
// there, one could mark some bytes as initialized even though there aren't.
assert!(core::ptr::addr_eq(prev_ptr, buf.buf));
assert!(core::ptr::eq(prev_ptr, buf.buf));
let filled = buf.filled;
let init = buf.init;
@@ -376,7 +362,7 @@ pub fn with_unfilled_buf<T>(&mut self, f: impl FnOnce(&mut BorrowedBuf<'_>) -> T
// SAFETY: These amounts of bytes were initialized/filled in the `BorrowedBuf`,
// and therefore they are initialized/filled in the cursor too, because the
// buffer wasn't replaced.
self.buf.init = self.buf.filled + init;
self.buf.init = init;
self.buf.filled += filled;
res
+24 -48
View File
@@ -8,7 +8,7 @@ fn new() {
let mut rbuf: BorrowedBuf<'_> = buf.into();
assert_eq!(rbuf.filled().len(), 0);
assert_eq!(rbuf.init_len(), 16);
assert!(rbuf.is_init());
assert_eq!(rbuf.capacity(), 16);
assert_eq!(rbuf.unfilled().capacity(), 16);
}
@@ -20,7 +20,7 @@ fn uninit() {
let mut rbuf: BorrowedBuf<'_> = buf.into();
assert_eq!(rbuf.filled().len(), 0);
assert_eq!(rbuf.init_len(), 0);
assert!(!rbuf.is_init());
assert_eq!(rbuf.capacity(), 16);
assert_eq!(rbuf.unfilled().capacity(), 16);
}
@@ -32,7 +32,7 @@ fn initialize_unfilled() {
rbuf.unfilled().ensure_init();
assert_eq!(rbuf.init_len(), 16);
assert!(rbuf.is_init());
}
#[test]
@@ -40,7 +40,7 @@ fn advance_filled() {
let buf: &mut [_] = &mut [0; 16];
let mut rbuf: BorrowedBuf<'_> = buf.into();
rbuf.unfilled().advance(1);
rbuf.unfilled().advance_checked(1);
assert_eq!(rbuf.filled().len(), 1);
assert_eq!(rbuf.unfilled().capacity(), 15);
@@ -51,7 +51,7 @@ fn clear() {
let buf: &mut [_] = &mut [255; 16];
let mut rbuf: BorrowedBuf<'_> = buf.into();
rbuf.unfilled().advance(16);
rbuf.unfilled().advance_checked(16);
assert_eq!(rbuf.filled().len(), 16);
assert_eq!(rbuf.unfilled().capacity(), 0);
@@ -61,7 +61,7 @@ fn clear() {
assert_eq!(rbuf.filled().len(), 0);
assert_eq!(rbuf.unfilled().capacity(), 16);
assert_eq!(rbuf.unfilled().init_mut(), [255; 16]);
assert_eq!(rbuf.unfilled().ensure_init(), [255; 16]);
}
#[test]
@@ -70,24 +70,10 @@ fn set_init() {
let mut rbuf: BorrowedBuf<'_> = buf.into();
unsafe {
rbuf.set_init(8);
rbuf.set_init();
}
assert_eq!(rbuf.init_len(), 8);
rbuf.unfilled().advance(4);
unsafe {
rbuf.set_init(2);
}
assert_eq!(rbuf.init_len(), 8);
unsafe {
rbuf.set_init(8);
}
assert_eq!(rbuf.init_len(), 8);
assert!(rbuf.is_init());
}
#[test]
@@ -97,7 +83,7 @@ fn append() {
rbuf.unfilled().append(&[0; 8]);
assert_eq!(rbuf.init_len(), 8);
assert!(!rbuf.is_init());
assert_eq!(rbuf.filled().len(), 8);
assert_eq!(rbuf.filled(), [0; 8]);
@@ -105,7 +91,7 @@ fn append() {
rbuf.unfilled().append(&[1; 16]);
assert_eq!(rbuf.init_len(), 16);
assert!(!rbuf.is_init());
assert_eq!(rbuf.filled().len(), 16);
assert_eq!(rbuf.filled(), [1; 16]);
}
@@ -125,7 +111,7 @@ fn reborrow_written() {
assert_eq!(cursor.written(), 32);
assert_eq!(buf.unfilled().written(), 32);
assert_eq!(buf.init_len(), 32);
assert!(!buf.is_init());
assert_eq!(buf.filled().len(), 32);
let filled = buf.filled();
assert_eq!(&filled[..16], [1; 16]);
@@ -136,30 +122,20 @@ fn reborrow_written() {
fn cursor_set_init() {
let buf: &mut [_] = &mut [MaybeUninit::zeroed(); 16];
let mut rbuf: BorrowedBuf<'_> = buf.into();
let mut cursor = rbuf.unfilled();
unsafe {
rbuf.unfilled().set_init(8);
cursor.set_init();
}
assert_eq!(rbuf.init_len(), 8);
assert_eq!(rbuf.unfilled().init_mut().len(), 8);
assert_eq!(unsafe { rbuf.unfilled().as_mut().len() }, 16);
assert!(cursor.is_init());
assert_eq!(unsafe { cursor.as_mut().len() }, 16);
rbuf.unfilled().advance(4);
cursor.advance_checked(4);
unsafe {
rbuf.unfilled().set_init(2);
}
assert_eq!(unsafe { cursor.as_mut().len() }, 12);
assert_eq!(rbuf.init_len(), 8);
unsafe {
rbuf.unfilled().set_init(8);
}
assert_eq!(rbuf.init_len(), 12);
assert_eq!(rbuf.unfilled().init_mut().len(), 8);
assert_eq!(unsafe { rbuf.unfilled().as_mut().len() }, 12);
assert!(rbuf.is_init());
}
#[test]
@@ -173,26 +149,26 @@ fn cursor_with_unfilled_buf() {
assert_eq!(buf.filled(), &[1, 2, 3]);
});
assert_eq!(cursor.init_mut().len(), 0);
assert!(!cursor.is_init());
assert_eq!(cursor.written(), 3);
cursor.with_unfilled_buf(|buf| {
assert_eq!(buf.capacity(), 13);
assert_eq!(buf.init_len(), 0);
assert!(!buf.is_init());
buf.unfilled().ensure_init();
buf.unfilled().advance(4);
buf.unfilled().advance_checked(4);
});
assert_eq!(cursor.init_mut().len(), 9);
assert!(cursor.is_init());
assert_eq!(cursor.written(), 7);
cursor.with_unfilled_buf(|buf| {
assert_eq!(buf.capacity(), 9);
assert_eq!(buf.init_len(), 9);
assert!(buf.is_init());
});
assert_eq!(cursor.init_mut().len(), 9);
assert!(cursor.is_init());
assert_eq!(cursor.written(), 7);
assert_eq!(rbuf.filled(), &[1, 2, 3, 0, 0, 0, 0]);
+1
View File
@@ -8,6 +8,7 @@
#![feature(async_iter_from_iter)]
#![feature(async_iterator)]
#![feature(bool_to_result)]
#![feature(borrowed_buf_init)]
#![feature(bstr)]
#![feature(cfg_target_has_reliable_f16_f128)]
#![feature(char_internals)]
+1 -1
View File
@@ -717,7 +717,7 @@ fn file_test_read_buf() {
check!(file.read_buf(buf.unfilled()));
assert_eq!(buf.filled(), &[1, 2, 3, 4]);
// File::read_buf should omit buffer initialization.
assert_eq!(buf.init_len(), 4);
assert!(!buf.is_init());
check!(fs::remove_file(filename));
}
+1 -1
View File
@@ -288,7 +288,7 @@ pub(in crate::io) fn discard_buffer(&mut self) {
#[cfg(test)]
impl<R: ?Sized> BufReader<R> {
#[allow(missing_docs)]
pub fn initialized(&self) -> usize {
pub fn initialized(&self) -> bool {
self.buf.initialized()
}
}
+13 -11
View File
@@ -26,20 +26,20 @@ pub struct Buffer {
// defensive initialization as possible. Note that while this often the same as `filled`, it
// doesn't need to be. Calls to `fill_buf` are not required to actually fill the buffer, and
// omitting this is a huge perf regression for `Read` impls that do not.
initialized: usize,
initialized: bool,
}
impl Buffer {
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
let buf = Box::new_uninit_slice(capacity);
Self { buf, pos: 0, filled: 0, initialized: 0 }
Self { buf, pos: 0, filled: 0, initialized: false }
}
#[inline]
pub fn try_with_capacity(capacity: usize) -> io::Result<Self> {
match Box::try_new_uninit_slice(capacity) {
Ok(buf) => Ok(Self { buf, pos: 0, filled: 0, initialized: 0 }),
Ok(buf) => Ok(Self { buf, pos: 0, filled: 0, initialized: false }),
Err(_) => {
Err(io::const_error!(ErrorKind::OutOfMemory, "failed to allocate read buffer"))
}
@@ -70,7 +70,7 @@ pub fn pos(&self) -> usize {
// This is only used by a test which asserts that the initialization-tracking is correct.
#[cfg(test)]
pub fn initialized(&self) -> usize {
pub fn initialized(&self) -> bool {
self.initialized
}
@@ -110,13 +110,14 @@ pub fn unconsume(&mut self, amt: usize) {
/// Read more bytes into the buffer without discarding any of its contents
pub fn read_more(&mut self, mut reader: impl Read) -> io::Result<usize> {
let mut buf = BorrowedBuf::from(&mut self.buf[self.filled..]);
let old_init = self.initialized - self.filled;
unsafe {
buf.set_init(old_init);
if self.initialized {
unsafe { buf.set_init() };
}
reader.read_buf(buf.unfilled())?;
self.filled += buf.len();
self.initialized += buf.init_len() - old_init;
self.initialized = buf.is_init();
Ok(buf.len())
}
@@ -138,15 +139,16 @@ pub fn fill_buf(&mut self, mut reader: impl Read) -> io::Result<&[u8]> {
let mut buf = BorrowedBuf::from(&mut *self.buf);
// SAFETY: `self.filled` bytes will always have been initialized.
unsafe {
buf.set_init(self.initialized);
if self.initialized {
unsafe { buf.set_init() };
}
let result = reader.read_buf(buf.unfilled());
self.pos = 0;
self.filled = buf.len();
self.initialized = buf.init_len();
self.initialized = buf.is_init();
result?;
}
+2 -2
View File
@@ -1067,13 +1067,13 @@ fn read(&mut self, buf: &mut [u8]) -> crate::io::Result<usize> {
}
let mut reader = BufReader::new(OneByteReader);
// Nothing is initialized yet.
assert_eq!(reader.initialized(), 0);
assert!(!reader.initialized());
let buf = reader.fill_buf().unwrap();
// We read one byte...
assert_eq!(buf.len(), 1);
// But we initialized the whole buffer!
assert_eq!(reader.initialized(), reader.capacity());
assert!(reader.initialized());
}
/// This is a regression test for https://github.com/rust-lang/rust/issues/127584.
+4 -8
View File
@@ -214,15 +214,15 @@ fn copy_from<R: Read + ?Sized>(&mut self, reader: &mut R) -> Result<u64> {
}
let mut len = 0;
let mut init = 0;
let mut init = false;
loop {
let buf = self.buffer_mut();
let mut read_buf: BorrowedBuf<'_> = buf.spare_capacity_mut().into();
unsafe {
if init {
// SAFETY: init is either 0 or the init_len from the previous iteration.
read_buf.set_init(init);
unsafe { read_buf.set_init() };
}
if read_buf.capacity() >= DEFAULT_BUF_SIZE {
@@ -235,7 +235,7 @@ fn copy_from<R: Read + ?Sized>(&mut self, reader: &mut R) -> Result<u64> {
return Ok(len);
}
init = read_buf.init_len() - bytes_read;
init = read_buf.is_init();
len += bytes_read as u64;
// SAFETY: BorrowedBuf guarantees all of its filled bytes are init
@@ -248,10 +248,6 @@ fn copy_from<R: Read + ?Sized>(&mut self, reader: &mut R) -> Result<u64> {
Err(e) => return Err(e),
}
} else {
// All the bytes that were already in the buffer are initialized,
// treat them as such when the buffer is flushed.
init += buf.len();
self.flush_buf()?;
}
}
+24 -33
View File
@@ -419,8 +419,6 @@ pub(crate) fn default_read_to_end<R: Read + ?Sized>(
.and_then(|s| s.checked_add(1024)?.checked_next_multiple_of(DEFAULT_BUF_SIZE))
.unwrap_or(DEFAULT_BUF_SIZE);
let mut initialized = 0; // Extra initialized bytes from previous loop iteration
const PROBE_SIZE: usize = 32;
fn small_probe_read<R: Read + ?Sized>(r: &mut R, buf: &mut Vec<u8>) -> Result<usize> {
@@ -449,8 +447,6 @@ fn small_probe_read<R: Read + ?Sized>(r: &mut R, buf: &mut Vec<u8>) -> Result<us
}
}
let mut consecutive_short_reads = 0;
loop {
if buf.len() == buf.capacity() && buf.capacity() == start_cap {
// The buffer might be an exact fit. Let's read into a probe buffer
@@ -474,11 +470,8 @@ fn small_probe_read<R: Read + ?Sized>(r: &mut R, buf: &mut Vec<u8>) -> Result<us
spare = &mut spare[..buf_len];
let mut read_buf: BorrowedBuf<'_> = spare.into();
// SAFETY: These bytes were initialized but not filled in the previous loop
unsafe {
read_buf.set_init(initialized);
}
// Note that we don't track already initialized bytes here, but this is fine
// because we explicitly limit the read size
let mut cursor = read_buf.unfilled();
let result = loop {
match r.read_buf(cursor.reborrow()) {
@@ -489,9 +482,8 @@ fn small_probe_read<R: Read + ?Sized>(r: &mut R, buf: &mut Vec<u8>) -> Result<us
}
};
let unfilled_but_initialized = cursor.init_mut().len();
let bytes_read = cursor.written();
let was_fully_initialized = read_buf.init_len() == buf_len;
let is_init = read_buf.is_init();
// SAFETY: BorrowedBuf's invariants mean this much memory is initialized.
unsafe {
@@ -506,15 +498,6 @@ fn small_probe_read<R: Read + ?Sized>(r: &mut R, buf: &mut Vec<u8>) -> Result<us
return Ok(buf.len() - start_len);
}
if bytes_read < buf_len {
consecutive_short_reads += 1;
} else {
consecutive_short_reads = 0;
}
// store how much was initialized but not filled
initialized = unfilled_but_initialized;
// Use heuristics to determine the max read size if no initial size hint was provided
if size_hint.is_none() {
// The reader is returning short reads but it doesn't call ensure_init().
@@ -523,13 +506,12 @@ fn small_probe_read<R: Read + ?Sized>(r: &mut R, buf: &mut Vec<u8>) -> Result<us
// When reading from disk we usually don't get any short reads except at EOF.
// So we wait for at least 2 short reads before uncapping the read buffer;
// this helps with the Windows issue.
if !was_fully_initialized && consecutive_short_reads > 1 {
if !is_init {
max_read_size = usize::MAX;
}
// we have passed a larger buffer than previously and the
// reader still hasn't returned a short read
if buf_len >= max_read_size && bytes_read == buf_len {
else if buf_len >= max_read_size && bytes_read == buf_len {
max_read_size = max_read_size.saturating_mul(2);
}
}
@@ -587,8 +569,8 @@ pub(crate) fn default_read_buf<F>(read: F, mut cursor: BorrowedCursor<'_>) -> Re
where
F: FnOnce(&mut [u8]) -> Result<usize>,
{
let n = read(cursor.ensure_init().init_mut())?;
cursor.advance(n);
let n = read(cursor.ensure_init())?;
cursor.advance_checked(n);
Ok(())
}
@@ -3098,7 +3080,7 @@ fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> Result<()> {
// The condition above guarantees that `self.limit` fits in `usize`.
let limit = self.limit as usize;
let extra_init = cmp::min(limit, buf.init_mut().len());
let is_init = buf.is_init();
// SAFETY: no uninit data is written to ibuf
let ibuf = unsafe { &mut buf.as_mut()[..limit] };
@@ -3106,23 +3088,32 @@ fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> Result<()> {
let mut sliced_buf: BorrowedBuf<'_> = ibuf.into();
// SAFETY: extra_init bytes of ibuf are known to be initialized
unsafe {
sliced_buf.set_init(extra_init);
if is_init {
unsafe { sliced_buf.set_init() };
}
let mut cursor = sliced_buf.unfilled();
let result = self.inner.read_buf(cursor.reborrow());
let new_init = cursor.init_mut().len();
let should_init = cursor.is_init();
let filled = sliced_buf.len();
// cursor / sliced_buf / ibuf must drop here
// Avoid accidentally quadratic behaviour by initializing the whole
// cursor if only part of it was initialized.
if should_init {
// SAFETY: no uninit data is written
let uninit = unsafe { &mut buf.as_mut()[limit..] };
uninit.write_filled(0);
// SAFETY: all bytes that were not initialized by `T::read_buf`
// have just been written to.
unsafe { buf.set_init() };
}
unsafe {
// SAFETY: filled bytes have been filled and therefore initialized
buf.advance_unchecked(filled);
// SAFETY: new_init bytes of buf's unfilled buffer have been initialized
buf.set_init(new_init);
// SAFETY: filled bytes have been filled
buf.advance(filled);
}
self.limit -= filled as u64;
+2 -2
View File
@@ -214,8 +214,8 @@ fn read_buf_exact() {
fn borrowed_cursor_advance_overflow() {
let mut buf = [0; 512];
let mut buf = BorrowedBuf::from(&mut buf[..]);
buf.unfilled().advance(1);
buf.unfilled().advance(usize::MAX);
buf.unfilled().advance_checked(1);
buf.unfilled().advance_checked(usize::MAX);
}
#[test]
+1 -1
View File
@@ -283,7 +283,7 @@ fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> io::Result<()> {
// SAFETY: No uninit bytes are being written.
unsafe { buf.as_mut() }.write_filled(self.byte);
// SAFETY: the entire unfilled portion of buf has been initialized.
unsafe { buf.advance_unchecked(buf.capacity()) };
unsafe { buf.advance(buf.capacity()) };
Ok(())
}
+8 -8
View File
@@ -75,43 +75,43 @@ fn empty_reads() {
let mut buf: BorrowedBuf<'_> = buf.into();
e.read_buf(buf.unfilled()).unwrap();
assert_eq!(buf.len(), 0);
assert_eq!(buf.init_len(), 0);
assert!(!buf.is_init());
let buf: &mut [_] = &mut [MaybeUninit::uninit()];
let mut buf: BorrowedBuf<'_> = buf.into();
e.read_buf(buf.unfilled()).unwrap();
assert_eq!(buf.len(), 0);
assert_eq!(buf.init_len(), 0);
assert!(!buf.is_init());
let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024];
let mut buf: BorrowedBuf<'_> = buf.into();
e.read_buf(buf.unfilled()).unwrap();
assert_eq!(buf.len(), 0);
assert_eq!(buf.init_len(), 0);
assert!(!buf.is_init());
let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024];
let mut buf: BorrowedBuf<'_> = buf.into();
Read::by_ref(&mut e).read_buf(buf.unfilled()).unwrap();
assert_eq!(buf.len(), 0);
assert_eq!(buf.init_len(), 0);
assert!(!buf.is_init());
let buf: &mut [MaybeUninit<_>] = &mut [];
let mut buf: BorrowedBuf<'_> = buf.into();
e.read_buf_exact(buf.unfilled()).unwrap();
assert_eq!(buf.len(), 0);
assert_eq!(buf.init_len(), 0);
assert!(!buf.is_init());
let buf: &mut [_] = &mut [MaybeUninit::uninit()];
let mut buf: BorrowedBuf<'_> = buf.into();
assert_eq!(e.read_buf_exact(buf.unfilled()).unwrap_err().kind(), ErrorKind::UnexpectedEof);
assert_eq!(buf.len(), 0);
assert_eq!(buf.init_len(), 0);
assert!(!buf.is_init());
let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024];
let mut buf: BorrowedBuf<'_> = buf.into();
assert_eq!(e.read_buf_exact(buf.unfilled()).unwrap_err().kind(), ErrorKind::UnexpectedEof);
assert_eq!(buf.len(), 0);
assert_eq!(buf.init_len(), 0);
assert!(!buf.is_init());
let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024];
let mut buf: BorrowedBuf<'_> = buf.into();
@@ -120,7 +120,7 @@ fn empty_reads() {
ErrorKind::UnexpectedEof,
);
assert_eq!(buf.len(), 0);
assert_eq!(buf.init_len(), 0);
assert!(!buf.is_init());
let mut buf = Vec::new();
assert_eq!(e.read_to_end(&mut buf).unwrap(), 0);
+1
View File
@@ -311,6 +311,7 @@
//
// Library features (core):
// tidy-alphabetical-start
#![feature(borrowed_buf_init)]
#![feature(bstr)]
#![feature(bstr_internals)]
#![feature(cast_maybe_uninit)]
+1 -1
View File
@@ -317,7 +317,7 @@ fn read_buf() {
t!(s.read_buf(buf.unfilled()));
assert_eq!(buf.filled(), &[1, 2, 3, 4]);
// TcpStream::read_buf should omit buffer initialization.
assert_eq!(buf.init_len(), 4);
assert!(!buf.is_init());
t.join().ok().expect("thread panicked");
})
+2 -2
View File
@@ -188,10 +188,10 @@ fn child_stdout_read_buf() {
// ChildStdout::read_buf should omit buffer initialization.
if cfg!(target_os = "windows") {
assert_eq!(buf.filled(), b"abc\r\n");
assert_eq!(buf.init_len(), 5);
assert!(!buf.is_init());
} else {
assert_eq!(buf.filled(), b"abc\n");
assert_eq!(buf.init_len(), 4);
assert!(!buf.is_init());
};
}
+1 -1
View File
@@ -33,7 +33,7 @@ pub fn read_buf(&self, mut buf: BorrowedCursor<'_>) -> io::Result<()> {
)
})?;
// SAFETY: Exactly `result` bytes have been filled.
unsafe { buf.advance_unchecked(result as usize) };
unsafe { buf.advance(result as usize) };
Ok(())
}
+2 -2
View File
@@ -185,7 +185,7 @@ pub fn read_buf(&self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
// SAFETY: `ret` bytes were written to the initialized portion of the buffer
unsafe {
cursor.advance_unchecked(ret as usize);
cursor.advance(ret as usize);
}
Ok(())
}
@@ -203,7 +203,7 @@ pub fn read_buf_at(&self, mut cursor: BorrowedCursor<'_>, offset: u64) -> io::Re
// SAFETY: `ret` bytes were written to the initialized portion of the buffer
unsafe {
cursor.advance_unchecked(ret as usize);
cursor.advance(ret as usize);
}
Ok(())
}
+1 -1
View File
@@ -401,7 +401,7 @@ pub fn read_buf(&self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
// Safety: `num_bytes_read` bytes were written to the unfilled
// portion of the buffer
cursor.advance_unchecked(num_bytes_read);
cursor.advance(num_bytes_read);
Ok(())
}
@@ -142,7 +142,7 @@ fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: i32) -> io::Result
)
})?;
unsafe {
buf.advance_unchecked(ret as usize);
buf.advance(ret as usize);
}
Ok(())
}
@@ -190,7 +190,7 @@ fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Resu
netc::recv(self.as_raw_fd(), buf.as_mut().as_mut_ptr().cast(), buf.capacity(), flags)
})?;
unsafe {
buf.advance_unchecked(ret as usize);
buf.advance(ret as usize);
}
Ok(())
}
@@ -294,7 +294,7 @@ fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Resu
)
})?;
unsafe {
buf.advance_unchecked(ret as usize);
buf.advance(ret as usize);
}
Ok(())
}
@@ -243,7 +243,7 @@ fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Resu
}
}
_ => {
unsafe { buf.advance_unchecked(result as usize) };
unsafe { buf.advance(result as usize) };
Ok(())
}
}
@@ -44,7 +44,7 @@ pub fn read_buf(fd: Fd, mut buf: BorrowedCursor<'_>) -> io::Result<()> {
let mut userbuf = alloc::User::<[u8]>::uninitialized(buf.capacity());
let len = raw::read(fd, userbuf.as_mut_ptr().cast(), userbuf.len()).from_sgx_result()?;
userbuf[..len].copy_to_enclave(&mut buf.as_mut()[..len]);
buf.advance_unchecked(len);
buf.advance(len);
Ok(())
}
}
+2 -2
View File
@@ -117,7 +117,7 @@ pub fn read_buf(&self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
Ok(read) => {
// Safety: `read` bytes were written to the initialized portion of the buffer
unsafe {
cursor.advance_unchecked(read);
cursor.advance(read);
}
Ok(())
}
@@ -140,7 +140,7 @@ pub fn read_buf_at(&self, mut cursor: BorrowedCursor<'_>, offset: u64) -> io::Re
// SAFETY: `read` bytes were written to the initialized portion of the buffer
unsafe {
cursor.advance_unchecked(read);
cursor.advance(read);
}
Ok(())
}
@@ -260,7 +260,7 @@ pub fn read_buf(&self, mut buf: BorrowedCursor<'_>) -> io::Result<()> {
Err(e) => Err(e),
Ok(n) => {
unsafe {
buf.advance_unchecked(n);
buf.advance(n);
}
Ok(())
}
+1 -1
View File
@@ -19,7 +19,7 @@ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> io::Result<()> {
unsafe {
let n = abi::sys_read(fileno::STDIN, buf.as_mut().as_mut_ptr().cast(), buf.capacity());
buf.advance_unchecked(n);
buf.advance(n);
}
Ok(())
}
+2 -2
View File
@@ -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 <https://doc.rust-lang.org/nightly/cargo/guide/cargo-home.html#caching-the-cargo-home-in-ci>.
@@ -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
+1 -1
View File
@@ -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 <https://doc.rust-lang.org/nightly/cargo/guide/cargo-home.html#caching-the-cargo-home-in-ci>.
+2 -2
View File
@@ -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",
+1 -1
View File
@@ -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.
+1 -1
View File
@@ -1 +1 @@
116458d0a5ae01cd517cabd2d1aee7f5457018ab
55e86c996809902e8bbad512cfb4d2c18be446d9
+8 -6
View File
@@ -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 @@ fn read<'tcx>(
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 @@ fn read<'tcx>(
) -> 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<usize>,
len: usize,
ptr: Pointer,
) -> InterpResult<'tcx, Result<usize, IoError>> {
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 @@ fn read_from_host(
}
}
/// 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,
@@ -612,6 +612,24 @@ fn emulate_foreign_item_inner(
)?;
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),
+351 -8
View File
@@ -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 @@ fn name(&self) -> &'static str {
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<Self>,
communicate_allowed: bool,
ptr: Pointer,
len: usize,
ecx: &mut MiriInterpCx<'tcx>,
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
) -> 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<Self>,
communicate_allowed: bool,
ptr: Pointer,
len: usize,
ecx: &mut MiriInterpCx<'tcx>,
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
) -> 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 @@ fn socket(
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 @@ fn accept4(
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 @@ fn connect(
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::<Socket>() 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<usize, IoError>| {
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::<Socket>() 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<usize, IoError>| {
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 @@ fn block_for_connect(&mut self, socket: FileDescriptionRef<Socket>, dest: MPlace
}),
);
}
/// 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<Socket>,
buffer_ptr: Pointer,
length: usize,
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
) {
let this = self.eval_context_mut();
this.block_thread_for_io(
socket.clone(),
Interest::WRITABLE,
None,
callback!(@capture<'tcx> {
socket: FileDescriptionRef<Socket>,
buffer_ptr: Pointer,
length: usize,
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
} |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<Socket>,
buffer_ptr: Pointer,
length: usize,
should_peek: bool,
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
) {
let this = self.eval_context_mut();
this.block_thread_for_io(
socket.clone(),
Interest::READABLE,
None,
callback!(@capture<'tcx> {
socket: FileDescriptionRef<Socket>,
buffer_ptr: Pointer,
length: usize,
should_peek: bool,
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
} |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<Socket> {
+28 -3
View File
@@ -6,16 +6,20 @@
/// 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<u64>,
) -> 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 @@ fn rwlock_get_data<'tcx, 'a>(
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 @@ fn cond_get_data<'tcx, 'a>(
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"
@@ -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.
+408 -279
View File
@@ -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]]
@@ -73,10 +55,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "futures"
version = "0.3.31"
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "futures"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
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 = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013"
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
[[package]]
name = "indexmap"
version = "2.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
dependencies = [
"bitflags",
"cfg-if",
"libc",
"equivalent",
"hashbrown 0.16.1",
"serde",
"serde_core",
]
[[package]]
name = "js-sys"
version = "0.3.77"
name = "itoa"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "js-sys"
version = "0.3.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9"
dependencies = [
"once_cell",
"wasm-bindgen",
]
[[package]]
name = "libc"
version = "0.2.174"
name = "leb128fmt"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
[[package]]
name = "libc"
version = "0.2.183"
source = "registry+https://github.com/rust-lang/crates.io-index"
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 = "wasip3"
version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
dependencies = [
"wit-bindgen",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.100"
version = "0.2.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
dependencies = [
"bumpalo",
"log",
"proc-macro2",
"quote",
"syn",
"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"
name = "windows-link"
version = "0.2.1"
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"
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-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
version = "0.53.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
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",
]
[[package]]
name = "windows-targets"
version = "0.53.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef"
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"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
version = "0.53.1"
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"
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
[[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"
[[package]]
name = "windows_i686_gnu"
version = "0.53.0"
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"
[[package]]
name = "windows_i686_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_i686_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnu"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
[[package]]
name = "windows_x86_64_msvc"
version = "0.53.0"
name = "wit-bindgen"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
[[package]]
name = "wit-bindgen-rt"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
dependencies = [
"wit-bindgen-rust-macro",
]
[[package]]
name = "wit-bindgen-core"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
dependencies = [
"anyhow",
"heck",
"wit-parser",
]
[[package]]
name = "wit-bindgen-rust"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
dependencies = [
"anyhow",
"heck",
"indexmap",
"prettyplease",
"syn",
"wasm-metadata",
"wit-bindgen-core",
"wit-component",
]
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
dependencies = [
"anyhow",
"prettyplease",
"proc-macro2",
"quote",
"syn",
"wit-bindgen-core",
"wit-bindgen-rust",
]
[[package]]
name = "wit-component"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
dependencies = [
"anyhow",
"bitflags",
"indexmap",
"log",
"serde",
"serde_derive",
"serde_json",
"wasm-encoder",
"wasm-metadata",
"wasmparser",
"wit-parser",
]
[[package]]
name = "wit-parser"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
dependencies = [
"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"
+199 -236
View File
@@ -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::<libc::sockaddr>(),
size_of::<libc::sockaddr_in>() 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::<libc::sockaddr>(),
size_of::<libc::sockaddr_in>() 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::<libc::sockaddr>(),
size_of::<libc::sockaddr_in>() 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::<libc::sockaddr>(),
size_of::<libc::sockaddr_in>() 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();
});
let server_thread = thread::spawn(move || net::accept_ipv4(server_sockfd).unwrap());
// Test connecting to an already accepting server.
unsafe {
errno_check(libc::connect(
client_sockfd,
(&addr as *const libc::sockaddr_in).cast::<libc::sockaddr>(),
size_of::<libc::sockaddr_in>() as libc::socklen_t,
));
}
net::connect_ipv4(client_sockfd, addr);
let (_, peer_addr) =
sockname(|storage, len| unsafe { libc::getpeername(client_sockfd, storage, len) }).unwrap();
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::<libc::sockaddr>(),
size_of::<libc::sockaddr_in6>() 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::<libc::sockaddr>(),
size_of::<libc::sockaddr_in6>() 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<T>(
sockfd: i32,
level: libc::c_int,
option_name: libc::c_int,
option_value: T,
) -> io::Result<()> {
let option_len = size_of::<T>() 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: F) -> io::Result<(libc::c_int, LibcSocketAddr)>
where
F: FnOnce(*mut libc::sockaddr, *mut libc::socklen_t) -> libc::c_int,
{
let mut storage = MaybeUninit::<libc::sockaddr_storage>::zeroed();
let mut len = size_of::<libc::sockaddr_storage>() 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::<libc::sockaddr_in>());
LibcSocketAddr::V4(*(storage.as_ptr() as *const _ as *const libc::sockaddr_in))
}
libc::AF_INET6 => {
assert!(len as usize >= size_of::<libc::sockaddr_in6>());
LibcSocketAddr::V6(*(storage.as_ptr() as *const _ as *const libc::sockaddr_in6))
}
_ => return Err(io::Error::new(ErrorKind::InvalidInput, "invalid argument")),
}
};
Ok((value, address))
}
+59
View File
@@ -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() {
+225 -4
View File
@@ -159,13 +159,17 @@ pub fn check_epoll_wait_noblock<const N: usize>(epfd: i32, expected: &[Ev]) {
}
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 fn ipv6_sock_addr_full(
..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::<libc::sockaddr>(),
size_of::<libc::sockaddr_in>() 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::<libc::sockaddr>(),
size_of::<libc::sockaddr_in6>() 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::<libc::sockaddr_in>() 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::<libc::sockaddr_in6>() 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<T>(
sockfd: i32,
level: libc::c_int,
option_name: libc::c_int,
option_value: T,
) -> io::Result<()> {
let option_len = size_of::<T>() 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: 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: 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: 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::<libc::sockaddr_storage>::zeroed();
let mut len = size_of::<libc::sockaddr_storage>() 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::<libc::sockaddr_in>());
LibcSocketAddr::V4(*(storage.as_ptr() as *const _ as *const libc::sockaddr_in))
}
libc::AF_INET6 => {
assert!(len as usize >= size_of::<libc::sockaddr_in6>());
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;
}
}