Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added a cleanup step that will mark all the memory as inaccessible and thus force termination of threads #3688

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
5 changes: 5 additions & 0 deletions lib/api/src/externals/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,11 @@ 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_inaccessible(store)
}

/// Copies the memory to a new store and returns a memory reference to it
pub fn copy_to_store(
&self,
Expand Down
5 changes: 5 additions & 0 deletions lib/api/src/js/externals/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,11 @@ impl Memory {
pub fn duplicate(&mut self, _store: &impl AsStoreRef) -> Result<VMMemory, MemoryError> {
self.handle.duplicate()
}

#[allow(unused)]
pub fn make_inaccessible(&self, store: &impl AsStoreRef) -> Result<(), MemoryError> {
Err(MemoryError::NotImplemented)
}
}

impl std::cmp::PartialEq for Memory {
Expand Down
7 changes: 7 additions & 0 deletions lib/api/src/sys/externals/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ 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_inaccessible(&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,
Expand Down
1 change: 1 addition & 0 deletions lib/api/src/sys/tunables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ mod tests {
mem,
}))
}

/*
// this code allow custom memory to be ignoring init_memory
use wasmer_vm::Trap;
Expand Down
22 changes: 22 additions & 0 deletions lib/sys-utils/src/memory/fd_memory/fd_mmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) }
Expand Down
33 changes: 33 additions & 0 deletions lib/sys-utils/src/memory/fd_memory/memories.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,13 @@ 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.
Expand Down Expand Up @@ -307,6 +314,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 {
Expand Down Expand Up @@ -349,6 +361,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.
Expand Down Expand Up @@ -395,6 +412,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 {
Expand Down Expand Up @@ -443,6 +466,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<VMOwnedMemory> for VMMemory {
Expand Down Expand Up @@ -510,6 +538,11 @@ impl LinearMemory for VMMemory {
fn duplicate(&mut self) -> Result<Box<dyn LinearMemory + 'static>, 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 {
Expand Down
3 changes: 3 additions & 0 deletions lib/types/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ pub enum MemoryError {
/// A user defined error value, used for error cases not listed above.
#[error("A user-defined error occurred: {0}")]
Generic(String),
/// Not implemented
#[error("This operation is not yet implemented")]
NotImplemented,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This enum does not have non_exhaustive, so adding a new variant is a breaking change. We can't do this quite yet.

I suggest:

  • Use the Generic variant
  • Add a TODO and create a follow up issue for the 4.0 milestone to track adding a new variant
  • Just ignore the error in the cleanup() code

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand why you would mention non_exhaustive enums but this change in the upcoming beta does not sound like a big issue to me considering all the other breaking changes that are going into that release - either way I would rather go back to the way I had it returning Ok(()) if we are not going to do it properly - this small part of the change already cost too much time.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is this is in the core Wasmer types, not in WASI.

There currently is no breaking change planned for Wasmer immediately, only later down the line.

So the next release will be 3.2, a non-breaking change.

}

/// An ImportError.
Expand Down
43 changes: 43 additions & 0 deletions lib/vm/src/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,13 @@ 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.
Expand Down Expand Up @@ -299,6 +306,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 {
Expand Down Expand Up @@ -341,6 +353,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.
Expand Down Expand Up @@ -390,6 +407,12 @@ impl VMSharedMemory {
conditions: ThreadConditions::new(),
})
}

/// 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 {
Expand Down Expand Up @@ -452,6 +475,11 @@ impl LinearMemory for VMSharedMemory {
fn do_notify(&mut self, dst: NotifyLocation, count: u32) -> u32 {
self.conditions.do_notify(dst, count)
}

/// Makes all the memory inaccessible to reads and writes
fn make_inaccessible(&self) -> Result<(), MemoryError> {
Self::make_inaccessible(self)
}
}

impl From<VMOwnedMemory> for VMMemory {
Expand Down Expand Up @@ -533,6 +561,11 @@ impl LinearMemory for VMMemory {
fn do_notify(&mut self, dst: NotifyLocation, count: u32) -> u32 {
self.0.do_notify(dst, count)
}

/// Makes all the memory inaccessible to reads and writes
fn make_inaccessible(&self) -> Result<(), MemoryError> {
self.0.make_inaccessible()
}
}

impl VMMemory {
Expand Down Expand Up @@ -596,6 +629,11 @@ impl VMMemory {
pub fn duplicate(&mut self) -> Result<Box<dyn LinearMemory + 'static>, 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)]
Expand Down Expand Up @@ -666,4 +704,9 @@ where
fn do_notify(&mut self, _dst: NotifyLocation, _count: u32) -> u32 {
0
}

/// Makes all the memory inaccessible to reads and writes
fn make_inaccessible(&self) -> Result<(), MemoryError> {
Err(MemoryError::NotImplemented)
}
}
22 changes: 22 additions & 0 deletions lib/vm/src/mmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably keep the additional API surface we expose minimal, so we can change/remove it again without a breaking change.

Wouldn't exposing make_inaccessible(&self) that equals make_all_inaccessible() be enough?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah that's true - i can make this change and hide some of the API internals

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) }
Expand Down
14 changes: 7 additions & 7 deletions lib/wasi/src/bin_factory/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,15 @@ 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);
module
}
(None, None) => {
error!("package has no entry [{}]", name,);
env.blocking_cleanup(Some(Errno::Noexec.into()));
env.blocking_cleanup(&store, Some(Errno::Noexec.into()));
return Err(VirtualBusError::CompileError);
}
};
Expand Down Expand Up @@ -113,7 +113,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;
}
};
Expand All @@ -127,7 +127,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;
}

Expand All @@ -137,7 +137,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;
}
}
Expand Down Expand Up @@ -168,7 +168,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()));
Expand Down Expand Up @@ -200,7 +200,7 @@ impl BinFactory {
.await
.ok_or(VirtualBusError::NotFound);
if binary.is_err() {
env.cleanup(Some(Errno::Noent.into())).await;
env.cleanup(&store, Some(Errno::Noent.into())).await;
}
let binary = binary?;

Expand Down
Loading