mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 09:57:56 +00:00
Improve sandbox internal api (#9709)
* Improve sandbox internal api This improves the internal sandbox api for the executor implementations. The main point is to hide the tls in the internal api and not having it exposed to the outside. This is especially needed for wasmtime 0.29.0 * Fmt * Make it nicer
This commit is contained in:
@@ -37,6 +37,8 @@ use wasmi::{
|
||||
use crate::util::wasmer::MemoryWrapper as WasmerMemoryWrapper;
|
||||
use crate::util::wasmi::MemoryWrapper as WasmiMemoryWrapper;
|
||||
|
||||
environmental::environmental!(SandboxContextStore: trait SandboxContext);
|
||||
|
||||
/// Index of a function inside the supervisor.
|
||||
///
|
||||
/// This is a typically an index in the default table of the supervisor, however
|
||||
@@ -164,17 +166,11 @@ impl ImportResolver for Imports {
|
||||
}
|
||||
}
|
||||
|
||||
/// This trait encapsulates sandboxing capabilities.
|
||||
///
|
||||
/// Note that this functions are only called in the `supervisor` context.
|
||||
pub trait SandboxCapabilities: FunctionContext {
|
||||
/// Represents a function reference into the supervisor environment.
|
||||
/// Provides an abstraction over execution environment.
|
||||
type SupervisorFuncRef;
|
||||
|
||||
/// The sandbox context used to execute sandboxed functions.
|
||||
pub trait SandboxContext {
|
||||
/// Invoke a function in the supervisor environment.
|
||||
///
|
||||
/// This first invokes the dispatch_thunk function, passing in the function index of the
|
||||
/// 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
|
||||
@@ -187,24 +183,23 @@ pub trait SandboxCapabilities: FunctionContext {
|
||||
/// 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>;
|
||||
|
||||
/// Returns the supervisor context.
|
||||
fn supervisor_context(&mut self) -> &mut dyn FunctionContext;
|
||||
}
|
||||
|
||||
/// 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 + 'a> {
|
||||
/// Supervisor function environment
|
||||
supervisor_externals: &'a mut FE,
|
||||
|
||||
pub struct GuestExternals<'a> {
|
||||
/// Instance of sandboxed module to be dispatched
|
||||
sandbox_instance: &'a SandboxInstance<FE::SupervisorFuncRef>,
|
||||
sandbox_instance: &'a SandboxInstance,
|
||||
|
||||
/// External state passed to guest environment, see the `instantiate` function
|
||||
state: u32,
|
||||
@@ -232,114 +227,108 @@ fn deserialize_result(
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, FE: SandboxCapabilities + 'a> Externals for GuestExternals<'a, FE> {
|
||||
impl<'a> Externals for GuestExternals<'a> {
|
||||
fn invoke_index(
|
||||
&mut self,
|
||||
index: usize,
|
||||
args: RuntimeArgs,
|
||||
) -> std::result::Result<Option<RuntimeValue>, Trap> {
|
||||
// Make `index` typesafe again.
|
||||
let index = GuestFuncIndex(index);
|
||||
SandboxContextStore::with(|sandbox_context| {
|
||||
// Make `index` typesafe again.
|
||||
let index = GuestFuncIndex(index);
|
||||
|
||||
// Convert function index from guest to supervisor space
|
||||
let func_idx = self.sandbox_instance
|
||||
.guest_to_supervisor_mapping
|
||||
.func_by_guest_index(index)
|
||||
.expect(
|
||||
"`invoke_index` is called with indexes registered via `FuncInstance::alloc_host`;
|
||||
`FuncInstance::alloc_host` is called with indexes that were obtained from `guest_to_supervisor_mapping`;
|
||||
`func_by_guest_index` called with `index` can't return `None`;
|
||||
qed"
|
||||
// Convert function index from guest to supervisor space
|
||||
let func_idx = self.sandbox_instance
|
||||
.guest_to_supervisor_mapping
|
||||
.func_by_guest_index(index)
|
||||
.expect(
|
||||
"`invoke_index` is called with indexes registered via `FuncInstance::alloc_host`;
|
||||
`FuncInstance::alloc_host` is called with indexes that were obtained from `guest_to_supervisor_mapping`;
|
||||
`func_by_guest_index` called with `index` can't return `None`;
|
||||
qed"
|
||||
);
|
||||
|
||||
// Serialize arguments into a byte vector.
|
||||
let invoke_args_data: Vec<u8> = args
|
||||
.as_ref()
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(sp_wasm_interface::Value::from)
|
||||
.collect::<Vec<_>>()
|
||||
.encode();
|
||||
|
||||
let state = self.state;
|
||||
|
||||
// Move serialized arguments inside the memory, invoke dispatch thunk and
|
||||
// then free allocated memory.
|
||||
let invoke_args_len = invoke_args_data.len() as WordSize;
|
||||
let invoke_args_ptr = sandbox_context
|
||||
.supervisor_context()
|
||||
.allocate_memory(invoke_args_len)
|
||||
.map_err(|_| trap("Can't allocate memory in supervisor for the arguments"))?;
|
||||
|
||||
let deallocate = |supervisor_context: &mut dyn FunctionContext, ptr, fail_msg| {
|
||||
supervisor_context.deallocate_memory(ptr).map_err(|_| trap(fail_msg))
|
||||
};
|
||||
|
||||
if sandbox_context
|
||||
.supervisor_context()
|
||||
.write_memory(invoke_args_ptr, &invoke_args_data)
|
||||
.is_err()
|
||||
{
|
||||
deallocate(
|
||||
sandbox_context.supervisor_context(),
|
||||
invoke_args_ptr,
|
||||
"Failed dealloction after failed write of invoke arguments",
|
||||
)?;
|
||||
return Err(trap("Can't write invoke args into memory"))
|
||||
}
|
||||
|
||||
let result = sandbox_context.invoke(
|
||||
invoke_args_ptr,
|
||||
invoke_args_len,
|
||||
state,
|
||||
func_idx,
|
||||
);
|
||||
|
||||
// Serialize arguments into a byte vector.
|
||||
let invoke_args_data: Vec<u8> = args
|
||||
.as_ref()
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(sp_wasm_interface::Value::from)
|
||||
.collect::<Vec<_>>()
|
||||
.encode();
|
||||
|
||||
let state = self.state;
|
||||
|
||||
// Move serialized arguments inside the memory, invoke dispatch thunk and
|
||||
// then free allocated memory.
|
||||
let invoke_args_len = invoke_args_data.len() as WordSize;
|
||||
let invoke_args_ptr = self
|
||||
.supervisor_externals
|
||||
.allocate_memory(invoke_args_len)
|
||||
.map_err(|_| trap("Can't allocate memory in supervisor for the arguments"))?;
|
||||
|
||||
let deallocate = |this: &mut GuestExternals<FE>, ptr, fail_msg| {
|
||||
this.supervisor_externals.deallocate_memory(ptr).map_err(|_| trap(fail_msg))
|
||||
};
|
||||
|
||||
if self
|
||||
.supervisor_externals
|
||||
.write_memory(invoke_args_ptr, &invoke_args_data)
|
||||
.is_err()
|
||||
{
|
||||
deallocate(
|
||||
self,
|
||||
sandbox_context.supervisor_context(),
|
||||
invoke_args_ptr,
|
||||
"Failed dealloction after failed write of invoke arguments",
|
||||
"Can't deallocate memory for dispatch thunk's invoke arguments",
|
||||
)?;
|
||||
return Err(trap("Can't write invoke args into memory"))
|
||||
}
|
||||
let result = result?;
|
||||
|
||||
let result = self.supervisor_externals.invoke(
|
||||
&self.sandbox_instance.dispatch_thunk,
|
||||
invoke_args_ptr,
|
||||
invoke_args_len,
|
||||
state,
|
||||
func_idx,
|
||||
);
|
||||
// dispatch_thunk returns pointer to serialized arguments.
|
||||
// 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)
|
||||
};
|
||||
|
||||
deallocate(
|
||||
self,
|
||||
invoke_args_ptr,
|
||||
"Can't deallocate memory for dispatch thunk's invoke arguments",
|
||||
)?;
|
||||
let result = result?;
|
||||
let serialized_result_val = sandbox_context
|
||||
.supervisor_context()
|
||||
.read_memory(serialized_result_val_ptr, serialized_result_val_len)
|
||||
.map_err(|_| trap("Can't read the serialized result from dispatch thunk"));
|
||||
|
||||
// dispatch_thunk returns pointer to serialized arguments.
|
||||
// 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
|
||||
.read_memory(serialized_result_val_ptr, serialized_result_val_len)
|
||||
.map_err(|_| trap("Can't read the serialized result from dispatch thunk"));
|
||||
|
||||
deallocate(
|
||||
self,
|
||||
serialized_result_val_ptr,
|
||||
"Can't deallocate memory for dispatch thunk's result",
|
||||
)
|
||||
.and_then(|_| serialized_result_val)
|
||||
.and_then(|serialized_result_val| deserialize_result(&serialized_result_val))
|
||||
deallocate(
|
||||
sandbox_context.supervisor_context(),
|
||||
serialized_result_val_ptr,
|
||||
"Can't deallocate memory for dispatch thunk's result",
|
||||
)
|
||||
.and_then(|_| serialized_result_val)
|
||||
.and_then(|serialized_result_val| deserialize_result(&serialized_result_val))
|
||||
}).expect("SandboxContextStore is set when invoking sandboxed functions; qed")
|
||||
}
|
||||
}
|
||||
|
||||
fn with_guest_externals<FE, R, F>(
|
||||
supervisor_externals: &mut FE,
|
||||
sandbox_instance: &SandboxInstance<FE::SupervisorFuncRef>,
|
||||
state: u32,
|
||||
f: F,
|
||||
) -> R
|
||||
fn with_guest_externals<R, F>(sandbox_instance: &SandboxInstance, state: u32, f: F) -> R
|
||||
where
|
||||
FE: SandboxCapabilities,
|
||||
F: FnOnce(&mut GuestExternals<FE>) -> R,
|
||||
F: FnOnce(&mut GuestExternals) -> R,
|
||||
{
|
||||
let mut guest_externals = GuestExternals { supervisor_externals, sandbox_instance, state };
|
||||
f(&mut guest_externals)
|
||||
f(&mut GuestExternals { sandbox_instance, state })
|
||||
}
|
||||
|
||||
/// Module instance in terms of selected backend
|
||||
@@ -366,13 +355,12 @@ enum BackendInstance {
|
||||
/// This is generic over a supervisor function reference type.
|
||||
///
|
||||
/// [`invoke`]: #method.invoke
|
||||
pub struct SandboxInstance<FR> {
|
||||
pub struct SandboxInstance {
|
||||
backend_instance: BackendInstance,
|
||||
dispatch_thunk: FR,
|
||||
guest_to_supervisor_mapping: GuestToSupervisorFunctionMapping,
|
||||
}
|
||||
|
||||
impl<FR> SandboxInstance<FR> {
|
||||
impl SandboxInstance {
|
||||
/// Invoke an exported function by a name.
|
||||
///
|
||||
/// `supervisor_externals` is required to execute the implementations
|
||||
@@ -380,7 +368,7 @@ impl<FR> SandboxInstance<FR> {
|
||||
///
|
||||
/// The `state` parameter can be used to provide custom data for
|
||||
/// these syscall implementations.
|
||||
pub fn invoke<'a, FE, SCH, DTH>(
|
||||
pub fn invoke(
|
||||
&self,
|
||||
|
||||
// function to call that is exported from the module
|
||||
@@ -391,68 +379,66 @@ impl<FR> SandboxInstance<FR> {
|
||||
|
||||
// arbitraty context data of the call
|
||||
state: u32,
|
||||
) -> std::result::Result<Option<wasmi::RuntimeValue>, wasmi::Error>
|
||||
where
|
||||
FE: SandboxCapabilities<SupervisorFuncRef = FR> + 'a,
|
||||
SCH: SandboxCapabilitiesHolder<SupervisorFuncRef = FR, SC = FE>,
|
||||
DTH: DispatchThunkHolder<DispatchThunk = FR>,
|
||||
{
|
||||
SCH::with_sandbox_capabilities(|supervisor_externals| {
|
||||
with_guest_externals(supervisor_externals, self, state, |guest_externals| {
|
||||
match &self.backend_instance {
|
||||
BackendInstance::Wasmi(wasmi_instance) => {
|
||||
let wasmi_result =
|
||||
wasmi_instance.invoke_export(export_name, args, guest_externals)?;
|
||||
|
||||
Ok(wasmi_result)
|
||||
},
|
||||
sandbox_context: &mut dyn SandboxContext,
|
||||
) -> std::result::Result<Option<wasmi::RuntimeValue>, wasmi::Error> {
|
||||
match &self.backend_instance {
|
||||
BackendInstance::Wasmi(wasmi_instance) =>
|
||||
with_guest_externals(self, state, |guest_externals| {
|
||||
let wasmi_result = SandboxContextStore::using(sandbox_context, || {
|
||||
wasmi_instance.invoke_export(export_name, args, guest_externals)
|
||||
})?;
|
||||
|
||||
#[cfg(feature = "wasmer-sandbox")]
|
||||
BackendInstance::Wasmer(wasmer_instance) => {
|
||||
let function = wasmer_instance
|
||||
.exports
|
||||
.get_function(export_name)
|
||||
.map_err(|error| wasmi::Error::Function(error.to_string()))?;
|
||||
Ok(wasmi_result)
|
||||
}),
|
||||
|
||||
let args: Vec<wasmer::Val> = args
|
||||
.iter()
|
||||
.map(|v| match *v {
|
||||
RuntimeValue::I32(val) => wasmer::Val::I32(val),
|
||||
RuntimeValue::I64(val) => wasmer::Val::I64(val),
|
||||
RuntimeValue::F32(val) => wasmer::Val::F32(val.into()),
|
||||
RuntimeValue::F64(val) => wasmer::Val::F64(val.into()),
|
||||
})
|
||||
.collect();
|
||||
#[cfg(feature = "wasmer-sandbox")]
|
||||
BackendInstance::Wasmer(wasmer_instance) => {
|
||||
let function = wasmer_instance
|
||||
.exports
|
||||
.get_function(export_name)
|
||||
.map_err(|error| wasmi::Error::Function(error.to_string()))?;
|
||||
|
||||
let wasmer_result =
|
||||
DTH::initialize_thunk(&self.dispatch_thunk, || function.call(&args))
|
||||
.map_err(|error| wasmi::Error::Function(error.to_string()))?;
|
||||
let args: Vec<wasmer::Val> = args
|
||||
.iter()
|
||||
.map(|v| match *v {
|
||||
RuntimeValue::I32(val) => wasmer::Val::I32(val),
|
||||
RuntimeValue::I64(val) => wasmer::Val::I64(val),
|
||||
RuntimeValue::F32(val) => wasmer::Val::F32(val.into()),
|
||||
RuntimeValue::F64(val) => wasmer::Val::F64(val.into()),
|
||||
})
|
||||
.collect();
|
||||
|
||||
if wasmer_result.len() > 1 {
|
||||
return Err(wasmi::Error::Function(
|
||||
"multiple return types are not supported yet".to_owned(),
|
||||
))
|
||||
}
|
||||
let wasmer_result = SandboxContextStore::using(sandbox_context, || {
|
||||
function.call(&args).map_err(|error| wasmi::Error::Function(error.to_string()))
|
||||
})?;
|
||||
|
||||
let wasmer_result = if let Some(wasmer_value) = wasmer_result.first() {
|
||||
let wasmer_value = match *wasmer_value {
|
||||
wasmer::Val::I32(val) => RuntimeValue::I32(val),
|
||||
wasmer::Val::I64(val) => RuntimeValue::I64(val),
|
||||
wasmer::Val::F32(val) => RuntimeValue::F32(val.into()),
|
||||
wasmer::Val::F64(val) => RuntimeValue::F64(val.into()),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
if wasmer_result.len() > 1 {
|
||||
return Err(wasmi::Error::Function(
|
||||
"multiple return types are not supported yet".into(),
|
||||
))
|
||||
}
|
||||
|
||||
Some(wasmer_value)
|
||||
} else {
|
||||
None
|
||||
wasmer_result
|
||||
.first()
|
||||
.map(|wasm_value| {
|
||||
let wasmer_value = match *wasm_value {
|
||||
wasmer::Val::I32(val) => RuntimeValue::I32(val),
|
||||
wasmer::Val::I64(val) => RuntimeValue::I64(val),
|
||||
wasmer::Val::F32(val) => RuntimeValue::F32(val.into()),
|
||||
wasmer::Val::F64(val) => RuntimeValue::F64(val.into()),
|
||||
_ =>
|
||||
return Err(wasmi::Error::Function(format!(
|
||||
"Unsupported return value: {:?}",
|
||||
wasm_value,
|
||||
))),
|
||||
};
|
||||
|
||||
Ok(wasmer_result)
|
||||
},
|
||||
}
|
||||
})
|
||||
})
|
||||
Ok(wasmer_value)
|
||||
})
|
||||
.transpose()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the value from a global with the given `name`.
|
||||
@@ -548,8 +534,8 @@ impl GuestEnvironment {
|
||||
/// Decodes an environment definition from the given raw bytes.
|
||||
///
|
||||
/// Returns `Err` if the definition cannot be decoded.
|
||||
pub fn decode<FR>(
|
||||
store: &Store<FR>,
|
||||
pub fn decode<DT>(
|
||||
store: &Store<DT>,
|
||||
raw_env_def: &[u8],
|
||||
) -> std::result::Result<Self, InstantiationError> {
|
||||
let (imports, guest_to_supervisor_mapping) =
|
||||
@@ -562,47 +548,18 @@ impl GuestEnvironment {
|
||||
///
|
||||
/// To finish off the instantiation the user must call `register`.
|
||||
#[must_use]
|
||||
pub struct UnregisteredInstance<FR> {
|
||||
sandbox_instance: Rc<SandboxInstance<FR>>,
|
||||
pub struct UnregisteredInstance {
|
||||
sandbox_instance: Rc<SandboxInstance>,
|
||||
}
|
||||
|
||||
impl<FR> UnregisteredInstance<FR> {
|
||||
impl UnregisteredInstance {
|
||||
/// Finalizes instantiation of this module.
|
||||
pub fn register(self, store: &mut Store<FR>) -> u32 {
|
||||
pub fn register<DT>(self, store: &mut Store<DT>, dispatch_thunk: DT) -> u32 {
|
||||
// At last, register the instance.
|
||||
let instance_idx = store.register_sandbox_instance(self.sandbox_instance);
|
||||
instance_idx
|
||||
store.register_sandbox_instance(self.sandbox_instance, dispatch_thunk)
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper type to provide sandbox capabilities to the inner context
|
||||
pub trait SandboxCapabilitiesHolder {
|
||||
/// Supervisor function reference
|
||||
type SupervisorFuncRef;
|
||||
|
||||
/// Capabilities trait
|
||||
type SC: SandboxCapabilities<SupervisorFuncRef = Self::SupervisorFuncRef>;
|
||||
|
||||
/// Wrapper that provides sandbox capabilities in a limited context
|
||||
fn with_sandbox_capabilities<R, F: FnOnce(&mut Self::SC) -> R>(f: F) -> R;
|
||||
}
|
||||
|
||||
/// Helper type to provide dispatch thunk to the inner context
|
||||
pub trait DispatchThunkHolder {
|
||||
/// Dispatch thunk for this particular context
|
||||
type DispatchThunk;
|
||||
|
||||
/// Provide `DispatchThunk` for the runtime method call and execute the given function `f`.
|
||||
///
|
||||
/// During the execution of the provided function `dispatch_thunk` will be callable.
|
||||
fn initialize_thunk<R, F>(s: &Self::DispatchThunk, f: F) -> R
|
||||
where
|
||||
F: FnOnce() -> R;
|
||||
|
||||
/// Wrapper that provides dispatch thunk in a limited context
|
||||
fn with_dispatch_thunk<R, F: FnOnce(&mut Self::DispatchThunk) -> R>(f: F) -> R;
|
||||
}
|
||||
|
||||
/// Sandbox backend to use
|
||||
pub enum SandboxBackend {
|
||||
/// Wasm interpreter
|
||||
@@ -716,14 +673,17 @@ impl BackendContext {
|
||||
/// This struct keeps track of all sandboxed components.
|
||||
///
|
||||
/// This is generic over a supervisor function reference type.
|
||||
pub struct Store<FR> {
|
||||
// Memories and instances are `Some` until torn down.
|
||||
instances: Vec<Option<Rc<SandboxInstance<FR>>>>,
|
||||
pub struct Store<DT> {
|
||||
/// Stores the instance and the dispatch thunk associated to per instance.
|
||||
///
|
||||
/// Instances are `Some` until torn down.
|
||||
instances: Vec<Option<(Rc<SandboxInstance>, DT)>>,
|
||||
/// Memories are `Some` until torn down.
|
||||
memories: Vec<Option<Memory>>,
|
||||
backend_context: BackendContext,
|
||||
}
|
||||
|
||||
impl<FR> Store<FR> {
|
||||
impl<DT: Clone> Store<DT> {
|
||||
/// Create a new empty sandbox store.
|
||||
pub fn new(backend: SandboxBackend) -> Self {
|
||||
Store {
|
||||
@@ -776,11 +736,28 @@ impl<FR> Store<FR> {
|
||||
///
|
||||
/// 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<FR>>> {
|
||||
pub fn instance(&self, instance_idx: u32) -> Result<Rc<SandboxInstance>> {
|
||||
self.instances
|
||||
.get(instance_idx as usize)
|
||||
.cloned()
|
||||
.ok_or_else(|| "Trying to access a non-existent instance")?
|
||||
.as_ref()
|
||||
.map(|v| v.0.clone())
|
||||
.ok_or_else(|| "Trying to access a torndown instance".into())
|
||||
}
|
||||
|
||||
/// Returns dispatch thunk by `instance_idx`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `Err` If `instance_idx` isn't a valid index of an instance or
|
||||
/// instance is already torndown.
|
||||
pub fn dispatch_thunk(&self, instance_idx: u32) -> Result<DT> {
|
||||
self.instances
|
||||
.get(instance_idx as usize)
|
||||
.as_ref()
|
||||
.ok_or_else(|| "Trying to access a non-existent instance")?
|
||||
.as_ref()
|
||||
.map(|v| v.1.clone())
|
||||
.ok_or_else(|| "Trying to access a torndown instance".into())
|
||||
}
|
||||
|
||||
@@ -842,27 +819,20 @@ impl<FR> Store<FR> {
|
||||
/// Note: Due to borrowing constraints dispatch thunk is now propagated using DTH
|
||||
///
|
||||
/// Returns uninitialized sandboxed module instance or an instantiation error.
|
||||
pub fn instantiate<'a, FE, SCH, DTH>(
|
||||
pub fn instantiate(
|
||||
&mut self,
|
||||
wasm: &[u8],
|
||||
guest_env: GuestEnvironment,
|
||||
state: u32,
|
||||
) -> std::result::Result<UnregisteredInstance<FR>, InstantiationError>
|
||||
where
|
||||
FR: Clone + 'static,
|
||||
FE: SandboxCapabilities<SupervisorFuncRef = FR> + 'a,
|
||||
SCH: SandboxCapabilitiesHolder<SupervisorFuncRef = FR, SC = FE>,
|
||||
DTH: DispatchThunkHolder<DispatchThunk = FR>,
|
||||
{
|
||||
let backend_context = &self.backend_context;
|
||||
|
||||
let sandbox_instance = match backend_context {
|
||||
sandbox_context: &mut dyn SandboxContext,
|
||||
) -> std::result::Result<UnregisteredInstance, InstantiationError> {
|
||||
let sandbox_instance = match self.backend_context {
|
||||
BackendContext::Wasmi =>
|
||||
Self::instantiate_wasmi::<FE, SCH, DTH>(wasm, guest_env, state)?,
|
||||
Self::instantiate_wasmi(wasm, guest_env, state, sandbox_context)?,
|
||||
|
||||
#[cfg(feature = "wasmer-sandbox")]
|
||||
BackendContext::Wasmer(context) =>
|
||||
Self::instantiate_wasmer::<FE, SCH, DTH>(context, wasm, guest_env, state)?,
|
||||
BackendContext::Wasmer(ref context) =>
|
||||
Self::instantiate_wasmer(&context, wasm, guest_env, state, sandbox_context)?,
|
||||
};
|
||||
|
||||
Ok(UnregisteredInstance { sandbox_instance })
|
||||
@@ -870,74 +840,58 @@ impl<FR> Store<FR> {
|
||||
}
|
||||
|
||||
// Private routines
|
||||
impl<FR> Store<FR> {
|
||||
fn register_sandbox_instance(&mut self, sandbox_instance: Rc<SandboxInstance<FR>>) -> u32 {
|
||||
impl<DT> Store<DT> {
|
||||
fn register_sandbox_instance(
|
||||
&mut self,
|
||||
sandbox_instance: Rc<SandboxInstance>,
|
||||
dispatch_thunk: DT,
|
||||
) -> u32 {
|
||||
let instance_idx = self.instances.len();
|
||||
self.instances.push(Some(sandbox_instance));
|
||||
self.instances.push(Some((sandbox_instance, dispatch_thunk)));
|
||||
instance_idx as u32
|
||||
}
|
||||
|
||||
fn instantiate_wasmi<'a, FE, SCH, DTH>(
|
||||
fn instantiate_wasmi(
|
||||
wasm: &[u8],
|
||||
guest_env: GuestEnvironment,
|
||||
state: u32,
|
||||
) -> std::result::Result<Rc<SandboxInstance<FR>>, InstantiationError>
|
||||
where
|
||||
FR: Clone + 'static,
|
||||
FE: SandboxCapabilities<SupervisorFuncRef = FR> + 'a,
|
||||
SCH: SandboxCapabilitiesHolder<SupervisorFuncRef = FR, SC = FE>,
|
||||
DTH: DispatchThunkHolder<DispatchThunk = FR>,
|
||||
{
|
||||
sandbox_context: &mut dyn SandboxContext,
|
||||
) -> std::result::Result<Rc<SandboxInstance>, InstantiationError> {
|
||||
let wasmi_module =
|
||||
Module::from_buffer(wasm).map_err(|_| InstantiationError::ModuleDecoding)?;
|
||||
let wasmi_instance = ModuleInstance::new(&wasmi_module, &guest_env.imports)
|
||||
.map_err(|_| InstantiationError::Instantiation)?;
|
||||
|
||||
let sandbox_instance = DTH::with_dispatch_thunk(|dispatch_thunk| {
|
||||
Rc::new(SandboxInstance {
|
||||
// In general, it's not a very good idea to use `.not_started_instance()` for
|
||||
// anything but for extracting memory and tables. But in this particular case, we
|
||||
// are extracting for the purpose of running `start` function which should be ok.
|
||||
backend_instance: BackendInstance::Wasmi(
|
||||
wasmi_instance.not_started_instance().clone(),
|
||||
),
|
||||
dispatch_thunk: dispatch_thunk.clone(),
|
||||
guest_to_supervisor_mapping: guest_env.guest_to_supervisor_mapping,
|
||||
})
|
||||
let sandbox_instance = Rc::new(SandboxInstance {
|
||||
// In general, it's not a very good idea to use `.not_started_instance()` for
|
||||
// anything but for extracting memory and tables. But in this particular case, we
|
||||
// are extracting for the purpose of running `start` function which should be ok.
|
||||
backend_instance: BackendInstance::Wasmi(wasmi_instance.not_started_instance().clone()),
|
||||
guest_to_supervisor_mapping: guest_env.guest_to_supervisor_mapping,
|
||||
});
|
||||
|
||||
SCH::with_sandbox_capabilities(|supervisor_externals| {
|
||||
with_guest_externals(
|
||||
supervisor_externals,
|
||||
&sandbox_instance,
|
||||
state,
|
||||
|guest_externals| {
|
||||
wasmi_instance
|
||||
.run_start(guest_externals)
|
||||
.map_err(|_| InstantiationError::StartTrapped)
|
||||
with_guest_externals(&sandbox_instance, state, |guest_externals| {
|
||||
SandboxContextStore::using(sandbox_context, || {
|
||||
wasmi_instance
|
||||
.run_start(guest_externals)
|
||||
.map_err(|_| InstantiationError::StartTrapped)
|
||||
})
|
||||
|
||||
// Note: no need to run start on wasmtime instance, since it's done
|
||||
// automatically
|
||||
},
|
||||
)
|
||||
// Note: no need to run start on wasmtime instance, since it's done
|
||||
// automatically
|
||||
})?;
|
||||
|
||||
Ok(sandbox_instance)
|
||||
}
|
||||
|
||||
#[cfg(feature = "wasmer-sandbox")]
|
||||
fn instantiate_wasmer<'a, FE, SCH, DTH>(
|
||||
fn instantiate_wasmer(
|
||||
context: &WasmerBackend,
|
||||
wasm: &[u8],
|
||||
guest_env: GuestEnvironment,
|
||||
state: u32,
|
||||
) -> std::result::Result<Rc<SandboxInstance<FR>>, InstantiationError>
|
||||
where
|
||||
FR: Clone + 'static,
|
||||
FE: SandboxCapabilities<SupervisorFuncRef = FR> + 'a,
|
||||
SCH: SandboxCapabilitiesHolder<SupervisorFuncRef = FR, SC = FE>,
|
||||
DTH: DispatchThunkHolder<DispatchThunk = FR>,
|
||||
{
|
||||
sandbox_context: &mut dyn SandboxContext,
|
||||
) -> std::result::Result<Rc<SandboxInstance>, InstantiationError> {
|
||||
let module = wasmer::Module::new(&context.store, wasm)
|
||||
.map_err(|_| InstantiationError::ModuleDecoding)?;
|
||||
|
||||
@@ -991,7 +945,7 @@ impl<FR> Store<FR> {
|
||||
.func_by_guest_index(guest_func_index)
|
||||
.ok_or(InstantiationError::ModuleDecoding)?;
|
||||
|
||||
let function = Self::wasmer_dispatch_function::<FE, SCH, DTH>(
|
||||
let function = Self::wasmer_dispatch_function(
|
||||
supervisor_func_index,
|
||||
&context.store,
|
||||
func_ty,
|
||||
@@ -1012,68 +966,71 @@ impl<FR> Store<FR> {
|
||||
import_object.register(module_name, exports);
|
||||
}
|
||||
|
||||
let instance =
|
||||
let instance = SandboxContextStore::using(sandbox_context, || {
|
||||
wasmer::Instance::new(&module, &import_object).map_err(|error| match error {
|
||||
wasmer::InstantiationError::Link(_) => InstantiationError::Instantiation,
|
||||
wasmer::InstantiationError::Start(_) => InstantiationError::StartTrapped,
|
||||
wasmer::InstantiationError::HostEnvInitialization(_) =>
|
||||
InstantiationError::EnvironmentDefinitionCorrupted,
|
||||
})?;
|
||||
})
|
||||
})?;
|
||||
|
||||
Ok(Rc::new(SandboxInstance {
|
||||
backend_instance: BackendInstance::Wasmer(instance),
|
||||
dispatch_thunk: DTH::with_dispatch_thunk(|dispatch_thunk| dispatch_thunk.clone()),
|
||||
guest_to_supervisor_mapping: guest_env.guest_to_supervisor_mapping,
|
||||
}))
|
||||
}
|
||||
|
||||
#[cfg(feature = "wasmer-sandbox")]
|
||||
fn wasmer_dispatch_function<'a, FE, SCH, DTH>(
|
||||
fn wasmer_dispatch_function(
|
||||
supervisor_func_index: SupervisorFuncIndex,
|
||||
store: &wasmer::Store,
|
||||
func_ty: &wasmer::FunctionType,
|
||||
state: u32,
|
||||
) -> wasmer::Function
|
||||
where
|
||||
FR: Clone + 'static,
|
||||
FE: SandboxCapabilities<SupervisorFuncRef = FR> + 'a,
|
||||
SCH: SandboxCapabilitiesHolder<SupervisorFuncRef = FR, SC = FE>,
|
||||
DTH: DispatchThunkHolder<DispatchThunk = FR>,
|
||||
{
|
||||
) -> wasmer::Function {
|
||||
wasmer::Function::new(store, func_ty, move |params| {
|
||||
SCH::with_sandbox_capabilities(|supervisor_externals| {
|
||||
SandboxContextStore::with(|sandbox_context| {
|
||||
use sp_wasm_interface::Value;
|
||||
|
||||
// Serialize arguments into a byte vector.
|
||||
let invoke_args_data = params
|
||||
.iter()
|
||||
.map(|val| match val {
|
||||
wasmer::Val::I32(val) => Value::I32(*val),
|
||||
wasmer::Val::I64(val) => Value::I64(*val),
|
||||
wasmer::Val::F32(val) => Value::F32(f32::to_bits(*val)),
|
||||
wasmer::Val::F64(val) => Value::F64(f64::to_bits(*val)),
|
||||
_ => unimplemented!(),
|
||||
wasmer::Val::I32(val) => Ok(Value::I32(*val)),
|
||||
wasmer::Val::I64(val) => Ok(Value::I64(*val)),
|
||||
wasmer::Val::F32(val) => Ok(Value::F32(f32::to_bits(*val))),
|
||||
wasmer::Val::F64(val) => Ok(Value::F64(f64::to_bits(*val))),
|
||||
_ => Err(wasmer::RuntimeError::new(format!(
|
||||
"Unsupported function argument: {:?}",
|
||||
val
|
||||
))),
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.collect::<std::result::Result<Vec<_>, _>>()?
|
||||
.encode();
|
||||
|
||||
// Move serialized arguments inside the memory, invoke dispatch thunk and
|
||||
// then free allocated memory.
|
||||
let invoke_args_len = invoke_args_data.len() as WordSize;
|
||||
let invoke_args_ptr =
|
||||
supervisor_externals.allocate_memory(invoke_args_len).map_err(|_| {
|
||||
let invoke_args_ptr = sandbox_context
|
||||
.supervisor_context()
|
||||
.allocate_memory(invoke_args_len)
|
||||
.map_err(|_| {
|
||||
wasmer::RuntimeError::new(
|
||||
"Can't allocate memory in supervisor for the arguments",
|
||||
)
|
||||
})?;
|
||||
|
||||
let deallocate = |fe: &mut FE, ptr, fail_msg| {
|
||||
let deallocate = |fe: &mut dyn FunctionContext, ptr, fail_msg| {
|
||||
fe.deallocate_memory(ptr).map_err(|_| wasmer::RuntimeError::new(fail_msg))
|
||||
};
|
||||
|
||||
if supervisor_externals.write_memory(invoke_args_ptr, &invoke_args_data).is_err() {
|
||||
if sandbox_context
|
||||
.supervisor_context()
|
||||
.write_memory(invoke_args_ptr, &invoke_args_data)
|
||||
.is_err()
|
||||
{
|
||||
deallocate(
|
||||
supervisor_externals,
|
||||
sandbox_context.supervisor_context(),
|
||||
invoke_args_ptr,
|
||||
"Failed dealloction after failed write of invoke arguments",
|
||||
)?;
|
||||
@@ -1082,16 +1039,9 @@ impl<FR> Store<FR> {
|
||||
}
|
||||
|
||||
// Perform the actuall call
|
||||
let serialized_result = DTH::with_dispatch_thunk(|dispatch_thunk| {
|
||||
supervisor_externals.invoke(
|
||||
&dispatch_thunk,
|
||||
invoke_args_ptr,
|
||||
invoke_args_len,
|
||||
state,
|
||||
supervisor_func_index,
|
||||
)
|
||||
})
|
||||
.map_err(|e| wasmer::RuntimeError::new(e.to_string()))?;
|
||||
let serialized_result = sandbox_context
|
||||
.invoke(invoke_args_ptr, invoke_args_len, state, supervisor_func_index)
|
||||
.map_err(|e| wasmer::RuntimeError::new(e.to_string()))?;
|
||||
|
||||
// dispatch_thunk returns pointer to serialized arguments.
|
||||
// Unpack pointer and len of the serialized result data.
|
||||
@@ -1103,7 +1053,8 @@ impl<FR> Store<FR> {
|
||||
(Pointer::new(ptr), len)
|
||||
};
|
||||
|
||||
let serialized_result_val = supervisor_externals
|
||||
let serialized_result_val = sandbox_context
|
||||
.supervisor_context()
|
||||
.read_memory(serialized_result_val_ptr, serialized_result_val_len)
|
||||
.map_err(|_| {
|
||||
wasmer::RuntimeError::new(
|
||||
@@ -1112,7 +1063,7 @@ impl<FR> Store<FR> {
|
||||
});
|
||||
|
||||
let deserialized_result = deallocate(
|
||||
supervisor_externals,
|
||||
sandbox_context.supervisor_context(),
|
||||
serialized_result_val_ptr,
|
||||
"Can't deallocate memory for dispatch thunk's result",
|
||||
)
|
||||
@@ -1133,6 +1084,7 @@ impl<FR> Store<FR> {
|
||||
Ok(vec![])
|
||||
}
|
||||
})
|
||||
.expect("SandboxContextStore is set when invoking sandboxed functions; qed")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user