diff --git a/crates/wiggle/src/borrow.rs b/crates/wiggle/src/borrow.rs index d0a85288e9ad..06b30db6b254 100644 --- a/crates/wiggle/src/borrow.rs +++ b/crates/wiggle/src/borrow.rs @@ -1,259 +1,113 @@ use crate::{BorrowHandle, GuestError, Region}; -use std::collections::HashMap; -use std::sync::Mutex; - +use std::sync::atomic::{AtomicU32, Ordering::Relaxed}; + +/// A simple borrow checker to implement the API guarantees of Wiggle. +/// +/// This is not a generalized borrow checker and is coarse-grained where it +/// doesn't actually take any regions into account. Instead it only tracks +/// whether there are temporally any shared/mutable borrows. This is +/// more-or-less a poor-man's `RefCell`. +/// +/// Note that this uses `&AtomicU32` because this is passed around as +/// `&BorrowChecker` in all `GuestPtr` structures. This needs to be mutated +/// which might look like it needs `Cell`, but `&Cell` isn't `Sync` +/// and we want futures with `&BorrowChecker` to be `Sync`, so this is an atomic +/// instead. Only one of these is created per-invocation though and it's not +/// actually shared across threads, so mutations here are not done with +/// compare-and-swap but instead just loads/stores. pub struct BorrowChecker { - /// Unfortunately, since the terminology of std::cell and the problem domain of borrow checking - /// overlap, the method calls on this member will be confusing. - /// - /// Also, unfortunately, for now this uses a `Mutex`. The reason for that is - /// that this is shared as `&BorrowChecker` in a bunch of `GuestPtr` values. - /// Through this sharing we still want each `GuestPtr` to be `Send` and the - /// "naive" way to make `&T` `Send` with interior mutability is to use a - /// `Mutex`. Fixing this will likely require rethinking `GuestPtr` one way - /// or another. That needs to happen for other reasons as well (for example - /// to allow for wasm calls to happen while `GuestPtr` values are active), - /// so it's hoped that in a later rethinking of `GuestPtr` we can revisit - /// this and remove this `Mutex`. - bc: Mutex, + // 0 => no borrows + // >0 => shared borrows + // u32::MAX => mutable borrow + state: AtomicU32, } impl BorrowChecker { - /// A `BorrowChecker` manages run-time validation of borrows from a - /// `GuestMemory`. It keeps track of regions of guest memory which are - /// possible to alias with Rust references (via the `GuestSlice` and - /// `GuestStr` structs, which implement `std::ops::Deref` and - /// `std::ops::DerefMut`. It also enforces that `GuestPtr::read` - /// does not access memory with an outstanding mutable borrow, and - /// `GuestPtr::write` does not access memory with an outstanding - /// shared or mutable borrow. pub fn new() -> Self { BorrowChecker { - bc: Mutex::new(InnerBorrowChecker::new()), + state: AtomicU32::new(0), } } - /// Indicates whether any outstanding shared or mutable borrows are known - /// to the `BorrowChecker`. This function must be `false` in order for it - /// to be safe to recursively call into a WebAssembly module, or to - /// manipulate the WebAssembly memory by any other means. - pub fn has_outstanding_borrows(&self) -> bool { - self.bc.lock().unwrap().has_outstanding_borrows() - } pub fn shared_borrow(&self, r: Region) -> Result { - self.bc.lock().unwrap().shared_borrow(r) + match self.state.load(Relaxed) { + n if n >= u32::MAX - 1 => Err(GuestError::PtrBorrowed(r)), + n => { + self.state.store(n + 1, Relaxed); + Ok(BorrowHandle { _priv: () }) + } + } } pub fn mut_borrow(&self, r: Region) -> Result { - self.bc.lock().unwrap().mut_borrow(r) - } - pub fn shared_unborrow(&self, h: BorrowHandle) { - self.bc.lock().unwrap().shared_unborrow(h) - } - pub fn mut_unborrow(&self, h: BorrowHandle) { - self.bc.lock().unwrap().mut_unborrow(h) - } - pub fn is_shared_borrowed(&self, r: Region) -> bool { - self.bc.lock().unwrap().is_shared_borrowed(r) - } - pub fn is_mut_borrowed(&self, r: Region) -> bool { - self.bc.lock().unwrap().is_mut_borrowed(r) - } -} - -#[derive(Debug)] -/// This is a pretty naive way to account for borrows. This datastructure -/// could be made a lot more efficient with some effort. -struct InnerBorrowChecker { - /// Maps from handle to region borrowed. A HashMap is probably not ideal - /// for this but it works. It would be more efficient if we could - /// check `is_borrowed` without an O(n) iteration, by organizing borrows - /// by an ordering of Region. - shared_borrows: HashMap, - mut_borrows: HashMap, - /// Handle to give out for the next borrow. This is the bare minimum of - /// bookkeeping of free handles, and in a pathological case we could run - /// out, hence [`GuestError::BorrowCheckerOutOfHandles`] - next_handle: BorrowHandle, -} - -impl InnerBorrowChecker { - fn new() -> Self { - InnerBorrowChecker { - shared_borrows: HashMap::new(), - mut_borrows: HashMap::new(), - next_handle: BorrowHandle(0), + match self.state.load(Relaxed) { + 0 => { + self.state.store(u32::MAX, Relaxed); + Ok(BorrowHandle { _priv: () }) + } + _ => Err(GuestError::PtrBorrowed(r)), } } - - fn has_outstanding_borrows(&self) -> bool { - !(self.shared_borrows.is_empty() && self.mut_borrows.is_empty()) + pub fn shared_unborrow(&self, _: BorrowHandle) { + let prev = self.state.load(Relaxed); + debug_assert!(prev > 0); + self.state.store(prev - 1, Relaxed); } - - fn is_shared_borrowed(&self, r: Region) -> bool { - self.shared_borrows.values().any(|b| b.overlaps(r)) + pub fn mut_unborrow(&self, _: BorrowHandle) { + let prev = self.state.load(Relaxed); + debug_assert!(prev == u32::MAX); + self.state.store(0, Relaxed); } - fn is_mut_borrowed(&self, r: Region) -> bool { - self.mut_borrows.values().any(|b| b.overlaps(r)) - } - - fn new_handle(&mut self) -> Result { - // Reset handles to 0 if all handles have been returned. - if self.shared_borrows.is_empty() && self.mut_borrows.is_empty() { - self.next_handle = BorrowHandle(0); - } - let h = self.next_handle; - // Get the next handle. Since we don't recycle handles until all of - // them have been returned, there is a pathological case where a user - // may make a Very Large (usize::MAX) number of valid borrows and - // unborrows while always keeping at least one borrow outstanding, and - // we will run out of borrow handles. - self.next_handle = BorrowHandle( - h.0.checked_add(1) - .ok_or_else(|| GuestError::BorrowCheckerOutOfHandles)?, - ); - Ok(h) + pub fn can_read(&self, _: Region) -> bool { + self.state.load(Relaxed) != u32::MAX } - - fn shared_borrow(&mut self, r: Region) -> Result { - if self.is_mut_borrowed(r) { - return Err(GuestError::PtrBorrowed(r)); - } - let h = self.new_handle()?; - self.shared_borrows.insert(h, r); - Ok(h) - } - - fn mut_borrow(&mut self, r: Region) -> Result { - if self.is_shared_borrowed(r) || self.is_mut_borrowed(r) { - return Err(GuestError::PtrBorrowed(r)); - } - let h = self.new_handle()?; - self.mut_borrows.insert(h, r); - Ok(h) - } - - fn shared_unborrow(&mut self, h: BorrowHandle) { - let removed = self.shared_borrows.remove(&h); - debug_assert!(removed.is_some(), "double-freed shared borrow"); - } - - fn mut_unborrow(&mut self, h: BorrowHandle) { - let removed = self.mut_borrows.remove(&h); - debug_assert!(removed.is_some(), "double-freed mut borrow"); + pub fn can_write(&self, _: Region) -> bool { + self.state.load(Relaxed) == 0 } } #[cfg(test)] mod test { use super::*; - #[test] - fn nonoverlapping() { - let mut bs = InnerBorrowChecker::new(); - let r1 = Region::new(0, 10); - let r2 = Region::new(10, 10); - assert!(!r1.overlaps(r2)); - bs.mut_borrow(r1).expect("can borrow r1"); - bs.mut_borrow(r2).expect("can borrow r2"); - - let mut bs = InnerBorrowChecker::new(); - let r1 = Region::new(10, 10); - let r2 = Region::new(0, 10); - assert!(!r1.overlaps(r2)); - bs.mut_borrow(r1).expect("can borrow r1"); - bs.mut_borrow(r2).expect("can borrow r2"); - } - - #[test] - fn overlapping() { - let mut bs = InnerBorrowChecker::new(); - let r1 = Region::new(0, 10); - let r2 = Region::new(9, 10); - assert!(r1.overlaps(r2)); - bs.shared_borrow(r1).expect("can borrow r1"); - assert!(bs.mut_borrow(r2).is_err(), "cant mut borrow r2"); - bs.shared_borrow(r2).expect("can shared borrow r2"); - - let mut bs = InnerBorrowChecker::new(); - let r1 = Region::new(0, 10); - let r2 = Region::new(2, 5); - assert!(r1.overlaps(r2)); - bs.shared_borrow(r1).expect("can borrow r1"); - assert!(bs.mut_borrow(r2).is_err(), "cant borrow r2"); - bs.shared_borrow(r2).expect("can shared borrow r2"); - - let mut bs = InnerBorrowChecker::new(); - let r1 = Region::new(9, 10); - let r2 = Region::new(0, 10); - assert!(r1.overlaps(r2)); - bs.shared_borrow(r1).expect("can borrow r1"); - assert!(bs.mut_borrow(r2).is_err(), "cant borrow r2"); - bs.shared_borrow(r2).expect("can shared borrow r2"); - - let mut bs = InnerBorrowChecker::new(); - let r1 = Region::new(2, 5); - let r2 = Region::new(0, 10); - assert!(r1.overlaps(r2)); - bs.shared_borrow(r1).expect("can borrow r1"); - assert!(bs.mut_borrow(r2).is_err(), "cant borrow r2"); - bs.shared_borrow(r2).expect("can shared borrow r2"); - - let mut bs = InnerBorrowChecker::new(); - let r1 = Region::new(2, 5); - let r2 = Region::new(10, 5); - let r3 = Region::new(15, 5); - let r4 = Region::new(0, 10); - assert!(r1.overlaps(r4)); - bs.shared_borrow(r1).expect("can borrow r1"); - bs.shared_borrow(r2).expect("can borrow r2"); - bs.shared_borrow(r3).expect("can borrow r3"); - assert!(bs.mut_borrow(r4).is_err(), "cant mut borrow r4"); - bs.shared_borrow(r4).expect("can shared borrow r4"); - } #[test] - fn unborrowing() { - let mut bs = InnerBorrowChecker::new(); - let r1 = Region::new(0, 10); - let r2 = Region::new(10, 10); - assert!(!r1.overlaps(r2)); - assert_eq!(bs.has_outstanding_borrows(), false, "start with no borrows"); - let h1 = bs.mut_borrow(r1).expect("can borrow r1"); - assert_eq!(bs.has_outstanding_borrows(), true, "h1 is outstanding"); - let h2 = bs.mut_borrow(r2).expect("can borrow r2"); - - assert!(bs.mut_borrow(r2).is_err(), "can't borrow r2 twice"); - bs.mut_unborrow(h2); - assert_eq!( - bs.has_outstanding_borrows(), - true, - "h1 is still outstanding" - ); - bs.mut_unborrow(h1); - assert_eq!(bs.has_outstanding_borrows(), false, "no remaining borrows"); - - let _h3 = bs - .mut_borrow(r2) - .expect("can borrow r2 again now that its been unborrowed"); - - // Lets try again with shared: - - let mut bs = InnerBorrowChecker::new(); - let r1 = Region::new(0, 10); - let r2 = Region::new(10, 10); - assert!(!r1.overlaps(r2)); - assert_eq!(bs.has_outstanding_borrows(), false, "start with no borrows"); - let h1 = bs.shared_borrow(r1).expect("can borrow r1"); - assert_eq!(bs.has_outstanding_borrows(), true, "h1 is outstanding"); - let h2 = bs.shared_borrow(r2).expect("can borrow r2"); - let h3 = bs.shared_borrow(r2).expect("can shared borrow r2 twice"); - - bs.shared_unborrow(h2); - assert_eq!( - bs.has_outstanding_borrows(), - true, - "h1, h3 still outstanding" - ); - bs.shared_unborrow(h1); - bs.shared_unborrow(h3); - assert_eq!(bs.has_outstanding_borrows(), false, "no remaining borrows"); + fn smoke() { + let b = BorrowChecker::new(); + let mut next = 0; + let mut region = || { + let a = next; + next += 1; + Region::new(a, a + 1) + }; + + // can read/write initially + assert!(b.can_read(region())); + assert!(b.can_write(region())); + + // can shared borrow multiple times which will prevent mutable borrows + let h1 = b.shared_borrow(region()).unwrap(); + let h2 = b.shared_borrow(region()).unwrap(); + assert!(b.mut_borrow(region()).is_err()); + + // can read, but can't write, while there are shared borrows + assert!(b.can_read(region())); + assert!(!b.can_write(region())); + + // releasing shared borrows enables reading/writing again + b.shared_unborrow(h1); + b.shared_unborrow(h2); + assert!(b.can_read(region())); + assert!(b.can_write(region())); + + // mutable borrow disallows shared borrows + let h1 = b.mut_borrow(region()).unwrap(); + assert!(b.shared_borrow(region()).is_err()); + + // active mutable borrows disallows reads/writes + assert!(!b.can_read(region())); + assert!(!b.can_write(region())); + + // releasing the mutable borrows allows raeding/writing again + b.mut_unborrow(h1); + assert!(b.can_read(region())); + assert!(b.can_write(region())); } } diff --git a/crates/wiggle/src/guest_type.rs b/crates/wiggle/src/guest_type.rs index 732f9e07ce8d..52458b8cf70f 100644 --- a/crates/wiggle/src/guest_type.rs +++ b/crates/wiggle/src/guest_type.rs @@ -81,7 +81,7 @@ macro_rules! integer_primitives { // // Note that shared memories don't allow borrows and other // shared borrows are ok to overlap with this. - if ptr.mem().is_mut_borrowed(region) { + if !ptr.mem().can_read(region) { return Err(GuestError::PtrBorrowed(region)); } @@ -106,7 +106,7 @@ macro_rules! integer_primitives { let offset = ptr.offset(); let (host_ptr, region) = super::validate_size_align::(ptr.mem(), offset, 1)?; let host_ptr = &host_ptr[0]; - if ptr.mem().is_shared_borrowed(region) || ptr.mem().is_mut_borrowed(region) { + if !ptr.mem().can_write(region) { return Err(GuestError::PtrBorrowed(region)); } let atomic_value_ref: &$ty_atomic = @@ -139,7 +139,7 @@ macro_rules! float_primitives { 1, )?; let host_ptr = &host_ptr[0]; - if ptr.mem().is_mut_borrowed(region) { + if !ptr.mem().can_read(region) { return Err(GuestError::PtrBorrowed(region)); } let atomic_value_ref: &$ty_atomic = @@ -158,7 +158,7 @@ macro_rules! float_primitives { 1, )?; let host_ptr = &host_ptr[0]; - if ptr.mem().is_shared_borrowed(region) || ptr.mem().is_mut_borrowed(region) { + if !ptr.mem().can_write(region) { return Err(GuestError::PtrBorrowed(region)); } let atomic_value_ref: &$ty_atomic = diff --git a/crates/wiggle/src/lib.rs b/crates/wiggle/src/lib.rs index 66fc3b49a38e..acdd99feb338 100644 --- a/crates/wiggle/src/lib.rs +++ b/crates/wiggle/src/lib.rs @@ -72,8 +72,7 @@ pub mod wasmtime_crate { /// into the memory. The safety of this mechanism depends on there being exactly /// one associated tracking of borrows for a given WebAssembly memory. There /// must be no other reads or writes of WebAssembly the memory by either Rust or -/// WebAssembly code while there are any outstanding borrows, as given by -/// `GuestMemory::has_outstanding_borrows()`. +/// WebAssembly code while there are any outstanding borrows. /// /// # Using References /// @@ -83,12 +82,9 @@ pub mod wasmtime_crate { /// into the memory region given by a `GuestMemory`. /// /// These smart pointers are dynamically borrow-checked by the borrow checker -/// methods on this trait. While a `GuestSlice` or a `GuestStr` are live, the -/// [`GuestMemory::has_outstanding_borrows()`] method will always return `true`. -/// If you need to re-enter the guest or otherwise read or write to the contents -/// of a WebAssembly memory, all `GuestSlice`s and `GuestStr`s for the memory -/// must be dropped, at which point `GuestMemory::has_outstanding_borrows()` -/// will return `false`. +/// methods on this trait. While a `GuestSlice` or a `GuestStr` are live, +/// WebAssembly cannot be reentered because the store's borrow is connected to +/// the relevant `'a` lifetime on the guest pointer. pub unsafe trait GuestMemory: Send + Sync { /// Returns the base allocation of this guest memory, located in host /// memory. @@ -115,36 +111,32 @@ pub unsafe trait GuestMemory: Send + Sync { GuestPtr::new(self, offset) } - /// Indicates whether any outstanding borrows are known to the - /// `GuestMemory`. This function must be `false` in order for it to be - /// safe to recursively call into a WebAssembly module, or to manipulate - /// the WebAssembly memory by any other means. - fn has_outstanding_borrows(&self) -> bool; - /// Check if a region of linear memory is exclusively borrowed. This is called during any - /// `GuestPtr::read` or `GuestPtr::write` operation to ensure that wiggle is not reading or - /// writing a region of memory which Rust believes it has exclusive access to. - fn is_mut_borrowed(&self, r: Region) -> bool; - /// Check if a region of linear memory has any shared borrows. - fn is_shared_borrowed(&self, r: Region) -> bool; - /// Exclusively borrow a region of linear memory. This is used when constructing a - /// `GuestSliceMut` or `GuestStrMut`. Those types will give Rust `&mut` access - /// to the region of linear memory, therefore, the `GuestMemory` impl must - /// guarantee that at most one `BorrowHandle` is issued to a given region, - /// `GuestMemory::has_outstanding_borrows` is true for the duration of the - /// borrow, and that `GuestMemory::is_mut_borrowed` of any overlapping region - /// is false for the duration of the borrow. + /// Check if a region of memory can be read. + /// + /// This will only return `true` if there are no active mutable borrows. + fn can_read(&self, r: Region) -> bool; + + /// Check if a region of memory can be written. + /// + /// This will only return `true` if there are no active borrows. + fn can_write(&self, r: Region) -> bool; + + /// Acquires a mutable borrow on a region of memory. + /// + /// Only succeeds if there are no active shared or mutable borrows and this + /// is not a `shared` WebAssembly memory. fn mut_borrow(&self, r: Region) -> Result; - /// Shared borrow a region of linear memory. This is used when constructing a - /// `GuestSlice` or `GuestStr`. Those types will give Rust `&` (shared reference) access - /// to the region of linear memory. + + /// Acquires a shared borrow on a region of memory. + /// + /// Only succeeds if there are no active mutable borrows and this is not a + /// `shared` WebAssembly memory. fn shared_borrow(&self, r: Region) -> Result; - /// Unborrow a previously borrowed mutable region. As long as `GuestSliceMut` and - /// `GuestStrMut` are implemented correctly, a mut `BorrowHandle` should only be - /// unborrowed once. + + /// Undoes a borrow by `mut_borrow`. fn mut_unborrow(&self, h: BorrowHandle); - /// Unborrow a previously borrowed shared region. As long as `GuestSlice` and - /// `GuestStr` are implemented correctly, a shared `BorrowHandle` should only be - /// unborrowed once. + + /// Undoes a borrow by `shared_borrow`. fn shared_unborrow(&self, h: BorrowHandle); /// Check if the underlying memory is shared across multiple threads; e.g., @@ -206,21 +198,20 @@ fn validate_size_align<'a, T: GuestTypeTransparent<'a>>( /// consumed by `{mut, shared}_unborrow`. Only the `GuestMemory` impl should ever construct /// a `BorrowHandle` or inspect its contents. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub struct BorrowHandle(pub usize); +pub struct BorrowHandle { + _priv: (), +} // Forwarding trait implementations to the original type unsafe impl<'a, T: ?Sized + GuestMemory> GuestMemory for &'a T { fn base(&self) -> &[UnsafeCell] { T::base(self) } - fn has_outstanding_borrows(&self) -> bool { - T::has_outstanding_borrows(self) + fn can_read(&self, r: Region) -> bool { + T::can_read(self, r) } - fn is_mut_borrowed(&self, r: Region) -> bool { - T::is_mut_borrowed(self, r) - } - fn is_shared_borrowed(&self, r: Region) -> bool { - T::is_shared_borrowed(self, r) + fn can_write(&self, r: Region) -> bool { + T::can_write(self, r) } fn mut_borrow(&self, r: Region) -> Result { T::mut_borrow(self, r) @@ -240,14 +231,11 @@ unsafe impl<'a, T: ?Sized + GuestMemory> GuestMemory for &'a mut T { fn base(&self) -> &[UnsafeCell] { T::base(self) } - fn has_outstanding_borrows(&self) -> bool { - T::has_outstanding_borrows(self) - } - fn is_mut_borrowed(&self, r: Region) -> bool { - T::is_mut_borrowed(self, r) + fn can_read(&self, r: Region) -> bool { + T::can_read(self, r) } - fn is_shared_borrowed(&self, r: Region) -> bool { - T::is_shared_borrowed(self, r) + fn can_write(&self, r: Region) -> bool { + T::can_write(self, r) } fn mut_borrow(&self, r: Region) -> Result { T::mut_borrow(self, r) @@ -267,14 +255,11 @@ unsafe impl GuestMemory for Box { fn base(&self) -> &[UnsafeCell] { T::base(self) } - fn has_outstanding_borrows(&self) -> bool { - T::has_outstanding_borrows(self) + fn can_read(&self, r: Region) -> bool { + T::can_read(self, r) } - fn is_mut_borrowed(&self, r: Region) -> bool { - T::is_mut_borrowed(self, r) - } - fn is_shared_borrowed(&self, r: Region) -> bool { - T::is_shared_borrowed(self, r) + fn can_write(&self, r: Region) -> bool { + T::can_write(self, r) } fn mut_borrow(&self, r: Region) -> Result { T::mut_borrow(self, r) @@ -294,14 +279,11 @@ unsafe impl GuestMemory for Arc { fn base(&self) -> &[UnsafeCell] { T::base(self) } - fn has_outstanding_borrows(&self) -> bool { - T::has_outstanding_borrows(self) - } - fn is_mut_borrowed(&self, r: Region) -> bool { - T::is_mut_borrowed(self, r) + fn can_read(&self, r: Region) -> bool { + T::can_read(self, r) } - fn is_shared_borrowed(&self, r: Region) -> bool { - T::is_shared_borrowed(self, r) + fn can_write(&self, r: Region) -> bool { + T::can_write(self, r) } fn mut_borrow(&self, r: Region) -> Result { T::mut_borrow(self, r) @@ -538,10 +520,11 @@ 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]` via the `Deref` trait. The region of memory - /// backing the slice will be marked as shareably borrowed by the - /// [`GuestMemory`] until the `GuestSlice` is dropped. Multiple shareable - /// borrows of the same memory are permitted, but only one mutable borrow. + /// that can be used as a `&[T]` via the `Deref` trait. + /// + /// This method will flag the entire linear memory as marked with a shared + /// borrow. This means that any writes to memory are disallowed until + /// the returned `GuestSlice` is dropped. /// /// This function will return a `GuestSlice` into host memory if all checks /// succeed (valid utf-8, valid pointers, memory is not borrowed, etc.). If @@ -564,9 +547,11 @@ impl<'a, T> GuestPtr<'a, [T]> { /// 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. + /// `Deref` and `DerefMut` traits. + /// + /// This method will flag the entire linear memory as marked with a mutable + /// borrow. This means that all reads/writes to memory are disallowed until + /// the returned `GuestSliceMut` type is dropped. /// /// This function will return a `GuestSliceMut` into host memory if all /// checks succeed (valid utf-8, valid pointers, memory is not borrowed, diff --git a/crates/wiggle/src/wasmtime.rs b/crates/wiggle/src/wasmtime.rs index 6972c52698b0..21933fc7aaf9 100644 --- a/crates/wiggle/src/wasmtime.rs +++ b/crates/wiggle/src/wasmtime.rs @@ -63,16 +63,12 @@ unsafe impl GuestMemory for WasmtimeGuestMemory<'_> { // checking a flag before calling the more expensive borrow-checker methods. #[inline] - fn has_outstanding_borrows(&self) -> bool { - !self.shared && self.bc.has_outstanding_borrows() + fn can_read(&self, r: Region) -> bool { + self.shared || self.bc.can_read(r) } #[inline] - fn is_shared_borrowed(&self, r: Region) -> bool { - !self.shared && self.bc.is_shared_borrowed(r) - } - #[inline] - fn is_mut_borrowed(&self, r: Region) -> bool { - !self.shared && self.bc.is_mut_borrowed(r) + fn can_write(&self, r: Region) -> bool { + self.shared || self.bc.can_write(r) } #[inline] fn shared_borrow(&self, r: Region) -> Result { diff --git a/crates/wiggle/test-helpers/src/lib.rs b/crates/wiggle/test-helpers/src/lib.rs index 0a29b3a56aca..d51685be7a1b 100644 --- a/crates/wiggle/test-helpers/src/lib.rs +++ b/crates/wiggle/test-helpers/src/lib.rs @@ -121,14 +121,11 @@ unsafe impl GuestMemory for HostMemory { let ptr = self.buffer.cell.get(); unsafe { std::slice::from_raw_parts(ptr.cast(), (*ptr).len()) } } - fn has_outstanding_borrows(&self) -> bool { - self.bc.has_outstanding_borrows() + fn can_read(&self, r: Region) -> bool { + self.bc.can_read(r) } - fn is_shared_borrowed(&self, r: Region) -> bool { - self.bc.is_shared_borrowed(r) - } - fn is_mut_borrowed(&self, r: Region) -> bool { - self.bc.is_mut_borrowed(r) + fn can_write(&self, r: Region) -> bool { + self.bc.can_write(r) } fn mut_borrow(&self, r: Region) -> Result { self.bc.mut_borrow(r)