Decouple node-executor structures from wasmi (#3610)

* executor: Support non-Wasmi sandbox supervisor environments.

* node-executor: Tests don't reference WasmExecutor directly.

* executor: Simplify encoding of heap metadata.

* executor: Decouple allocator from wasmi::MemoryRef.
This commit is contained in:
Jim Posen
2019-09-23 17:38:51 +02:00
committed by Sergei Pepyakin
parent c200ce757b
commit 82e7f9e423
4 changed files with 324 additions and 224 deletions
+74 -51
View File
@@ -23,7 +23,7 @@ use std::{collections::HashMap, rc::Rc};
use codec::{Decode, Encode};
use primitives::sandbox as sandbox_primitives;
use wasmi::{
Externals, FuncRef, ImportResolver, MemoryInstance, MemoryRef, Module, ModuleInstance,
Externals, ImportResolver, MemoryInstance, MemoryRef, Module, ModuleInstance,
ModuleRef, RuntimeArgs, RuntimeValue, Trap, TrapKind, memory_units::Pages,
};
use wasm_interface::{Pointer, WordSize};
@@ -33,7 +33,13 @@ use wasm_interface::{Pointer, WordSize};
/// This is a typically an index in the default table of the supervisor, however
/// the exact meaning of this index is depends on the implementation of dispatch function.
#[derive(Copy, Clone, Debug, PartialEq)]
struct SupervisorFuncIndex(usize);
pub struct SupervisorFuncIndex(usize);
impl From<SupervisorFuncIndex> for usize {
fn from(index: SupervisorFuncIndex) -> Self {
index.0
}
}
/// Index of a function within guest index space.
///
@@ -73,7 +79,7 @@ impl ImportResolver for Imports {
module_name: &str,
field_name: &str,
signature: &::wasmi::Signature,
) -> std::result::Result<FuncRef, wasmi::Error> {
) -> std::result::Result<wasmi::FuncRef, wasmi::Error> {
let key = (
module_name.as_bytes().to_owned(),
field_name.as_bytes().to_owned(),
@@ -138,11 +144,14 @@ impl ImportResolver for Imports {
///
/// Note that this functions are only called in the `supervisor` context.
pub trait SandboxCapabilities {
/// Represents a function reference into the supervisor environment.
type SupervisorFuncRef;
/// Returns a reference to an associated sandbox `Store`.
fn store(&self) -> &Store;
fn store(&self) -> &Store<Self::SupervisorFuncRef>;
/// Returns a mutable reference to an associated sandbox `Store`.
fn store_mut(&mut self) -> &mut Store;
fn store_mut(&mut self) -> &mut Store<Self::SupervisorFuncRef>;
/// Allocate space of the specified length in the supervisor memory.
///
@@ -175,15 +184,37 @@ pub trait SandboxCapabilities {
///
/// Returns `Err` if `ptr + len` is out of bounds.
fn read_memory(&self, ptr: Pointer<u8>, len: WordSize) -> Result<Vec<u8>>;
/// Invoke a function in the supervisor environment.
///
/// This first invokes the dispatch_thunk function, passing in the function index of the
/// desired function to call and serialized arguments. The thunk calls the desired function
/// with the deserialized arguments, then serializes the result into memory and returns
/// reference. The pointer to and length of the result in linear memory is encoded into an i64,
/// with the upper 32 bits representing the pointer and the lower 32 bits representing the
/// length.
///
/// # Errors
///
/// Returns `Err` if the dispatch_thunk function has an incorrect signature or traps during
/// execution.
fn invoke(
&mut self,
dispatch_thunk: &Self::SupervisorFuncRef,
invoke_args_ptr: Pointer<u8>,
invoke_args_len: WordSize,
state: u32,
func_idx: SupervisorFuncIndex,
) -> Result<i64>;
}
/// Implementation of [`Externals`] that allows execution of guest module with
/// [externals][`Externals`] that might refer functions defined by supervisor.
///
/// [`Externals`]: ../../wasmi/trait.Externals.html
pub struct GuestExternals<'a, FE: SandboxCapabilities + Externals + 'a> {
pub struct GuestExternals<'a, FE: SandboxCapabilities + 'a> {
supervisor_externals: &'a mut FE,
sandbox_instance: &'a SandboxInstance,
sandbox_instance: &'a SandboxInstance<FE::SupervisorFuncRef>,
state: u32,
}
@@ -205,7 +236,7 @@ fn deserialize_result(serialized_result: &[u8]) -> std::result::Result<Option<Ru
}
}
impl<'a, FE: SandboxCapabilities + Externals + 'a> Externals for GuestExternals<'a, FE> {
impl<'a, FE: SandboxCapabilities + 'a> Externals for GuestExternals<'a, FE> {
fn invoke_index(
&mut self,
index: usize,
@@ -214,7 +245,6 @@ impl<'a, FE: SandboxCapabilities + Externals + 'a> Externals for GuestExternals<
// Make `index` typesafe again.
let index = GuestFuncIndex(index);
let dispatch_thunk = self.sandbox_instance.dispatch_thunk.clone();
let func_idx = self.sandbox_instance
.guest_to_supervisor_mapping
.func_by_guest_index(index)
@@ -237,34 +267,26 @@ impl<'a, FE: SandboxCapabilities + Externals + 'a> Externals for GuestExternals<
// Move serialized arguments inside the memory and invoke dispatch thunk and
// then free allocated memory.
let invoke_args_ptr = self.supervisor_externals
.allocate(invoke_args_data.len() as u32)?;
self.supervisor_externals
.write_memory(invoke_args_ptr, &invoke_args_data)?;
let result = ::wasmi::FuncInstance::invoke(
&dispatch_thunk,
&[
RuntimeValue::I32(u32::from(invoke_args_ptr) as i32),
RuntimeValue::I32(invoke_args_data.len() as i32),
RuntimeValue::I32(state as i32),
RuntimeValue::I32(func_idx.0 as i32),
],
self.supervisor_externals,
);
let invoke_args_len = invoke_args_data.len() as WordSize;
let invoke_args_ptr = self.supervisor_externals.allocate(invoke_args_len)?;
self.supervisor_externals.write_memory(invoke_args_ptr, &invoke_args_data)?;
let result = self.supervisor_externals.invoke(
&self.sandbox_instance.dispatch_thunk,
invoke_args_ptr,
invoke_args_len,
state,
func_idx,
)?;
self.supervisor_externals.deallocate(invoke_args_ptr)?;
// dispatch_thunk returns pointer to serialized arguments.
let (serialized_result_val_ptr, serialized_result_val_len) = match result {
// Unpack pointer and len of the serialized result data.
Ok(Some(RuntimeValue::I64(v))) => {
// Cast to u64 to use zero-extension.
let v = v as u64;
let ptr = (v as u64 >> 32) as u32;
let len = (v & 0xFFFFFFFF) as u32;
(Pointer::new(ptr), len)
}
Ok(_) => return Err(trap("Supervisor function returned unexpected result!")),
Err(_) => return Err(trap("Supervisor function trapped!")),
// Unpack pointer and len of the serialized result data.
let (serialized_result_val_ptr, serialized_result_val_len) = {
// Cast to u64 to use zero-extension.
let v = result as u64;
let ptr = (v as u64 >> 32) as u32;
let len = (v & 0xFFFFFFFF) as u32;
(Pointer::new(ptr), len)
};
let serialized_result_val = self.supervisor_externals
@@ -272,21 +294,18 @@ impl<'a, FE: SandboxCapabilities + Externals + 'a> Externals for GuestExternals<
self.supervisor_externals
.deallocate(serialized_result_val_ptr)?;
// We do not have to check the signature here, because it's automatically
// checked by wasmi.
deserialize_result(&serialized_result_val)
}
}
fn with_guest_externals<FE, R, F>(
supervisor_externals: &mut FE,
sandbox_instance: &SandboxInstance,
sandbox_instance: &SandboxInstance<FE::SupervisorFuncRef>,
state: u32,
f: F,
) -> R
where
FE: SandboxCapabilities + Externals,
FE: SandboxCapabilities,
F: FnOnce(&mut GuestExternals<FE>) -> R,
{
let mut guest_externals = GuestExternals {
@@ -308,14 +327,16 @@ where
/// it's required to provide supervisor externals: it will be used to execute
/// code in the supervisor context.
///
/// This is generic over a supervisor function reference type.
///
/// [`invoke`]: #method.invoke
pub struct SandboxInstance {
pub struct SandboxInstance<FR> {
instance: ModuleRef,
dispatch_thunk: FuncRef,
dispatch_thunk: FR,
guest_to_supervisor_mapping: GuestToSupervisorFunctionMapping,
}
impl SandboxInstance {
impl<FR> SandboxInstance<FR> {
/// Invoke an exported function by a name.
///
/// `supervisor_externals` is required to execute the implementations
@@ -323,7 +344,7 @@ impl SandboxInstance {
///
/// The `state` parameter can be used to provide custom data for
/// these syscall implementations.
pub fn invoke<FE: SandboxCapabilities + Externals>(
pub fn invoke<FE: SandboxCapabilities<SupervisorFuncRef=FR>>(
&self,
export_name: &str,
args: &[RuntimeValue],
@@ -412,9 +433,9 @@ fn decode_environment_definition(
/// - Module in `wasm` is invalid or couldn't be instantiated.
///
/// [`EnvironmentDefinition`]: ../../sandbox/struct.EnvironmentDefinition.html
pub fn instantiate<FE: SandboxCapabilities + Externals>(
pub fn instantiate<FE: SandboxCapabilities>(
supervisor_externals: &mut FE,
dispatch_thunk: FuncRef,
dispatch_thunk: FE::SupervisorFuncRef,
wasm: &[u8],
raw_env_def: &[u8],
state: u32,
@@ -453,15 +474,17 @@ pub fn instantiate<FE: SandboxCapabilities + Externals>(
}
/// This struct keeps track of all sandboxed components.
pub struct Store {
///
/// This is generic over a supervisor function reference type.
pub struct Store<FR> {
// Memories and instances are `Some` untill torndown.
instances: Vec<Option<Rc<SandboxInstance>>>,
instances: Vec<Option<Rc<SandboxInstance<FR>>>>,
memories: Vec<Option<MemoryRef>>,
}
impl Store {
impl<FR> Store<FR> {
/// Create a new empty sandbox store.
pub fn new() -> Store {
pub fn new() -> Self {
Store {
instances: Vec::new(),
memories: Vec::new(),
@@ -497,7 +520,7 @@ impl Store {
///
/// Returns `Err` If `instance_idx` isn't a valid index of an instance or
/// instance is already torndown.
pub fn instance(&self, instance_idx: u32) -> Result<Rc<SandboxInstance>> {
pub fn instance(&self, instance_idx: u32) -> Result<Rc<SandboxInstance<FR>>> {
self.instances
.get(instance_idx as usize)
.cloned()
@@ -553,7 +576,7 @@ impl Store {
}
}
fn register_sandbox_instance(&mut self, sandbox_instance: Rc<SandboxInstance>) -> u32 {
fn register_sandbox_instance(&mut self, sandbox_instance: Rc<SandboxInstance<FR>>) -> u32 {
let instance_idx = self.instances.len();
self.instances.push(Some(sandbox_instance));
instance_idx as u32