diff --git a/lib/api/src/externals/memory.rs b/lib/api/src/externals/memory.rs index 0d29b0ecbc0..95ac34ad861 100644 --- a/lib/api/src/externals/memory.rs +++ b/lib/api/src/externals/memory.rs @@ -119,6 +119,14 @@ impl Memory { self.0.grow(store, delta) } + /// Makes all the memory inaccessible to any reads or writes + pub fn make_inaccessible( + &self, + store: &impl AsStoreRef, + ) -> Result<(), MemoryError> { + self.0.make_inaccessable(store) + } + /// Copies the memory to a new store and returns a memory reference to it pub fn copy_to_store( &self, diff --git a/lib/api/src/sys/externals/memory.rs b/lib/api/src/sys/externals/memory.rs index 0874825cef3..c65a585fcf5 100644 --- a/lib/api/src/sys/externals/memory.rs +++ b/lib/api/src/sys/externals/memory.rs @@ -57,6 +57,15 @@ impl Memory { self.handle.get_mut(store.objects_mut()).grow(delta.into()) } + /// Makes all the memory inaccessible to any reads or writes + pub fn make_inaccessable( + &self, + store: &impl AsStoreRef + ) -> Result<(), MemoryError> + { + self.handle.get(store.as_store_ref().objects()).make_inaccessible() + } + pub fn copy_to_store( &self, store: &impl AsStoreRef, diff --git a/lib/sys-utils/src/memory/fd_memory/fd_mmap.rs b/lib/sys-utils/src/memory/fd_memory/fd_mmap.rs index 4bda17c9862..071137e20eb 100644 --- a/lib/sys-utils/src/memory/fd_memory/fd_mmap.rs +++ b/lib/sys-utils/src/memory/fd_memory/fd_mmap.rs @@ -180,6 +180,28 @@ impl FdMmap { .map_err(|e| e.to_string()) } + /// Make the entire memory inaccessible to both reads and writes. + pub fn make_all_inaccessible(&self) -> Result<(), String> { + self.make_inaccessible(0, self.len) + } + + /// Make the memory starting at `start` and extending for `len` bytes inaccessible + /// to both reads and writes. + /// `start` and `len` must be native page-size multiples and describe a range within + /// `self`'s reserved memory. + pub fn make_inaccessible(&self, start: usize, len: usize) -> Result<(), String> { + let page_size = region::page::size(); + assert_eq!(start & (page_size - 1), 0); + assert_eq!(len & (page_size - 1), 0); + assert!(len <= self.len); + assert!(start <= self.len - len); + + // Commit the accessible size. + let ptr = self.ptr as *const u8; + unsafe { region::protect(ptr.add(start), len, region::Protection::NONE) } + .map_err(|e| e.to_string()) + } + /// Return the allocated memory as a slice of u8. pub fn as_slice(&self) -> &[u8] { unsafe { slice::from_raw_parts(self.ptr as *const u8, self.len) } diff --git a/lib/sys-utils/src/memory/fd_memory/memories.rs b/lib/sys-utils/src/memory/fd_memory/memories.rs index c058f1f681c..0adc1478ac2 100644 --- a/lib/sys-utils/src/memory/fd_memory/memories.rs +++ b/lib/sys-utils/src/memory/fd_memory/memories.rs @@ -149,6 +149,14 @@ impl WasmMmap { size: self.size, }) } + + /// Makes all the memory inaccessible to reads and writes + pub fn make_inaccessible(&self) -> Result<(), MemoryError> { + self + .alloc + .make_all_inaccessible() + .map_err(MemoryError::Region) + } } /// A linear memory instance. @@ -307,6 +315,11 @@ impl VMOwnedMemory { config: self.config.clone(), }) } + + /// Makes all the memory inaccessible to reads and writes + fn make_inaccessible(&self) -> Result<(), MemoryError> { + self.mmap.make_inaccessible() + } } impl LinearMemory for VMOwnedMemory { @@ -349,6 +362,11 @@ impl LinearMemory for VMOwnedMemory { let forked = Self::duplicate(self)?; Ok(Box::new(forked)) } + + /// Makes all the memory inaccessible to reads and writes + fn make_inaccessible(&self) -> Result<(), MemoryError> { + Self::make_inaccessible(self) + } } /// A shared linear memory instance. @@ -395,6 +413,12 @@ impl VMSharedMemory { config: self.config.clone(), }) } + + /// Makes all the memory inaccessible to reads and writes + pub fn make_inaccessible(&self) -> Result<(), MemoryError> { + let guard = self.mmap.write().unwrap(); + guard.make_inaccessible() + } } impl LinearMemory for VMSharedMemory { @@ -443,6 +467,11 @@ impl LinearMemory for VMSharedMemory { let forked = Self::duplicate(self)?; Ok(Box::new(forked)) } + + /// Makes all the memory inaccessible to reads and writes + fn make_inaccessible(&self) -> Result<(), MemoryError> { + Self::make_inaccessible(self) + } } impl From for VMMemory { @@ -510,6 +539,11 @@ impl LinearMemory for VMMemory { fn duplicate(&mut self) -> Result, MemoryError> { self.0.duplicate() } + + /// Makes all the memory inaccessible to reads and writes + fn make_inaccessible(&self) -> Result<(), MemoryError> { + self.0.make_inaccessible() + } } impl VMMemory { diff --git a/lib/vm/src/memory.rs b/lib/vm/src/memory.rs index d3d288a0c04..25356eb28fe 100644 --- a/lib/vm/src/memory.rs +++ b/lib/vm/src/memory.rs @@ -137,6 +137,14 @@ impl WasmMmap { size: self.size, }) } + + /// Makes all the memory inaccessible to reads and writes + pub fn make_inaccessible(&self) -> Result<(), MemoryError> { + self + .alloc + .make_all_inaccessible() + .map_err(MemoryError::Region) + } } /// A linear memory instance. @@ -295,6 +303,11 @@ impl VMOwnedMemory { config: self.config.clone(), }) } + + /// Makes all the memory inaccessible to reads and writes + pub fn make_inaccessible(&self) -> Result<(), MemoryError> { + self.mmap.make_inaccessible() + } } impl LinearMemory for VMOwnedMemory { @@ -337,6 +350,11 @@ impl LinearMemory for VMOwnedMemory { let forked = Self::duplicate(self)?; Ok(Box::new(forked)) } + + /// Makes all the memory inaccessible to reads and writes + fn make_inaccessible(&self) -> Result<(), MemoryError> { + Self::make_inaccessible(self) + } } /// A shared linear memory instance. @@ -383,6 +401,12 @@ impl VMSharedMemory { config: self.config.clone(), }) } + + /// Makes all the memory inaccessible to reads and writes + pub fn make_inaccessible(&self) -> Result<(), MemoryError> { + let guard = self.mmap.write().unwrap(); + guard.make_inaccessible() + } } impl LinearMemory for VMSharedMemory { @@ -431,6 +455,11 @@ impl LinearMemory for VMSharedMemory { let forked = Self::duplicate(self)?; Ok(Box::new(forked)) } + + /// Makes all the memory inaccessible to reads and writes + fn make_inaccessible(&self) -> Result<(), MemoryError> { + Self::make_inaccessible(self) + } } impl From for VMMemory { @@ -498,6 +527,11 @@ impl LinearMemory for VMMemory { fn duplicate(&mut self) -> Result, MemoryError> { self.0.duplicate() } + + /// Makes all the memory inaccessible to reads and writes + fn make_inaccessible(&self) -> Result<(), MemoryError> { + self.0.make_inaccessible() + } } impl VMMemory { @@ -561,6 +595,11 @@ impl VMMemory { pub fn duplicate(&mut self) -> Result, MemoryError> { LinearMemory::duplicate(self) } + + /// Makes all the memory inaccessible to reads and writes + pub fn make_inaccessible(&self) -> Result<(), MemoryError> { + LinearMemory::make_inaccessible(self) + } } #[doc(hidden)] @@ -616,4 +655,7 @@ where /// Copies this memory to a new memory fn duplicate(&mut self) -> Result, MemoryError>; + + /// Makes all the memory inaccessible to reads and writes + fn make_inaccessible(&self) -> Result<(), MemoryError>; } diff --git a/lib/vm/src/mmap.rs b/lib/vm/src/mmap.rs index abeb21a666b..f23899bea59 100644 --- a/lib/vm/src/mmap.rs +++ b/lib/vm/src/mmap.rs @@ -233,6 +233,28 @@ impl Mmap { Ok(()) } + /// Make the entire memory inaccessible to both reads and writes. + pub fn make_all_inaccessible(&self) -> Result<(), String> { + self.make_inaccessible(0, self.total_size) + } + + /// Make the memory starting at `start` and extending for `len` bytes inaccessible + /// to both reads and writes. + /// `start` and `len` must be native page-size multiples and describe a range within + /// `self`'s reserved memory. + pub fn make_inaccessible(&self, start: usize, len: usize) -> Result<(), String> { + let page_size = region::page::size(); + assert_eq!(start & (page_size - 1), 0); + assert_eq!(len & (page_size - 1), 0); + assert_le!(len, self.total_size); + assert_le!(start, self.total_size - len); + + // Commit the accessible size. + let ptr = self.ptr as *const u8; + unsafe { region::protect(ptr.add(start), len, region::Protection::NONE) } + .map_err(|e| e.to_string()) + } + /// Return the allocated memory as a slice of u8. pub fn as_slice(&self) -> &[u8] { unsafe { slice::from_raw_parts(self.ptr as *const u8, self.total_size) } diff --git a/lib/wasi/src/bin_factory/exec.rs b/lib/wasi/src/bin_factory/exec.rs index 9b3020f7266..2e72880d6f4 100644 --- a/lib/wasi/src/bin_factory/exec.rs +++ b/lib/wasi/src/bin_factory/exec.rs @@ -20,7 +20,7 @@ use crate::{ pub fn spawn_exec( binary: BinaryPackage, name: &str, - store: Store, + mut store: Store, env: WasiEnv, runtime: &Arc, compiled_modules: &ModuleCache, @@ -42,7 +42,7 @@ pub fn spawn_exec( VirtualBusError::CompileError }); if module.is_err() { - env.blocking_cleanup(Some(Errno::Noexec.into())); + env.blocking_cleanup(&store, Some(Errno::Noexec.into())); } let module = module?; compiled_modules.set_compiled_module(binary.hash().as_str(), compiler, &module); @@ -50,7 +50,7 @@ pub fn spawn_exec( } (None, None) => { error!("package has no entry [{}]", name,); - env.blocking_cleanup(Some(Errno::Noexec.into())); + env.blocking_cleanup(&mut store, Some(Errno::Noexec.into())); return Err(VirtualBusError::CompileError); } }; @@ -124,7 +124,7 @@ pub fn spawn_exec_module( error!("wasi[{}]::wasm instantiate error ({})", pid, err); wasi_env .data(&store) - .blocking_cleanup(Some(Errno::Noexec.into())); + .blocking_cleanup(&store, Some(Errno::Noexec.into())); return; } }; @@ -138,7 +138,7 @@ pub fn spawn_exec_module( error!("wasi[{}]::wasi initialize error ({})", pid, err); wasi_env .data(&store) - .blocking_cleanup(Some(Errno::Noexec.into())); + .blocking_cleanup(&store, Some(Errno::Noexec.into())); return; } @@ -148,7 +148,7 @@ pub fn spawn_exec_module( thread.thread.set_status_finished(Err(err.into())); wasi_env .data(&store) - .blocking_cleanup(Some(Errno::Noexec.into())); + .blocking_cleanup(&store, Some(Errno::Noexec.into())); return; } } @@ -179,7 +179,7 @@ pub fn spawn_exec_module( }; // Cleanup the environment - wasi_env.data(&store).blocking_cleanup(Some(code)); + wasi_env.data(&store).blocking_cleanup(&store, Some(code)); debug!("wasi[{pid}]::main() has exited with {code}"); thread.thread.set_status_finished(ret.map(|a| a.into())); @@ -201,7 +201,7 @@ impl BinFactory { pub fn spawn<'a>( &'a self, name: String, - store: Store, + mut store: Store, env: WasiEnv, ) -> Pin> + 'a>> { Box::pin(async move { @@ -211,7 +211,7 @@ impl BinFactory { .await .ok_or(VirtualBusError::NotFound); if binary.is_err() { - env.cleanup(Some(Errno::Noent.into())).await; + env.cleanup(&mut store, Some(Errno::Noent.into())).await; } let binary = binary?; diff --git a/lib/wasi/src/state/env.rs b/lib/wasi/src/state/env.rs index f0911bea6fb..c418c13b05f 100644 --- a/lib/wasi/src/state/env.rs +++ b/lib/wasi/src/state/env.rs @@ -475,7 +475,7 @@ impl WasiEnv { tracing::error!("wasi[{}]::wasm instantiate error ({})", pid, err); func_env .data(&store) - .blocking_cleanup(Some(Errno::Noexec.into())); + .blocking_cleanup(&store, Some(Errno::Noexec.into())); return Err(err.into()); } }; @@ -490,7 +490,7 @@ impl WasiEnv { tracing::error!("wasi[{}]::wasi initialize error ({})", pid, err); func_env .data(&store) - .blocking_cleanup(Some(Errno::Noexec.into())); + .blocking_cleanup(&store, Some(Errno::Noexec.into())); return Err(err.into()); } @@ -500,7 +500,7 @@ impl WasiEnv { if let Err(err) = crate::run_wasi_func_start(initialize, &mut store) { func_env .data(&store) - .blocking_cleanup(Some(Errno::Noexec.into())); + .blocking_cleanup(&store, Some(Errno::Noexec.into())); return Err(err); } } @@ -897,9 +897,9 @@ impl WasiEnv { /// Cleans up all the open files (if this is the main thread) #[allow(clippy::await_holding_lock)] - pub fn blocking_cleanup(&self, exit_code: Option) { + pub fn blocking_cleanup(&self, store: &impl AsStoreRef, exit_code: Option) { __asyncify_light(self, None, async { - self.cleanup(exit_code).await; + self.cleanup(store, exit_code).await; Ok(()) }) .ok(); @@ -907,7 +907,7 @@ impl WasiEnv { /// Cleans up all the open files (if this is the main thread) #[allow(clippy::await_holding_lock)] - pub async fn cleanup(&self, exit_code: Option) { + pub async fn cleanup(&self, store: &impl AsStoreRef, exit_code: Option) { const CLEANUP_TIMEOUT: Duration = Duration::from_secs(10); // If this is the main thread then also close all the files @@ -931,6 +931,15 @@ impl WasiEnv { // Terminate the process let exit_code = exit_code.unwrap_or_else(|| Errno::Canceled.into()); self.process.terminate(exit_code); + + // Now we also force all the memory into a protected state which will prevent any reads + // or writes and thus terminate processes that try to use it + if let Err(err) = self.memory().make_inaccessible(store) { + tracing::warn!( + "WasiEnv::cleanup failed to set memory to inaccessible - {}", + err + ); + } } } } diff --git a/lib/wasi/src/state/func_env.rs b/lib/wasi/src/state/func_env.rs index 9c784a8c53a..a6333af3c72 100644 --- a/lib/wasi/src/state/func_env.rs +++ b/lib/wasi/src/state/func_env.rs @@ -182,6 +182,6 @@ impl WasiFunctionEnv { } // Cleans up all the open files (if this is the main thread) - self.data(store).blocking_cleanup(exit_code); + self.data(store).blocking_cleanup(store, exit_code); } }