wiggle: make Mut variants of GuestStr, GuestPtr

This commit is contained in:
Pat Hickey
2020-11-18 12:32:21 -08:00
parent 78db3ff13b
commit fc608e392b

View File

@@ -301,12 +301,14 @@ unsafe impl<T: ?Sized + GuestMemory> GuestMemory for Arc<T> {
/// Note that the type parameter does not need to implement the `Sized` trait, /// Note that the type parameter does not need to implement the `Sized` trait,
/// so you can implement types such as this: /// so you can implement types such as this:
/// ///
/// * `GuestPtr<'_, str>` - a pointer to a guest string. Has the method /// * `GuestPtr<'_, str>` - a pointer to a guest string. Has the methods
/// [`GuestPtr::as_str`], which gives a dynamically borrow-checked /// [`GuestPtr::as_str_mut`], which gives a dynamically borrow-checked
/// `GuestStr<'_>`, which `DerefMut`s to a `&mut str`. /// `GuestStrMut<'_>`, which `DerefMut`s to a `&mut str`, and
/// * `GuestPtr<'_, [T]>` - a pointer to a guest array. Has the method /// [`GuestPtr::as_str`], which is the immutable version of same.
/// [`GuestPtr::as_slice`], which gives a dynamically borrow-checked /// * `GuestPtr<'_, [T]>` - a pointer to a guest array. Has methods
/// `GuestSlice<'_, T>`, which `DerefMut`s to a `&mut [T]`. /// [`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 /// Unsized types such as this may have extra methods and won't have methods
/// like [`GuestPtr::read`] or [`GuestPtr::write`]. /// 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 /// Attempts to create a [`GuestSlice<'_, T>`] from this pointer, performing
/// bounds checks and type validation. The `GuestSlice` is a smart pointer /// 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` /// that can be used as a `&[T]` via the `Deref` trait.
/// traits. The region of memory backing the slice will be marked as borrowed /// The region of memory backing the slice will be marked as immutably
/// by the [`GuestMemory`] until the `GuestSlice` is dropped. /// 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 /// This function will return a `GuestSlice` into host memory if all checks
/// succeed (valid utf-8, valid pointers, memory is not borrowed, etc). If /// succeed (valid utf-8, valid pointers, memory is not borrowed, etc). If
/// any checks fail then `GuestError` will be returned. /// any checks fail then `GuestError` will be returned.
pub fn as_slice(&self) -> Result<GuestSlice<'a, T>, GuestError> pub fn as_slice(&self) -> Result<GuestSlice<'a, T>, 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<GuestSliceMut<'a, T>, GuestError>
where where
T: GuestTypeTransparent<'a>, 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] // 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) }; let ptr = unsafe { slice::from_raw_parts_mut(ptr, self.pointer.1 as usize) };
Ok(GuestSlice { Ok(GuestSliceMut {
ptr, ptr,
mem: self.mem, mem: self.mem,
borrow, borrow,
@@ -543,7 +589,7 @@ impl<'a, T> GuestPtr<'a, [T]> {
T: GuestTypeTransparent<'a> + Copy, T: GuestTypeTransparent<'a> + Copy,
{ {
// bounds check ... // bounds check ...
let mut self_slice = self.as_slice()?; let mut self_slice = self.as_slice_mut()?;
// ... length check ... // ... length check ...
if self_slice.len() != slice.len() { if self_slice.len() != slice.len() {
return Err(GuestError::SliceLengthsDiffer); return Err(GuestError::SliceLengthsDiffer);
@@ -621,9 +667,9 @@ impl<'a> GuestPtr<'a, str> {
/// Attempts to create a [`GuestStr<'_>`] from this pointer, performing /// Attempts to create a [`GuestStr<'_>`] from this pointer, performing
/// bounds checks and utf-8 checks. The resulting `GuestStr` can be used /// 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 /// as a `&str` via the `Deref` trait. The region of memory backing the
/// region of memory backing the `str` will be marked as borrowed by the /// `str` will be marked as immutably borrowed by the [`GuestMemory`]
/// [`GuestMemory`] until the `GuestStr` is dropped. /// until the `GuestStr` is dropped.
/// ///
/// This function will return `GuestStr` into host memory if all checks /// This function will return `GuestStr` into host memory if all checks
/// succeed (valid utf-8, valid pointers, etc). If any checks fail then /// succeed (valid utf-8, valid pointers, etc). If any checks fail then
@@ -633,6 +679,39 @@ impl<'a> GuestPtr<'a, str> {
.mem .mem
.validate_size_align(self.pointer.0, 1, self.pointer.1)?; .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<GuestStrMut<'a>, GuestError> {
let ptr = self
.mem
.validate_size_align(self.pointer.0, 1, self.pointer.1)?;
let borrow = self.mem.mut_borrow(Region { let borrow = self.mem.mut_borrow(Region {
start: self.pointer.0, start: self.pointer.0,
len: self.pointer.1, 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) }; let ptr = unsafe { slice::from_raw_parts_mut(ptr, self.pointer.1 as usize) };
// Validate that contents are utf-8: // Validate that contents are utf-8:
match str::from_utf8_mut(ptr) { match str::from_utf8_mut(ptr) {
Ok(ptr) => Ok(GuestStr { Ok(ptr) => Ok(GuestStrMut {
ptr, ptr,
mem: self.mem, mem: self.mem,
borrow, borrow,
@@ -675,11 +754,10 @@ impl<T: ?Sized + Pointee> fmt::Debug for GuestPtr<'_, T> {
} }
} }
/// A smart pointer to a mutable slice in guest memory. /// A smart pointer to an immutable slice in guest memory.
/// Usable as a `&'a [T]` via [`std::ops::Deref`] and as a `&'a mut [T]` via /// Usable as a `&'a [T]` via [`std::ops::Deref`].
/// [`std::ops::DerefMut`].
pub struct GuestSlice<'a, T> { pub struct GuestSlice<'a, T> {
ptr: &'a mut [T], ptr: &'a [T],
mem: &'a dyn GuestMemory, mem: &'a dyn GuestMemory,
borrow: BorrowHandle, 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> { impl<'a, T> Drop for GuestSlice<'a, T> {
fn drop(&mut self) { fn drop(&mut self) {
self.mem.unborrow(self.borrow) self.mem.unborrow(self.borrow)
} }
} }
/// A smart pointer to a mutable `str` in guest memory. /// A smart pointer to a mutable slice in guest memory.
/// Usable as a `&'a str` via [`std::ops::Deref`] and as a `&'a mut str` via /// Usable as a `&'a [T]` via [`std::ops::Deref`] and as a `&'a mut [T]` via
/// [`std::ops::DerefMut`]. /// [`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> { pub struct GuestStr<'a> {
ptr: &'a mut str, ptr: &'a str,
mem: &'a dyn GuestMemory, mem: &'a dyn GuestMemory,
borrow: BorrowHandle, 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 { fn deref_mut(&mut self) -> &mut Self::Target {
self.ptr self.ptr
} }
} }
impl<'a> Drop for GuestStr<'a> { impl<'a> Drop for GuestStrMut<'a> {
fn drop(&mut self) { fn drop(&mut self) {
self.mem.unborrow(self.borrow) self.mem.unborrow(self.borrow)
} }