diff --git a/crates/wiggle/src/guest_type.rs b/crates/wiggle/src/guest_type.rs index 4359d3745f..27715254a8 100644 --- a/crates/wiggle/src/guest_type.rs +++ b/crates/wiggle/src/guest_type.rs @@ -85,7 +85,7 @@ macro_rules! primitives { start: offset, len: size, }; - if ptr.borrow_checker().is_borrowed(region) { + if ptr.mem().is_borrowed(region) { return Err(GuestError::PtrBorrowed(region)); } Ok(unsafe { *host_ptr.cast::() }) @@ -104,7 +104,7 @@ macro_rules! primitives { start: offset, len: size, }; - if ptr.borrow_checker().is_borrowed(region) { + if ptr.mem().is_borrowed(region) { return Err(GuestError::PtrBorrowed(region)); } unsafe { diff --git a/crates/wiggle/src/lib.rs b/crates/wiggle/src/lib.rs index e756dc8dbb..1887b6cb4c 100644 --- a/crates/wiggle/src/lib.rs +++ b/crates/wiggle/src/lib.rs @@ -11,15 +11,12 @@ pub use wiggle_macro::from_witx; #[cfg(feature = "wiggle_metadata")] pub use witx; -mod borrow; mod error; mod guest_type; mod region; pub extern crate tracing; -pub use borrow::BorrowChecker; -use borrow::BorrowHandle; pub use error::GuestError; pub use guest_type::{GuestErrorType, GuestType, GuestTypeTransparent}; pub use region::Region; @@ -55,24 +52,27 @@ pub use region::Region; /// must be "somehow nonzero in length" to allow users of `GuestMemory` and /// `GuestPtr` to safely read and write interior data. /// +/// This type also provides methods for run-time borrow checking of references +/// 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()`. /// /// # Using References /// -/// See the safety guarantees of [`BorrowChecker`], which asserts that exactly -/// one `BorrowChecker` may be constructed for each WebAssembly memory. -/// /// The [`GuestMemory::as_slice`] or [`GuestPtr::as_str`] will return smart /// pointers [`GuestSlice`] and [`GuestStr`]. These types, which implement /// [`std::ops::Deref`] and [`std::ops::DerefMut`], provide mutable references /// into the memory region given by a `GuestMemory`. /// -/// These smart pointers are dynamically borrow-checked by the `BorrowChecker` -/// given by [`GuestMemory::borrow_checker()`]. While a `GuestSlice` -/// or a `GuestStr` are live, the [`BorrowChecker::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 `BorrowChecker::has_outstanding_borrows()` will return `false`. +/// 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`. pub unsafe trait GuestMemory { /// Returns the base allocation of this guest memory, located in host /// memory. @@ -86,11 +86,6 @@ pub unsafe trait GuestMemory { /// [`GuestMemory`] documentation. fn base(&self) -> (*mut u8, u32); - /// Gives a reference to the [`BorrowChecker`] used to keep track of each - /// outstanding borrow of the memory region. [`BorrowChecker::new`] safety - /// rules require that exactly one checker exist for each memory region. - fn borrow_checker(&self) -> &BorrowChecker; - /// Validates a guest-relative pointer given various attributes, and returns /// the corresponding host pointer. /// @@ -152,16 +147,53 @@ pub unsafe trait GuestMemory { { 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 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_borrowed(&self, r: Region) -> bool; + /// Borrow a region of linear memory. This is used when constructing a + /// `GuestSlice` or `GuestStr`. 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_borrowed` of any overlapping region + /// is false for the duration of the borrow. + fn borrow(&self, r: Region) -> Result; + /// Unborrow a previously borrowed region. As long as `GuestSlice` and + /// `GuestStr` are implemented correctly, a `BorrowHandle` should only be + /// unborrowed once. + fn unborrow(&self, h: BorrowHandle); } -// Forwarding trait implementations to the original type +/// A handle to a borrow on linear memory. It is produced by `borrow` and +/// consumed by `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); +// Forwarding trait implementations to the original type unsafe impl<'a, T: ?Sized + GuestMemory> GuestMemory for &'a T { fn base(&self) -> (*mut u8, u32) { T::base(self) } - fn borrow_checker(&self) -> &BorrowChecker { - T::borrow_checker(self) + fn has_outstanding_borrows(&self) -> bool { + T::has_outstanding_borrows(self) + } + fn is_borrowed(&self, r: Region) -> bool { + T::is_borrowed(self, r) + } + fn borrow(&self, r: Region) -> Result { + T::borrow(self, r) + } + fn unborrow(&self, h: BorrowHandle) { + T::unborrow(self, h) } } @@ -169,8 +201,17 @@ unsafe impl<'a, T: ?Sized + GuestMemory> GuestMemory for &'a mut T { fn base(&self) -> (*mut u8, u32) { T::base(self) } - fn borrow_checker(&self) -> &BorrowChecker { - T::borrow_checker(self) + fn has_outstanding_borrows(&self) -> bool { + T::has_outstanding_borrows(self) + } + fn is_borrowed(&self, r: Region) -> bool { + T::is_borrowed(self, r) + } + fn borrow(&self, r: Region) -> Result { + T::borrow(self, r) + } + fn unborrow(&self, h: BorrowHandle) { + T::unborrow(self, h) } } @@ -178,8 +219,17 @@ unsafe impl GuestMemory for Box { fn base(&self) -> (*mut u8, u32) { T::base(self) } - fn borrow_checker(&self) -> &BorrowChecker { - T::borrow_checker(self) + fn has_outstanding_borrows(&self) -> bool { + T::has_outstanding_borrows(self) + } + fn is_borrowed(&self, r: Region) -> bool { + T::is_borrowed(self, r) + } + fn borrow(&self, r: Region) -> Result { + T::borrow(self, r) + } + fn unborrow(&self, h: BorrowHandle) { + T::unborrow(self, h) } } @@ -187,8 +237,17 @@ unsafe impl GuestMemory for Rc { fn base(&self) -> (*mut u8, u32) { T::base(self) } - fn borrow_checker(&self) -> &BorrowChecker { - T::borrow_checker(self) + fn has_outstanding_borrows(&self) -> bool { + T::has_outstanding_borrows(self) + } + fn is_borrowed(&self, r: Region) -> bool { + T::is_borrowed(self, r) + } + fn borrow(&self, r: Region) -> Result { + T::borrow(self, r) + } + fn unborrow(&self, h: BorrowHandle) { + T::unborrow(self, h) } } @@ -196,8 +255,17 @@ unsafe impl GuestMemory for Arc { fn base(&self) -> (*mut u8, u32) { T::base(self) } - fn borrow_checker(&self) -> &BorrowChecker { - T::borrow_checker(self) + fn has_outstanding_borrows(&self) -> bool { + T::has_outstanding_borrows(self) + } + fn is_borrowed(&self, r: Region) -> bool { + T::is_borrowed(self, r) + } + fn borrow(&self, r: Region) -> Result { + T::borrow(self, r) + } + fn unborrow(&self, h: BorrowHandle) { + T::unborrow(self, h) } } @@ -282,11 +350,6 @@ impl<'a, T: ?Sized + Pointee> GuestPtr<'a, T> { self.mem } - /// Returns the borrow checker that this pointer uses - pub fn borrow_checker(&self) -> &'a BorrowChecker { - self.mem.borrow_checker() - } - /// Casts this `GuestPtr` type to a different type. /// /// This is a safe method which is useful for simply reinterpreting the type @@ -409,7 +472,7 @@ impl<'a, T> GuestPtr<'a, [T]> { /// 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 [`BorrowChecker`] until the `GuestSlice` is dropped. + /// by the [`GuestMemory`] until the `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 @@ -426,7 +489,7 @@ impl<'a, T> GuestPtr<'a, [T]> { self.mem .validate_size_align(self.pointer.0, T::guest_align(), len)? as *mut T; - let borrow = self.mem.borrow_checker().borrow(Region { + let borrow = self.mem.borrow(Region { start: self.pointer.0, len, })?; @@ -442,7 +505,7 @@ impl<'a, T> GuestPtr<'a, [T]> { Ok(GuestSlice { ptr, - bc: self.mem.borrow_checker(), + mem: self.mem, borrow, }) } @@ -509,7 +572,7 @@ impl<'a> GuestPtr<'a, str> { /// 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 - /// [`BorrowChecker`] until the `GuestStr` is dropped. + /// [`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 @@ -519,7 +582,7 @@ impl<'a> GuestPtr<'a, str> { .mem .validate_size_align(self.pointer.0, 1, self.pointer.1)?; - let borrow = self.mem.borrow_checker().borrow(Region { + let borrow = self.mem.borrow(Region { start: self.pointer.0, len: self.pointer.1, })?; @@ -531,7 +594,7 @@ impl<'a> GuestPtr<'a, str> { match str::from_utf8_mut(ptr) { Ok(ptr) => Ok(GuestStr { ptr, - bc: self.mem.borrow_checker(), + mem: self.mem, borrow, }), Err(e) => Err(GuestError::InvalidUtf8(e)), @@ -566,7 +629,7 @@ impl fmt::Debug for GuestPtr<'_, T> { /// [`std::ops::DerefMut`]. pub struct GuestSlice<'a, T> { ptr: &'a mut [T], - bc: &'a BorrowChecker, + mem: &'a dyn GuestMemory, borrow: BorrowHandle, } @@ -585,7 +648,7 @@ impl<'a, T> std::ops::DerefMut for GuestSlice<'a, T> { impl<'a, T> Drop for GuestSlice<'a, T> { fn drop(&mut self) { - self.bc.unborrow(self.borrow) + self.mem.unborrow(self.borrow) } } @@ -594,7 +657,7 @@ impl<'a, T> Drop for GuestSlice<'a, T> { /// [`std::ops::DerefMut`]. pub struct GuestStr<'a> { ptr: &'a mut str, - bc: &'a BorrowChecker, + mem: &'a dyn GuestMemory, borrow: BorrowHandle, } @@ -613,7 +676,7 @@ impl<'a> std::ops::DerefMut for GuestStr<'a> { impl<'a> Drop for GuestStr<'a> { fn drop(&mut self) { - self.bc.unborrow(self.borrow) + self.mem.unborrow(self.borrow) } } diff --git a/crates/wiggle/src/borrow.rs b/crates/wiggle/test-helpers/src/borrow.rs similarity index 93% rename from crates/wiggle/src/borrow.rs rename to crates/wiggle/test-helpers/src/borrow.rs index 83d96c3448..454a084143 100644 --- a/crates/wiggle/src/borrow.rs +++ b/crates/wiggle/test-helpers/src/borrow.rs @@ -1,10 +1,6 @@ -use crate::error::GuestError; -use crate::region::Region; use std::cell::RefCell; use std::collections::HashMap; - -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub struct BorrowHandle(usize); +use wiggle::{BorrowHandle, GuestError, Region}; pub struct BorrowChecker { bc: RefCell, @@ -16,10 +12,6 @@ impl BorrowChecker { /// `GuestSlice` and `GuestStr` structs, which implement `std::ops::Deref` and /// `std::ops::DerefMut`. It also enforces that `GuestPtr::read` and `GuestPtr::write` do not /// access memory with an outstanding borrow. - /// The safety of this mechanism depends on creating exactly one `BorrowChecker` per - /// 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 - /// `BorrowChecker::has_outstanding_borrows()`. pub unsafe fn new() -> Self { BorrowChecker { bc: RefCell::new(InnerBorrowChecker::new()), diff --git a/crates/wiggle/test-helpers/src/lib.rs b/crates/wiggle/test-helpers/src/lib.rs index 5d03d27088..dbcccd4c77 100644 --- a/crates/wiggle/test-helpers/src/lib.rs +++ b/crates/wiggle/test-helpers/src/lib.rs @@ -1,7 +1,10 @@ use proptest::prelude::*; use std::cell::UnsafeCell; use std::marker; -use wiggle::{BorrowChecker, GuestMemory}; +use wiggle::{BorrowHandle, GuestMemory, Region}; + +mod borrow; +use borrow::BorrowChecker; #[derive(Debug, Clone)] pub struct MemAreas(Vec); @@ -119,8 +122,17 @@ unsafe impl GuestMemory for HostMemory { ((*ptr).as_mut_ptr(), (*ptr).len() as u32) } } - fn borrow_checker(&self) -> &BorrowChecker { - &self.bc + fn has_outstanding_borrows(&self) -> bool { + self.bc.has_outstanding_borrows() + } + fn is_borrowed(&self, r: Region) -> bool { + self.bc.is_borrowed(r) + } + fn borrow(&self, r: Region) -> Result { + self.bc.borrow(r) + } + fn unborrow(&self, h: BorrowHandle) { + self.bc.unborrow(h) } } diff --git a/crates/wiggle/wasmtime/macro/src/lib.rs b/crates/wiggle/wasmtime/macro/src/lib.rs index 2a60df1a8c..b075e54d7d 100644 --- a/crates/wiggle/wasmtime/macro/src/lib.rs +++ b/crates/wiggle/wasmtime/macro/src/lib.rs @@ -212,13 +212,7 @@ fn generate_func( #handle_early_error } }; - // Wiggle does not expose any methods for functions to re-enter the WebAssembly - // instance, or expose the memory via non-wiggle mechanisms. However, the - // user-defined code may end up re-entering the instance, in which case this - // is an incorrect implementation - we require exactly one BorrowChecker exist - // per instance. - let bc = #runtime::BorrowChecker::new(); - let mem = #runtime::WasmtimeGuestMemory::new( mem, bc ); + let mem = #runtime::WasmtimeGuestMemory::new(mem); #target_module::#name_ident( &mut my_cx.borrow_mut(), &mem, diff --git a/crates/wiggle/wasmtime/src/borrow.rs b/crates/wiggle/wasmtime/src/borrow.rs new file mode 100644 index 0000000000..454a084143 --- /dev/null +++ b/crates/wiggle/wasmtime/src/borrow.rs @@ -0,0 +1,188 @@ +use std::cell::RefCell; +use std::collections::HashMap; +use wiggle::{BorrowHandle, GuestError, Region}; + +pub struct BorrowChecker { + bc: RefCell, +} + +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` and `GuestPtr::write` do not + /// access memory with an outstanding borrow. + pub unsafe fn new() -> Self { + BorrowChecker { + bc: RefCell::new(InnerBorrowChecker::new()), + } + } + /// Indicates whether any outstanding 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.borrow().has_outstanding_borrows() + } + + pub(crate) fn borrow(&self, r: Region) -> Result { + self.bc.borrow_mut().borrow(r) + } + pub(crate) fn unborrow(&self, h: BorrowHandle) { + self.bc.borrow_mut().unborrow(h) + } + pub(crate) fn is_borrowed(&self, r: Region) -> bool { + self.bc.borrow().is_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 { + /// Map 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. + 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 { + borrows: HashMap::new(), + next_handle: BorrowHandle(0), + } + } + + fn has_outstanding_borrows(&self) -> bool { + !self.borrows.is_empty() + } + + fn is_borrowed(&self, r: Region) -> bool { + !self.borrows.values().all(|b| !b.overlaps(r)) + } + + fn new_handle(&mut self) -> Result { + // Reset handles to 0 if all handles have been returned. + if self.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) + } + + fn borrow(&mut self, r: Region) -> Result { + if self.is_borrowed(r) { + return Err(GuestError::PtrBorrowed(r)); + } + let h = self.new_handle()?; + self.borrows.insert(h, r); + Ok(h) + } + + fn unborrow(&mut self, h: BorrowHandle) { + let _ = self.borrows.remove(&h); + } +} + +#[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.borrow(r1).expect("can borrow r1"); + bs.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.borrow(r1).expect("can borrow r1"); + bs.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.borrow(r1).expect("can borrow r1"); + assert!(bs.borrow(r2).is_err(), "cant borrow r2"); + + let mut bs = InnerBorrowChecker::new(); + let r1 = Region::new(0, 10); + let r2 = Region::new(2, 5); + assert!(r1.overlaps(r2)); + bs.borrow(r1).expect("can borrow r1"); + assert!(bs.borrow(r2).is_err(), "cant borrow r2"); + + let mut bs = InnerBorrowChecker::new(); + let r1 = Region::new(9, 10); + let r2 = Region::new(0, 10); + assert!(r1.overlaps(r2)); + bs.borrow(r1).expect("can borrow r1"); + assert!(bs.borrow(r2).is_err(), "cant borrow r2"); + + let mut bs = InnerBorrowChecker::new(); + let r1 = Region::new(2, 5); + let r2 = Region::new(0, 10); + assert!(r1.overlaps(r2)); + bs.borrow(r1).expect("can borrow r1"); + assert!(bs.borrow(r2).is_err(), "cant 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.borrow(r1).expect("can borrow r1"); + bs.borrow(r2).expect("can borrow r2"); + bs.borrow(r3).expect("can borrow r3"); + assert!(bs.borrow(r4).is_err(), "cant 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.borrow(r1).expect("can borrow r1"); + assert_eq!(bs.has_outstanding_borrows(), true, "h1 is outstanding"); + let h2 = bs.borrow(r2).expect("can borrow r2"); + + assert!(bs.borrow(r2).is_err(), "can't borrow r2 twice"); + bs.unborrow(h2); + assert_eq!( + bs.has_outstanding_borrows(), + true, + "h1 is still outstanding" + ); + bs.unborrow(h1); + assert_eq!(bs.has_outstanding_borrows(), false, "no remaining borrows"); + + let _h3 = bs + .borrow(r2) + .expect("can borrow r2 again now that its been unborrowed"); + } +} diff --git a/crates/wiggle/wasmtime/src/lib.rs b/crates/wiggle/wasmtime/src/lib.rs index bd4d303ed6..358637aed4 100644 --- a/crates/wiggle/wasmtime/src/lib.rs +++ b/crates/wiggle/wasmtime/src/lib.rs @@ -1,6 +1,10 @@ pub use wasmtime_wiggle_macro::*; pub use wiggle::*; +mod borrow; + +use borrow::BorrowChecker; + /// Lightweight `wasmtime::Memory` wrapper so we can implement the /// `wiggle::GuestMemory` trait on it. pub struct WasmtimeGuestMemory { @@ -9,8 +13,20 @@ pub struct WasmtimeGuestMemory { } impl WasmtimeGuestMemory { - pub fn new(mem: wasmtime::Memory, bc: BorrowChecker) -> Self { - Self { mem, bc } + pub fn new(mem: wasmtime::Memory) -> Self { + Self { + mem, + // Wiggle does not expose any methods for functions to re-enter + // the WebAssembly instance, or expose the memory via non-wiggle + // mechanisms. However, the user-defined code may end up + // re-entering the instance, in which case this is an incorrect + // implementation - we require exactly one BorrowChecker exist per + // instance. + // This BorrowChecker construction is a holdover until it is + // integrated fully with wasmtime: + // https://github.com/bytecodealliance/wasmtime/issues/1917 + bc: unsafe { BorrowChecker::new() }, + } } } @@ -18,7 +34,16 @@ unsafe impl GuestMemory for WasmtimeGuestMemory { fn base(&self) -> (*mut u8, u32) { (self.mem.data_ptr(), self.mem.data_size() as _) } - fn borrow_checker(&self) -> &BorrowChecker { - &self.bc + fn has_outstanding_borrows(&self) -> bool { + self.bc.has_outstanding_borrows() + } + fn is_borrowed(&self, r: Region) -> bool { + self.bc.is_borrowed(r) + } + fn borrow(&self, r: Region) -> Result { + self.bc.borrow(r) + } + fn unborrow(&self, h: BorrowHandle) { + self.bc.unborrow(h) } }