diff --git a/crates/wiggle/src/lib.rs b/crates/wiggle/src/lib.rs index a625a48906..f28fdb8045 100644 --- a/crates/wiggle/src/lib.rs +++ b/crates/wiggle/src/lib.rs @@ -301,12 +301,14 @@ unsafe impl GuestMemory for Arc { /// Note that the type parameter does not need to implement the `Sized` trait, /// so you can implement types such as this: /// -/// * `GuestPtr<'_, str>` - a pointer to a guest string. Has the method -/// [`GuestPtr::as_str`], which gives a dynamically borrow-checked -/// `GuestStr<'_>`, which `DerefMut`s to a `&mut str`. -/// * `GuestPtr<'_, [T]>` - a pointer to a guest array. Has the method -/// [`GuestPtr::as_slice`], which gives a dynamically borrow-checked -/// `GuestSlice<'_, T>`, which `DerefMut`s to a `&mut [T]`. +/// * `GuestPtr<'_, str>` - a pointer to a guest string. Has the methods +/// [`GuestPtr::as_str_mut`], which gives a dynamically borrow-checked +/// `GuestStrMut<'_>`, which `DerefMut`s to a `&mut str`, and +/// [`GuestPtr::as_str`], which is the immutable version of same. +/// * `GuestPtr<'_, [T]>` - a pointer to a guest array. Has methods +/// [`GuestPtr::as_slice_mut`], which gives a dynamically borrow-checked +/// `GuestSliceMut<'_, T>`, which `DerefMut`s to a `&mut [T]` and +/// [`GuestPtr::as_slice`], which is the immutable version of same. /// /// Unsized types such as this may have extra methods and won't have methods /// like [`GuestPtr::read`] or [`GuestPtr::write`]. @@ -486,14 +488,58 @@ impl<'a, T> GuestPtr<'a, [T]> { /// Attempts to create a [`GuestSlice<'_, T>`] from this pointer, performing /// bounds checks and type validation. The `GuestSlice` is a smart pointer - /// that can be used as a `&[T]` or a `&mut [T]` via the `Deref` and `DerefMut` - /// traits. The region of memory backing the slice will be marked as borrowed - /// by the [`GuestMemory`] until the `GuestSlice` is dropped. + /// that can be used as a `&[T]` via the `Deref` trait. + /// The region of memory backing the slice will be marked as immutably + /// borrowed by the [`GuestMemory`] until the `GuestSlice` is dropped. + /// Multiple immutable borrows of the same memory are permitted, but only + /// one mutable borrow. /// /// This function will return a `GuestSlice` into host memory if all checks /// succeed (valid utf-8, valid pointers, memory is not borrowed, etc). If /// any checks fail then `GuestError` will be returned. pub fn as_slice(&self) -> Result, GuestError> + where + T: GuestTypeTransparent<'a>, + { + let len = match self.pointer.1.checked_mul(T::guest_size()) { + Some(l) => l, + None => return Err(GuestError::PtrOverflow), + }; + let ptr = + self.mem + .validate_size_align(self.pointer.0, T::guest_align(), len)? as *mut T; + + let borrow = self.mem.immut_borrow(Region { + start: self.pointer.0, + len, + })?; + + // Validate all elements in slice. + // SAFETY: ptr has been validated by self.mem.validate_size_align + for offs in 0..self.pointer.1 { + T::validate(unsafe { ptr.add(offs as usize) })?; + } + + // SAFETY: iff there are no overlapping mut borrows it is valid to construct a &[T] + let ptr = unsafe { slice::from_raw_parts(ptr, self.pointer.1 as usize) }; + + Ok(GuestSlice { + ptr, + mem: self.mem, + borrow, + }) + } + + /// Attempts to create a [`GuestSliceMut<'_, T>`] from this pointer, performing + /// bounds checks and type validation. The `GuestSliceMut` is a smart pointer + /// that can be used as a `&[T]` or a `&mut [T]` via the `Deref` and `DerefMut` + /// traits. The region of memory backing the slice will be marked as borrowed + /// by the [`GuestMemory`] until the `GuestSlice` is dropped. + /// + /// This function will return a `GuestSliceMut` into host memory if all checks + /// succeed (valid utf-8, valid pointers, memory is not borrowed, etc). If + /// any checks fail then `GuestError` will be returned. + pub fn as_slice_mut(&self) -> Result, GuestError> where T: GuestTypeTransparent<'a>, { @@ -519,7 +565,7 @@ impl<'a, T> GuestPtr<'a, [T]> { // SAFETY: iff there are no overlapping borrows it is valid to construct a &mut [T] let ptr = unsafe { slice::from_raw_parts_mut(ptr, self.pointer.1 as usize) }; - Ok(GuestSlice { + Ok(GuestSliceMut { ptr, mem: self.mem, borrow, @@ -543,7 +589,7 @@ impl<'a, T> GuestPtr<'a, [T]> { T: GuestTypeTransparent<'a> + Copy, { // bounds check ... - let mut self_slice = self.as_slice()?; + let mut self_slice = self.as_slice_mut()?; // ... length check ... if self_slice.len() != slice.len() { return Err(GuestError::SliceLengthsDiffer); @@ -621,9 +667,9 @@ impl<'a> GuestPtr<'a, str> { /// Attempts to create a [`GuestStr<'_>`] from this pointer, performing /// bounds checks and utf-8 checks. The resulting `GuestStr` can be used - /// as a `&str` or `&mut str` via the `Deref` and `DerefMut` traits. The - /// region of memory backing the `str` will be marked as borrowed by the - /// [`GuestMemory`] until the `GuestStr` is dropped. + /// as a `&str` via the `Deref` trait. The region of memory backing the + /// `str` will be marked as immutably borrowed by the [`GuestMemory`] + /// until the `GuestStr` is dropped. /// /// This function will return `GuestStr` into host memory if all checks /// succeed (valid utf-8, valid pointers, etc). If any checks fail then @@ -633,6 +679,39 @@ impl<'a> GuestPtr<'a, str> { .mem .validate_size_align(self.pointer.0, 1, self.pointer.1)?; + let borrow = self.mem.immut_borrow(Region { + start: self.pointer.0, + len: self.pointer.1, + })?; + + // SAFETY: iff there are no overlapping borrows it is ok to construct + // a &mut str. + let ptr = unsafe { slice::from_raw_parts(ptr, self.pointer.1 as usize) }; + // Validate that contents are utf-8: + match str::from_utf8(ptr) { + Ok(ptr) => Ok(GuestStr { + ptr, + mem: self.mem, + borrow, + }), + Err(e) => Err(GuestError::InvalidUtf8(e)), + } + } + + /// Attempts to create a [`GuestStrMut<'_>`] from this pointer, performing + /// bounds checks and utf-8 checks. The resulting `GuestStrMut` can be used + /// as a `&str` or `&mut str` via the `Deref` and `DerefMut` traits. The + /// region of memory backing the `str` will be marked as borrowed by the + /// [`GuestMemory`] until the `GuestStrMut` is dropped. + /// + /// This function will return `GuestStrMut` into host memory if all checks + /// succeed (valid utf-8, valid pointers, etc). If any checks fail then + /// `GuestError` will be returned. + pub fn as_str_mut(&self) -> Result, GuestError> { + let ptr = self + .mem + .validate_size_align(self.pointer.0, 1, self.pointer.1)?; + let borrow = self.mem.mut_borrow(Region { start: self.pointer.0, len: self.pointer.1, @@ -643,7 +722,7 @@ impl<'a> GuestPtr<'a, str> { let ptr = unsafe { slice::from_raw_parts_mut(ptr, self.pointer.1 as usize) }; // Validate that contents are utf-8: match str::from_utf8_mut(ptr) { - Ok(ptr) => Ok(GuestStr { + Ok(ptr) => Ok(GuestStrMut { ptr, mem: self.mem, borrow, @@ -675,11 +754,10 @@ impl fmt::Debug for GuestPtr<'_, T> { } } -/// A smart pointer to a mutable slice in guest memory. -/// Usable as a `&'a [T]` via [`std::ops::Deref`] and as a `&'a mut [T]` via -/// [`std::ops::DerefMut`]. +/// A smart pointer to an immutable slice in guest memory. +/// Usable as a `&'a [T]` via [`std::ops::Deref`]. pub struct GuestSlice<'a, T> { - ptr: &'a mut [T], + ptr: &'a [T], mem: &'a dyn GuestMemory, borrow: BorrowHandle, } @@ -691,23 +769,44 @@ impl<'a, T> std::ops::Deref for GuestSlice<'a, T> { } } -impl<'a, T> std::ops::DerefMut for GuestSlice<'a, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - self.ptr - } -} - impl<'a, T> Drop for GuestSlice<'a, T> { fn drop(&mut self) { self.mem.unborrow(self.borrow) } } -/// A smart pointer to a mutable `str` in guest memory. -/// Usable as a `&'a str` via [`std::ops::Deref`] and as a `&'a mut str` via +/// A smart pointer to a mutable slice in guest memory. +/// Usable as a `&'a [T]` via [`std::ops::Deref`] and as a `&'a mut [T]` via /// [`std::ops::DerefMut`]. +pub struct GuestSliceMut<'a, T> { + ptr: &'a mut [T], + mem: &'a dyn GuestMemory, + borrow: BorrowHandle, +} + +impl<'a, T> std::ops::Deref for GuestSliceMut<'a, T> { + type Target = [T]; + fn deref(&self) -> &Self::Target { + self.ptr + } +} + +impl<'a, T> std::ops::DerefMut for GuestSliceMut<'a, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.ptr + } +} + +impl<'a, T> Drop for GuestSliceMut<'a, T> { + fn drop(&mut self) { + self.mem.unborrow(self.borrow) + } +} + +/// A smart pointer to an immutable `str` in guest memory. +/// Usable as a `&'a str` via [`std::ops::Deref`]. pub struct GuestStr<'a> { - ptr: &'a mut str, + ptr: &'a str, mem: &'a dyn GuestMemory, borrow: BorrowHandle, } @@ -719,13 +818,35 @@ impl<'a> std::ops::Deref for GuestStr<'a> { } } -impl<'a> std::ops::DerefMut for GuestStr<'a> { +impl<'a> Drop for GuestStr<'a> { + fn drop(&mut self) { + self.mem.unborrow(self.borrow) + } +} + +/// A smart pointer to a mutable `str` in guest memory. +/// Usable as a `&'a str` via [`std::ops::Deref`] and as a `&'a mut str` via +/// [`std::ops::DerefMut`]. +pub struct GuestStrMut<'a> { + ptr: &'a mut str, + mem: &'a dyn GuestMemory, + borrow: BorrowHandle, +} + +impl<'a> std::ops::Deref for GuestStrMut<'a> { + type Target = str; + fn deref(&self) -> &Self::Target { + self.ptr + } +} + +impl<'a> std::ops::DerefMut for GuestStrMut<'a> { fn deref_mut(&mut self) -> &mut Self::Target { self.ptr } } -impl<'a> Drop for GuestStr<'a> { +impl<'a> Drop for GuestStrMut<'a> { fn drop(&mut self) { self.mem.unborrow(self.borrow) }