Skip to content

Commit

Permalink
add deallocation
Browse files Browse the repository at this point in the history
  • Loading branch information
damip committed Aug 18, 2023
1 parent b14d71f commit 4bb9ceb
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 45 deletions.
32 changes: 16 additions & 16 deletions src/wasmv1_execution/abi/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,19 +103,19 @@ pub struct ABIHandler<'a, 'b> {

impl<'a, 'b> ABIHandler<'a, 'b> {
/// Read argument
pub fn read_arg<M>(&self, arg_offset: i32) -> Result<M, WasmV1Error>
pub fn read_arg<M>(&mut self, arg_offset: i32) -> Result<M, WasmV1Error>
where
M: prost::Message + Default,
{
let byte_vec = self
.exec_env
.read_buffer(&self.store_env, arg_offset)
.take_buffer(&mut self.store_env, arg_offset)
.map_err(|err| {
WasmV1Error::RuntimeError(format!(
"Could not read ABI argument: {}",
err
))
})?;
WasmV1Error::RuntimeError(format!(
"Could not read ABI argument: {}",
err
))
})?;
M::decode(&mut Cursor::new(&byte_vec)).map_err(|err| {
WasmV1Error::RuntimeError(format!(
"Could not deserialize ABI argument: {}",
Expand All @@ -127,18 +127,18 @@ impl<'a, 'b> ABIHandler<'a, 'b> {
/// Read argument raw
/// For use with abort and other function that cannot use protobuf
pub fn read_arg_raw(
&self,
&mut self,
arg_offset: i32,
) -> Result<Vec<u8>, WasmV1Error> {
let byte_vec = self
.exec_env
.read_buffer(&self.store_env, arg_offset)
.take_buffer(&mut self.store_env, arg_offset)
.map_err(|err| {
WasmV1Error::RuntimeError(format!(
"Could not read ABI argument: {}",
err
))
})?;
WasmV1Error::RuntimeError(format!(
"Could not read ABI argument: {}",
err
))
})?;

Ok(byte_vec)
}
Expand All @@ -156,7 +156,7 @@ impl<'a, 'b> ABIHandler<'a, 'b> {
))
})?;
self.exec_env
.write_buffer(&mut self.store_env, &buf)
.create_buffer(&mut self.store_env, &buf)
.map_err(|err| {
WasmV1Error::RuntimeError(format!(
"Could not write ABI return value: {}",
Expand All @@ -171,7 +171,7 @@ impl<'a, 'b> ABIHandler<'a, 'b> {
value: &[u8],
) -> Result<i32, WasmV1Error> {
self.exec_env
.write_buffer(&mut self.store_env, value)
.create_buffer(&mut self.store_env, value)
.map_err(|err| {
WasmV1Error::RuntimeError(format!(
"Could not write ABI return value: {}",
Expand Down
16 changes: 9 additions & 7 deletions src/wasmv1_execution/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,22 +185,24 @@ impl ExecutionEnv {
}
}

/// Read buffer from memory.
pub fn read_buffer(
/// Read buffer from guest memory,
/// try to deallocate it.
pub fn take_buffer(
&self,
store: &impl AsStoreRef,
store: &mut impl AsStoreMut,
offset: i32,
) -> Result<Vec<u8>, WasmV1Error> {
self.ffi.read_buffer(store, offset)
self.ffi.take_buffer(store, offset)
}

/// Write buffer to memory.
pub fn write_buffer(
/// Allocate a buffer into guest memory,
/// write data into it.
pub fn create_buffer(
&self,
store: &mut impl AsStoreMut,
data: &[u8],
) -> Result<i32, WasmV1Error> {
self.ffi.write_buffer(store, data)
self.ffi.create_buffer(store, data)
}

/// Get gas costs.
Expand Down
46 changes: 37 additions & 9 deletions src/wasmv1_execution/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use super::WasmV1Error;
#[derive(Clone)]
pub struct Ffi {
guest_alloc_func: TypedFunction<i32, i32>,
guest_dealloc_func: Option<TypedFunction<i32, ()>>,
guest_memory: Memory,
}

Expand All @@ -22,6 +23,22 @@ impl Ffi {
err
))
})?;
let guest_dealloc_func = match instance
.exports
.get_typed_function::<i32, ()>(store, "__dealloc")
{
// deallocator was successfully loaded
Ok(func) => Some(func),
// deallocator is absent
Err(wasmer::ExportError::Missing(_)) => None,
// deallocator is present but has the wrong signature
Err(err) => {
return Err(WasmV1Error::RuntimeError(format!(
"Invalid __dealloc function signature exported by guest instance: {}",
err
)));
}
};
let guest_memory = instance
.exports
.get_memory("memory")
Expand All @@ -34,6 +51,7 @@ impl Ffi {
.clone();
Ok(Self {
guest_alloc_func,
guest_dealloc_func,
guest_memory,
})
}
Expand All @@ -43,19 +61,20 @@ impl Ffi {
self.guest_memory.view(store).data_size()
}

/// Reads a buffer and tries to deallocate it guest-side.
/// Assumes memory layout is: [len: i32 little-endian][data: u8*]
pub fn read_buffer(
pub fn take_buffer(
&self,
store: &impl AsStoreRef,
store: &mut impl AsStoreMut,
offset: i32,
) -> Result<Vec<u8>, WasmV1Error> {
let Ok(offset): Result<u64, _> = offset.try_into() else {
let Ok(offset_u64): Result<u64, _> = offset.try_into() else {
return Err(WasmV1Error::RuntimeError(format!("Invalid memory read offset: {}", offset)));
};
let view = self.guest_memory.view(store);

let mut len_buffer = [0u8; 4];
view.read(offset, &mut len_buffer).map_err(|err| {
view.read(offset_u64, &mut len_buffer).map_err(|err| {
WasmV1Error::RuntimeError(format!(
"Could not read length prefix from guest memory: {}",
err
Expand All @@ -79,21 +98,30 @@ impl Ffi {
"Buffer too large to be addressed on this system using usize"
)
];
let Some(offset) = offset.checked_add(len_buffer.len() as u64) else {
let Some(data_offset) = offset_u64.checked_add(len_buffer.len() as u64) else {
return Err(WasmV1Error::RuntimeError("Offset overflow".into()));
};
view.read(offset, &mut buffer).map_err(|err| {
view.read(data_offset, &mut buffer).map_err(|err| {
WasmV1Error::RuntimeError(format!(
"Could not read guest memory: {}",
err
))
})?;

// Deallocate the buffer if there is a dealloc guest function
if let Some(guest_dealloc_func) = &self.guest_dealloc_func {
guest_dealloc_func.call(store, offset).map_err(|err| {
WasmV1Error::RuntimeError(format!(
"__dealloc function call failed: {}",
err
))
})?;
}
Ok(buffer)
}

/// Does not assume anything on memory layout (managed by the guest on
/// allocation)
pub fn write_buffer(
/// Allocates and creates a buffer.
pub fn create_buffer(
&self,
store: &mut impl AsStoreMut,
buffer: &[u8],
Expand Down
25 changes: 12 additions & 13 deletions src/wasmv1_execution/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,10 +263,10 @@ pub(crate) fn exec_wasmv1_module(
}
})?;

// Write function argument to guest memory
// Allocate and write function argument to guest memory
let param_offset =
execution_env
.write_buffer(&mut store, param)
.create_buffer(&mut store, param)
.map_err(|err| VMError::ExecutionError {
error: format!(
"Could not write argument for guest call {}: {}",
Expand Down Expand Up @@ -299,17 +299,16 @@ pub(crate) fn exec_wasmv1_module(
.take()
.expect("Execution environment unavailable after execution");

// Read returned value
let ret =
execution_env
.read_buffer(&store, returned_offset)
.map_err(|err| VMError::ExecutionError {
error: format!(
"Could not read return value from guest call {}: {}",
function, err
),
init_gas_cost,
})?;
// Read returned value from guest memory and deallocate it
let ret = execution_env
.take_buffer(&mut store, returned_offset)
.map_err(|err| VMError::ExecutionError {
error: format!(
"Could not read return value from guest call {}: {}",
function, err
),
init_gas_cost,
})?;

// Get remaining gas
let remaining_gas = execution_env.get_remaining_gas(&mut store);
Expand Down

0 comments on commit 4bb9ceb

Please sign in to comment.