mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-26 13:01:27 +03:00
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:
+2
-2
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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?;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -311,6 +311,7 @@
|
||||
//
|
||||
// Library features (core):
|
||||
// tidy-alphabetical-start
|
||||
#![feature(borrowed_buf_init)]
|
||||
#![feature(bstr)]
|
||||
#![feature(bstr_internals)]
|
||||
#![feature(cast_maybe_uninit)]
|
||||
|
||||
@@ -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");
|
||||
})
|
||||
|
||||
@@ -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());
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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>.
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 @@
|
||||
116458d0a5ae01cd517cabd2d1aee7f5457018ab
|
||||
55e86c996809902e8bbad512cfb4d2c18be446d9
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user