diff --git a/crates/wiggle/src/borrow.rs b/crates/wiggle/src/borrow.rs index 992ecddeb5..2519f44dae 100644 --- a/crates/wiggle/src/borrow.rs +++ b/crates/wiggle/src/borrow.rs @@ -11,23 +11,36 @@ pub struct BorrowChecker { } impl BorrowChecker { - pub fn new() -> Self { + /// 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. + /// 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()), } } - pub fn borrow(&self, r: Region) -> Result { + /// 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 fn unborrow(&self, h: BorrowHandle) { + pub(crate) fn unborrow(&self, h: BorrowHandle) { self.bc.borrow_mut().unborrow(h) } - pub fn is_borrowed(&self, r: Region) -> bool { + pub(crate) fn is_borrowed(&self, r: Region) -> bool { self.bc.borrow().is_borrowed(r) } - pub fn is_empty(&self) -> bool { - self.bc.borrow().is_empty() - } } #[derive(Debug)] @@ -44,8 +57,8 @@ impl InnerBorrowChecker { } } - fn is_empty(&self) -> bool { - self.borrows.is_empty() + fn has_outstanding_borrows(&self) -> bool { + !self.borrows.is_empty() } fn is_borrowed(&self, r: Region) -> bool { @@ -147,11 +160,20 @@ mod test { let r1 = Region::new(0, 10); let r2 = Region::new(10, 10); assert!(!r1.overlaps(r2)); - let _h1 = bs.borrow(r1).expect("can borrow r1"); + 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) diff --git a/crates/wiggle/tests/arrays.rs b/crates/wiggle/tests/arrays.rs index 61b47ba416..4fba074667 100644 --- a/crates/wiggle/tests/arrays.rs +++ b/crates/wiggle/tests/arrays.rs @@ -77,7 +77,7 @@ impl ReduceExcusesExcercise { pub fn test(&self) { let ctx = WasiCtx::new(); let host_memory = HostMemory::new(); - let bc = BorrowChecker::new(); + let bc = unsafe { BorrowChecker::new() }; // Populate memory with pointers to generated Excuse values for (&excuse, ptr) in self.excuse_values.iter().zip(self.excuse_ptr_locs.iter()) { @@ -169,7 +169,7 @@ impl PopulateExcusesExcercise { pub fn test(&self) { let ctx = WasiCtx::new(); let host_memory = HostMemory::new(); - let bc = BorrowChecker::new(); + let bc = unsafe { BorrowChecker::new() }; // Populate array with valid pointers to Excuse type in memory let ptr = host_memory.ptr::<[GuestPtr<'_, types::Excuse>]>( diff --git a/crates/wiggle/tests/atoms.rs b/crates/wiggle/tests/atoms.rs index 674fdaa5d5..3497215f0c 100644 --- a/crates/wiggle/tests/atoms.rs +++ b/crates/wiggle/tests/atoms.rs @@ -31,7 +31,7 @@ impl IntFloatExercise { pub fn test(&self) { let ctx = WasiCtx::new(); let host_memory = HostMemory::new(); - let bc = BorrowChecker::new(); + let bc = unsafe { BorrowChecker::new() }; let e = atoms::int_float_args(&ctx, &host_memory, &bc, self.an_int as i32, self.an_float); @@ -61,7 +61,7 @@ impl DoubleIntExercise { pub fn test(&self) { let ctx = WasiCtx::new(); let host_memory = HostMemory::new(); - let bc = BorrowChecker::new(); + let bc = unsafe { BorrowChecker::new() }; let e = atoms::double_int_return_float( &ctx, diff --git a/crates/wiggle/tests/flags.rs b/crates/wiggle/tests/flags.rs index 33f47c8ccc..727a9262bf 100644 --- a/crates/wiggle/tests/flags.rs +++ b/crates/wiggle/tests/flags.rs @@ -65,7 +65,7 @@ impl ConfigureCarExercise { pub fn test(&self) { let ctx = WasiCtx::new(); let host_memory = HostMemory::new(); - let bc = BorrowChecker::new(); + let bc = unsafe { BorrowChecker::new() }; // Populate input ptr host_memory diff --git a/crates/wiggle/tests/handles.rs b/crates/wiggle/tests/handles.rs index 292e689eb4..8b066c7aa5 100644 --- a/crates/wiggle/tests/handles.rs +++ b/crates/wiggle/tests/handles.rs @@ -34,7 +34,7 @@ impl HandleExercise { pub fn test(&self) { let ctx = WasiCtx::new(); let host_memory = HostMemory::new(); - let bc = BorrowChecker::new(); + let bc = unsafe { BorrowChecker::new() }; let e = handle_examples::fd_create(&ctx, &host_memory, &bc, self.return_loc.ptr as i32); diff --git a/crates/wiggle/tests/ints.rs b/crates/wiggle/tests/ints.rs index 20d30ff86d..db47ea469d 100644 --- a/crates/wiggle/tests/ints.rs +++ b/crates/wiggle/tests/ints.rs @@ -46,7 +46,7 @@ impl CookieCutterExercise { pub fn test(&self) { let ctx = WasiCtx::new(); let host_memory = HostMemory::new(); - let bc = BorrowChecker::new(); + let bc = unsafe { BorrowChecker::new() }; let res = ints::cookie_cutter( &ctx, diff --git a/crates/wiggle/tests/pointers.rs b/crates/wiggle/tests/pointers.rs index 2a63507900..74ca3a1fa2 100644 --- a/crates/wiggle/tests/pointers.rs +++ b/crates/wiggle/tests/pointers.rs @@ -128,7 +128,7 @@ impl PointersAndEnumsExercise { pub fn test(&self) { let ctx = WasiCtx::new(); let host_memory = HostMemory::new(); - let bc = BorrowChecker::new(); + let bc = unsafe { BorrowChecker::new() }; host_memory .ptr(&bc, self.input2_loc.ptr) diff --git a/crates/wiggle/tests/strings.rs b/crates/wiggle/tests/strings.rs index 51c5b4fde2..aa34312a86 100644 --- a/crates/wiggle/tests/strings.rs +++ b/crates/wiggle/tests/strings.rs @@ -69,7 +69,7 @@ impl HelloStringExercise { pub fn test(&self) { let ctx = WasiCtx::new(); let host_memory = HostMemory::new(); - let bc = BorrowChecker::new(); + let bc = unsafe { BorrowChecker::new() }; // Populate string in guest's memory let ptr = @@ -178,7 +178,7 @@ impl MultiStringExercise { pub fn test(&self) { let ctx = WasiCtx::new(); let host_memory = HostMemory::new(); - let bc = BorrowChecker::new(); + let bc = unsafe { BorrowChecker::new() }; let write_string = |val: &str, loc: MemArea| { let ptr = host_memory.ptr::(&bc, (loc.ptr, val.len() as u32)); diff --git a/crates/wiggle/tests/structs.rs b/crates/wiggle/tests/structs.rs index accba503f7..4861239ac2 100644 --- a/crates/wiggle/tests/structs.rs +++ b/crates/wiggle/tests/structs.rs @@ -83,7 +83,7 @@ impl SumOfPairExercise { pub fn test(&self) { let ctx = WasiCtx::new(); let host_memory = HostMemory::new(); - let bc = BorrowChecker::new(); + let bc = unsafe { BorrowChecker::new() }; host_memory .ptr(&bc, self.input_loc.ptr) @@ -173,7 +173,7 @@ impl SumPairPtrsExercise { pub fn test(&self) { let ctx = WasiCtx::new(); let host_memory = HostMemory::new(); - let bc = BorrowChecker::new(); + let bc = unsafe { BorrowChecker::new() }; host_memory .ptr(&bc, self.input_first_loc.ptr) @@ -259,7 +259,7 @@ impl SumIntAndPtrExercise { pub fn test(&self) { let ctx = WasiCtx::new(); let host_memory = HostMemory::new(); - let bc = BorrowChecker::new(); + let bc = unsafe { BorrowChecker::new() }; host_memory .ptr(&bc, self.input_first_loc.ptr) @@ -318,7 +318,7 @@ impl ReturnPairInts { pub fn test(&self) { let ctx = WasiCtx::new(); let host_memory = HostMemory::new(); - let bc = BorrowChecker::new(); + let bc = unsafe { BorrowChecker::new() }; let err = structs::return_pair_ints(&ctx, &host_memory, &bc, self.return_loc.ptr as i32); @@ -384,7 +384,7 @@ impl ReturnPairPtrsExercise { pub fn test(&self) { let ctx = WasiCtx::new(); let host_memory = HostMemory::new(); - let bc = BorrowChecker::new(); + let bc = unsafe { BorrowChecker::new() }; host_memory .ptr(&bc, self.input_first_loc.ptr) diff --git a/crates/wiggle/tests/union.rs b/crates/wiggle/tests/union.rs index 5a6e2b3a42..55894ba49a 100644 --- a/crates/wiggle/tests/union.rs +++ b/crates/wiggle/tests/union.rs @@ -107,7 +107,7 @@ impl GetTagExercise { pub fn test(&self) { let ctx = WasiCtx::new(); let host_memory = HostMemory::new(); - let bc = BorrowChecker::new(); + let bc = unsafe { BorrowChecker::new() }; let discriminant: u8 = reason_tag(&self.input).into(); host_memory @@ -186,7 +186,7 @@ impl ReasonMultExercise { pub fn test(&self) { let ctx = WasiCtx::new(); let host_memory = HostMemory::new(); - let bc = BorrowChecker::new(); + let bc = unsafe { BorrowChecker::new() }; let discriminant: u8 = reason_tag(&self.input).into(); host_memory