diff --git a/Cargo.lock b/Cargo.lock index 4794c1c0f5c41..4b6cc99426b1c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8429,6 +8429,7 @@ dependencies = [ name = "sc-runtime-test" version = "2.0.0" dependencies = [ + "paste 1.0.4", "sp-core", "sp-io", "sp-runtime", diff --git a/client/executor/runtime-test/Cargo.toml b/client/executor/runtime-test/Cargo.toml index 1e3b5e926b964..2c82a9705ceeb 100644 --- a/client/executor/runtime-test/Cargo.toml +++ b/client/executor/runtime-test/Cargo.toml @@ -19,6 +19,7 @@ sp-runtime = { version = "4.0.0-dev", default-features = false, path = "../../.. sp-sandbox = { version = "0.10.0-dev", default-features = false, path = "../../../primitives/sandbox" } sp-std = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/std" } sp-tasks = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/tasks" } +paste = "1.0.4" [build-dependencies] substrate-wasm-builder = { version = "5.0.0-dev", path = "../../../utils/wasm-builder" } diff --git a/client/executor/runtime-test/src/lib.rs b/client/executor/runtime-test/src/lib.rs index 2b5699fa3f77a..3ea6e2d7ed1aa 100644 --- a/client/executor/runtime-test/src/lib.rs +++ b/client/executor/runtime-test/src/lib.rs @@ -30,7 +30,7 @@ use sp_runtime::{ traits::{BlakeTwo256, Hash}, }; #[cfg(not(feature = "std"))] -use sp_sandbox::Value; +use sp_sandbox::{SandboxEnvironmentBuilder, SandboxInstance, SandboxMemory, Value}; extern "C" { #[allow(dead_code)] @@ -183,61 +183,6 @@ sp_core::wasm_export_functions! { ).as_ref().to_vec() } - fn test_sandbox(code: Vec) -> bool { - execute_sandboxed(&code, &[]).is_ok() - } - - fn test_sandbox_args(code: Vec) -> bool { - execute_sandboxed( - &code, - &[ - Value::I32(0x12345678), - Value::I64(0x1234567887654321), - ], - ).is_ok() - } - - fn test_sandbox_return_val(code: Vec) -> bool { - let ok = match execute_sandboxed( - &code, - &[ - Value::I32(0x1336), - ] - ) { - Ok(sp_sandbox::ReturnValue::Value(Value::I32(0x1337))) => true, - _ => false, - }; - - ok - } - - fn test_sandbox_instantiate(code: Vec) -> u8 { - let env_builder = sp_sandbox::EnvironmentDefinitionBuilder::new(); - let code = match sp_sandbox::Instance::new(&code, &env_builder, &mut ()) { - Ok(_) => 0, - Err(sp_sandbox::Error::Module) => 1, - Err(sp_sandbox::Error::Execution) => 2, - Err(sp_sandbox::Error::OutOfBounds) => 3, - }; - - code - } - - fn test_sandbox_get_global_val(code: Vec) -> i64 { - let env_builder = sp_sandbox::EnvironmentDefinitionBuilder::new(); - let instance = if let Ok(i) = sp_sandbox::Instance::new(&code, &env_builder, &mut ()) { - i - } else { - return 20; - }; - - match instance.get_global_val("test_global") { - Some(sp_sandbox::Value::I64(val)) => val, - None => 30, - _ => 40, - } - } - fn test_offchain_index_set() { sp_io::offchain_index::set(b"k", b"v"); } @@ -408,15 +353,112 @@ mod tasks { } } +/// A macro to define a test entrypoint for each available sandbox executor. +macro_rules! wasm_export_sandbox_test_functions { + ( + $( + fn $name:ident( + $( $arg_name:ident: $arg_ty:ty ),* $(,)? + ) $( -> $ret_ty:ty )? where T: SandboxInstance<$state:ty> $(,)? + { $( $fn_impl:tt )* } + )* + ) => { + $( + #[cfg(not(feature = "std"))] + fn $name( $($arg_name: $arg_ty),* ) $( -> $ret_ty )? where T: SandboxInstance<$state> { + $( $fn_impl )* + } + + paste::paste! { + sp_core::wasm_export_functions! { + fn [<$name _host>]( $($arg_name: $arg_ty),* ) $( -> $ret_ty )? { + $name::>( $( $arg_name ),* ) + } + + fn [<$name _embedded>]( $($arg_name: $arg_ty),* ) $( -> $ret_ty )? { + $name::>( $( $arg_name ),* ) + } + } + } + )* + }; +} + +wasm_export_sandbox_test_functions! { + fn test_sandbox(code: Vec) -> bool + where + T: SandboxInstance, + { + execute_sandboxed::(&code, &[]).is_ok() + } + + fn test_sandbox_args(code: Vec) -> bool + where + T: SandboxInstance, + { + execute_sandboxed::(&code, &[Value::I32(0x12345678), Value::I64(0x1234567887654321)]) + .is_ok() + } + + fn test_sandbox_return_val(code: Vec) -> bool + where + T: SandboxInstance, + { + let ok = match execute_sandboxed::(&code, &[Value::I32(0x1336)]) { + Ok(sp_sandbox::ReturnValue::Value(Value::I32(0x1337))) => true, + _ => false, + }; + + ok + } + + fn test_sandbox_instantiate(code: Vec) -> u8 + where + T: SandboxInstance<()>, + { + let env_builder = T::EnvironmentBuilder::new(); + let code = match T::new(&code, &env_builder, &mut ()) { + Ok(_) => 0, + Err(sp_sandbox::Error::Module) => 1, + Err(sp_sandbox::Error::Execution) => 2, + Err(sp_sandbox::Error::OutOfBounds) => 3, + }; + + code + } + + fn test_sandbox_get_global_val(code: Vec) -> i64 + where + T: SandboxInstance<()>, + { + let env_builder = T::EnvironmentBuilder::new(); + let instance = if let Ok(i) = T::new(&code, &env_builder, &mut ()) { + i + } else { + return 20 + }; + + match instance.get_global_val("test_global") { + Some(sp_sandbox::Value::I64(val)) => val, + None => 30, + _ => 40, + } + } +} + #[cfg(not(feature = "std"))] -fn execute_sandboxed( +struct State { + counter: u32, +} + +#[cfg(not(feature = "std"))] +fn execute_sandboxed( code: &[u8], args: &[Value], -) -> Result { - struct State { - counter: u32, - } - +) -> Result +where + T: sp_sandbox::SandboxInstance, +{ fn env_assert( _e: &mut State, args: &[Value], @@ -446,10 +488,10 @@ fn execute_sandboxed( let mut state = State { counter: 0 }; let env_builder = { - let mut env_builder = sp_sandbox::EnvironmentDefinitionBuilder::new(); + let mut env_builder = T::EnvironmentBuilder::new(); env_builder.add_host_func("env", "assert", env_assert); env_builder.add_host_func("env", "inc_counter", env_inc_counter); - let memory = match sp_sandbox::Memory::new(1, Some(16)) { + let memory = match T::Memory::new(1, Some(16)) { Ok(m) => m, Err(_) => unreachable!( " @@ -462,7 +504,7 @@ fn execute_sandboxed( env_builder }; - let mut instance = sp_sandbox::Instance::new(code, &env_builder, &mut state)?; + let mut instance = T::new(code, &env_builder, &mut state)?; let result = instance.invoke("call", args, &mut state); result.map_err(|_| sp_sandbox::HostError) diff --git a/client/executor/src/integration_tests/mod.rs b/client/executor/src/integration_tests/mod.rs index fe964f47ba374..1cded769c6856 100644 --- a/client/executor/src/integration_tests/mod.rs +++ b/client/executor/src/integration_tests/mod.rs @@ -71,6 +71,53 @@ macro_rules! test_wasm_execution { }; } +/// A macro to run a given test for each available WASM execution method *and* for each +/// sandbox execution method. +#[macro_export] +macro_rules! test_wasm_execution_sandbox { + ($method_name:ident) => { + paste::item! { + #[test] + fn [<$method_name _interpreted_host_executor>]() { + $method_name(WasmExecutionMethod::Interpreted, "_host"); + } + + #[test] + fn [<$method_name _interpreted_embedded_executor>]() { + $method_name(WasmExecutionMethod::Interpreted, "_embedded"); + } + + #[test] + #[cfg(feature = "wasmtime")] + fn [<$method_name _compiled_host_executor>]() { + $method_name(WasmExecutionMethod::Compiled, "_host"); + } + + #[test] + #[cfg(feature = "wasmtime")] + fn [<$method_name _compiled_embedded_executor>]() { + $method_name(WasmExecutionMethod::Compiled, "_embedded"); + } + } + }; + + (interpreted_only $method_name:ident) => { + paste::item! { + #[test] + fn [<$method_name _interpreted_host_executor>]() { + $method_name(WasmExecutionMethod::Interpreted, "_host"); + } + } + + paste::item! { + #[test] + fn [<$method_name _interpreted_embedded_executor>]() { + $method_name(WasmExecutionMethod::Interpreted, "_embedded"); + } + } + }; +} + fn call_in_wasm( function: &str, call_data: &[u8], diff --git a/client/executor/src/integration_tests/sandbox.rs b/client/executor/src/integration_tests/sandbox.rs index aacd493297cc8..2b536f541f088 100644 --- a/client/executor/src/integration_tests/sandbox.rs +++ b/client/executor/src/integration_tests/sandbox.rs @@ -17,12 +17,12 @@ // along with this program. If not, see . use super::{call_in_wasm, TestExternalities}; -use crate::{test_wasm_execution, WasmExecutionMethod}; +use crate::{test_wasm_execution_sandbox, WasmExecutionMethod}; use codec::Encode; -test_wasm_execution!(sandbox_should_work); -fn sandbox_should_work(wasm_method: WasmExecutionMethod) { +test_wasm_execution_sandbox!(sandbox_should_work); +fn sandbox_should_work(wasm_method: WasmExecutionMethod, fn_suffix: &str) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); @@ -51,11 +51,14 @@ fn sandbox_should_work(wasm_method: WasmExecutionMethod) { .unwrap() .encode(); - assert_eq!(call_in_wasm("test_sandbox", &code, wasm_method, &mut ext).unwrap(), true.encode()); + assert_eq!( + call_in_wasm(&format!("test_sandbox{}", fn_suffix), &code, wasm_method, &mut ext).unwrap(), + true.encode() + ); } -test_wasm_execution!(sandbox_trap); -fn sandbox_trap(wasm_method: WasmExecutionMethod) { +test_wasm_execution_sandbox!(sandbox_trap); +fn sandbox_trap(wasm_method: WasmExecutionMethod, fn_suffix: &str) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); @@ -72,11 +75,14 @@ fn sandbox_trap(wasm_method: WasmExecutionMethod) { ) .unwrap(); - assert_eq!(call_in_wasm("test_sandbox", &code, wasm_method, &mut ext).unwrap(), vec![0]); + assert_eq!( + call_in_wasm(&format!("test_sandbox{}", fn_suffix), &code, wasm_method, &mut ext).unwrap(), + vec![0] + ); } -test_wasm_execution!(start_called); -fn start_called(wasm_method: WasmExecutionMethod) { +test_wasm_execution_sandbox!(start_called); +fn start_called(wasm_method: WasmExecutionMethod, fn_suffix: &str) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); @@ -111,11 +117,14 @@ fn start_called(wasm_method: WasmExecutionMethod) { .unwrap() .encode(); - assert_eq!(call_in_wasm("test_sandbox", &code, wasm_method, &mut ext).unwrap(), true.encode()); + assert_eq!( + call_in_wasm(&format!("test_sandbox{}", fn_suffix), &code, wasm_method, &mut ext).unwrap(), + true.encode() + ); } -test_wasm_execution!(invoke_args); -fn invoke_args(wasm_method: WasmExecutionMethod) { +test_wasm_execution_sandbox!(invoke_args); +fn invoke_args(wasm_method: WasmExecutionMethod, fn_suffix: &str) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); @@ -147,13 +156,14 @@ fn invoke_args(wasm_method: WasmExecutionMethod) { .encode(); assert_eq!( - call_in_wasm("test_sandbox_args", &code, wasm_method, &mut ext,).unwrap(), + call_in_wasm(&format!("test_sandbox_args{}", fn_suffix), &code, wasm_method, &mut ext,) + .unwrap(), true.encode(), ); } -test_wasm_execution!(return_val); -fn return_val(wasm_method: WasmExecutionMethod) { +test_wasm_execution_sandbox!(return_val); +fn return_val(wasm_method: WasmExecutionMethod, fn_suffix: &str) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); @@ -173,13 +183,19 @@ fn return_val(wasm_method: WasmExecutionMethod) { .encode(); assert_eq!( - call_in_wasm("test_sandbox_return_val", &code, wasm_method, &mut ext,).unwrap(), + call_in_wasm( + &format!("test_sandbox_return_val{}", fn_suffix), + &code, + wasm_method, + &mut ext, + ) + .unwrap(), true.encode(), ); } -test_wasm_execution!(unlinkable_module); -fn unlinkable_module(wasm_method: WasmExecutionMethod) { +test_wasm_execution_sandbox!(unlinkable_module); +fn unlinkable_module(wasm_method: WasmExecutionMethod, fn_suffix: &str) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); @@ -197,13 +213,19 @@ fn unlinkable_module(wasm_method: WasmExecutionMethod) { .encode(); assert_eq!( - call_in_wasm("test_sandbox_instantiate", &code, wasm_method, &mut ext,).unwrap(), + call_in_wasm( + &format!("test_sandbox_instantiate{}", fn_suffix), + &code, + wasm_method, + &mut ext, + ) + .unwrap(), 1u8.encode(), ); } -test_wasm_execution!(corrupted_module); -fn corrupted_module(wasm_method: WasmExecutionMethod) { +test_wasm_execution_sandbox!(corrupted_module); +fn corrupted_module(wasm_method: WasmExecutionMethod, fn_suffix: &str) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); @@ -211,13 +233,19 @@ fn corrupted_module(wasm_method: WasmExecutionMethod) { let code = vec![0u8, 0, 0, 0, 1, 0, 0, 0].encode(); assert_eq!( - call_in_wasm("test_sandbox_instantiate", &code, wasm_method, &mut ext,).unwrap(), + call_in_wasm( + &format!("test_sandbox_instantiate{}", fn_suffix), + &code, + wasm_method, + &mut ext, + ) + .unwrap(), 1u8.encode(), ); } -test_wasm_execution!(start_fn_ok); -fn start_fn_ok(wasm_method: WasmExecutionMethod) { +test_wasm_execution_sandbox!(start_fn_ok); +fn start_fn_ok(wasm_method: WasmExecutionMethod, fn_suffix: &str) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); @@ -238,13 +266,19 @@ fn start_fn_ok(wasm_method: WasmExecutionMethod) { .encode(); assert_eq!( - call_in_wasm("test_sandbox_instantiate", &code, wasm_method, &mut ext,).unwrap(), + call_in_wasm( + &format!("test_sandbox_instantiate{}", fn_suffix), + &code, + wasm_method, + &mut ext, + ) + .unwrap(), 0u8.encode(), ); } -test_wasm_execution!(start_fn_traps); -fn start_fn_traps(wasm_method: WasmExecutionMethod) { +test_wasm_execution_sandbox!(start_fn_traps); +fn start_fn_traps(wasm_method: WasmExecutionMethod, fn_suffix: &str) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); @@ -266,13 +300,19 @@ fn start_fn_traps(wasm_method: WasmExecutionMethod) { .encode(); assert_eq!( - call_in_wasm("test_sandbox_instantiate", &code, wasm_method, &mut ext,).unwrap(), + call_in_wasm( + &format!("test_sandbox_instantiate{}", fn_suffix), + &code, + wasm_method, + &mut ext, + ) + .unwrap(), 2u8.encode(), ); } -test_wasm_execution!(get_global_val_works); -fn get_global_val_works(wasm_method: WasmExecutionMethod) { +test_wasm_execution_sandbox!(get_global_val_works); +fn get_global_val_works(wasm_method: WasmExecutionMethod, fn_suffix: &str) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); @@ -287,7 +327,13 @@ fn get_global_val_works(wasm_method: WasmExecutionMethod) { .encode(); assert_eq!( - call_in_wasm("test_sandbox_get_global_val", &code, wasm_method, &mut ext,).unwrap(), + call_in_wasm( + &format!("test_sandbox_get_global_val{}", fn_suffix), + &code, + wasm_method, + &mut ext, + ) + .unwrap(), 500i64.encode(), ); } diff --git a/frame/contracts/src/benchmarking/code.rs b/frame/contracts/src/benchmarking/code.rs index 6b90381e7d353..98f52f4719a61 100644 --- a/frame/contracts/src/benchmarking/code.rs +++ b/frame/contracts/src/benchmarking/code.rs @@ -35,7 +35,10 @@ use pwasm_utils::parity_wasm::{ }; use sp_core::crypto::UncheckedFrom; use sp_runtime::traits::Hash; -use sp_sandbox::{EnvironmentDefinitionBuilder, Memory}; +use sp_sandbox::{ + default_executor::{EnvironmentDefinitionBuilder, Memory}, + SandboxEnvironmentBuilder, SandboxMemory, +}; use sp_std::{borrow::ToOwned, prelude::*}; /// Pass to `create_code` in order to create a compiled `WasmModule`. diff --git a/frame/contracts/src/benchmarking/sandbox.rs b/frame/contracts/src/benchmarking/sandbox.rs index 320ac90cce64e..4412542b547df 100644 --- a/frame/contracts/src/benchmarking/sandbox.rs +++ b/frame/contracts/src/benchmarking/sandbox.rs @@ -20,7 +20,10 @@ /// ! environment that provides the seal interface as imported functions. use super::{code::WasmModule, Config}; use sp_core::crypto::UncheckedFrom; -use sp_sandbox::{EnvironmentDefinitionBuilder, Instance, Memory}; +use sp_sandbox::{ + default_executor::{EnvironmentDefinitionBuilder, Instance, Memory}, + SandboxEnvironmentBuilder, SandboxInstance, +}; /// Minimal execution environment without any exported functions. pub struct Sandbox { diff --git a/frame/contracts/src/wasm/mod.rs b/frame/contracts/src/wasm/mod.rs index 565a424323ac6..10aa0d19a04f7 100644 --- a/frame/contracts/src/wasm/mod.rs +++ b/frame/contracts/src/wasm/mod.rs @@ -36,6 +36,7 @@ use crate::{ use codec::{Decode, Encode}; use frame_support::dispatch::DispatchError; use sp_core::crypto::UncheckedFrom; +use sp_sandbox::{SandboxEnvironmentBuilder, SandboxInstance, SandboxMemory}; use sp_std::prelude::*; #[cfg(test)] pub use tests::MockExt; @@ -182,8 +183,8 @@ where function: &ExportedFunction, input_data: Vec, ) -> ExecResult { - let memory = - sp_sandbox::Memory::new(self.initial, Some(self.maximum)).unwrap_or_else(|_| { + let memory = sp_sandbox::default_executor::Memory::new(self.initial, Some(self.maximum)) + .unwrap_or_else(|_| { // unlike `.expect`, explicit panic preserves the source location. // Needed as we can't use `RUST_BACKTRACE` in here. panic!( @@ -193,7 +194,7 @@ where ) }); - let mut imports = sp_sandbox::EnvironmentDefinitionBuilder::new(); + let mut imports = sp_sandbox::default_executor::EnvironmentDefinitionBuilder::new(); imports.add_memory(self::prepare::IMPORT_MODULE_MEMORY, "memory", memory.clone()); runtime::Env::impls(&mut |module, name, func_ptr| { imports.add_host_func(module, name, func_ptr); @@ -209,7 +210,7 @@ where // Instantiate the instance from the instrumented module code and invoke the contract // entrypoint. - let result = sp_sandbox::Instance::new(&code, &imports, &mut runtime) + let result = sp_sandbox::default_executor::Instance::new(&code, &imports, &mut runtime) .and_then(|mut instance| instance.invoke(function.identifier(), &[], &mut runtime)); runtime.to_execution_result(result) diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index 52b864bf18eac..883dfd0802483 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -32,6 +32,7 @@ use pwasm_utils::parity_wasm::elements::ValueType; use sp_core::{crypto::UncheckedFrom, Bytes}; use sp_io::hashing::{blake2_128, blake2_256, keccak_256, sha2_256}; use sp_runtime::traits::Bounded; +use sp_sandbox::SandboxMemory; use sp_std::prelude::*; /// Every error that can be returned to a contract when it calls any of the host functions. @@ -357,7 +358,7 @@ fn already_charged(_: u32) -> Option { pub struct Runtime<'a, E: Ext + 'a> { ext: &'a mut E, input_data: Option>, - memory: sp_sandbox::Memory, + memory: sp_sandbox::default_executor::Memory, trap_reason: Option, } @@ -367,7 +368,11 @@ where ::AccountId: UncheckedFrom<::Hash> + AsRef<[u8]>, { - pub fn new(ext: &'a mut E, input_data: Vec, memory: sp_sandbox::Memory) -> Self { + pub fn new( + ext: &'a mut E, + input_data: Vec, + memory: sp_sandbox::default_executor::Memory, + ) -> Self { Runtime { ext, input_data: Some(input_data), memory, trap_reason: None } } diff --git a/primitives/sandbox/embedded_executor.rs b/primitives/sandbox/src/embedded_executor.rs similarity index 90% rename from primitives/sandbox/embedded_executor.rs rename to primitives/sandbox/src/embedded_executor.rs index 678da3c3aeaf5..c521ff2cb63fb 100755 --- a/primitives/sandbox/embedded_executor.rs +++ b/primitives/sandbox/src/embedded_executor.rs @@ -15,6 +15,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! An embedded WASM executor utilizing `wasmi`. + use super::{Error, HostError, HostFuncType, ReturnValue, Value, TARGET}; use alloc::string::String; use log::debug; @@ -27,13 +29,14 @@ use wasmi::{ RuntimeArgs, RuntimeValue, Signature, TableDescriptor, TableRef, Trap, TrapKind, }; +/// The linear memory used by the sandbox. #[derive(Clone)] pub struct Memory { memref: MemoryRef, } -impl Memory { - pub fn new(initial: u32, maximum: Option) -> Result { +impl super::SandboxMemory for Memory { + fn new(initial: u32, maximum: Option) -> Result { Ok(Memory { memref: MemoryInstance::alloc( Pages(initial as usize), @@ -43,12 +46,12 @@ impl Memory { }) } - pub fn get(&self, ptr: u32, buf: &mut [u8]) -> Result<(), Error> { + fn get(&self, ptr: u32, buf: &mut [u8]) -> Result<(), Error> { self.memref.get_into(ptr, buf).map_err(|_| Error::OutOfBounds)?; Ok(()) } - pub fn set(&self, ptr: u32, value: &[u8]) -> Result<(), Error> { + fn set(&self, ptr: u32, value: &[u8]) -> Result<(), Error> { self.memref.set(ptr, value).map_err(|_| Error::OutOfBounds)?; Ok(()) } @@ -118,20 +121,21 @@ enum ExternVal { Memory(Memory), } +/// A builder for the environment of the sandboxed WASM module. pub struct EnvironmentDefinitionBuilder { map: BTreeMap<(Vec, Vec), ExternVal>, defined_host_functions: DefinedHostFunctions, } -impl EnvironmentDefinitionBuilder { - pub fn new() -> EnvironmentDefinitionBuilder { +impl super::SandboxEnvironmentBuilder for EnvironmentDefinitionBuilder { + fn new() -> EnvironmentDefinitionBuilder { EnvironmentDefinitionBuilder { map: BTreeMap::new(), defined_host_functions: DefinedHostFunctions::new(), } } - pub fn add_host_func(&mut self, module: N1, field: N2, f: HostFuncType) + fn add_host_func(&mut self, module: N1, field: N2, f: HostFuncType) where N1: Into>, N2: Into>, @@ -140,7 +144,7 @@ impl EnvironmentDefinitionBuilder { self.map.insert((module.into(), field.into()), ExternVal::HostFunc(idx)); } - pub fn add_memory(&mut self, module: N1, field: N2, mem: Memory) + fn add_memory(&mut self, module: N1, field: N2, mem: Memory) where N1: Into>, N2: Into>, @@ -213,14 +217,18 @@ impl ImportResolver for EnvironmentDefinitionBuilder { } } +/// Sandboxed instance of a WASM module. pub struct Instance { instance: ModuleRef, defined_host_functions: DefinedHostFunctions, _marker: PhantomData, } -impl Instance { - pub fn new( +impl super::SandboxInstance for Instance { + type Memory = Memory; + type EnvironmentBuilder = EnvironmentDefinitionBuilder; + + fn new( code: &[u8], env_def_builder: &EnvironmentDefinitionBuilder, state: &mut T, @@ -241,12 +249,7 @@ impl Instance { Ok(Instance { instance, defined_host_functions, _marker: PhantomData:: }) } - pub fn invoke( - &mut self, - name: &str, - args: &[Value], - state: &mut T, - ) -> Result { + fn invoke(&mut self, name: &str, args: &[Value], state: &mut T) -> Result { let args = args.iter().cloned().map(to_wasmi).collect::>(); let mut externals = @@ -260,7 +263,7 @@ impl Instance { } } - pub fn get_global_val(&self, name: &str) -> Option { + fn get_global_val(&self, name: &str) -> Option { let global = self.instance.export_by_name(name)?.as_global()?.get(); Some(to_interface(global)) @@ -289,7 +292,8 @@ fn to_interface(value: RuntimeValue) -> Value { #[cfg(test)] mod tests { - use crate::{EnvironmentDefinitionBuilder, Error, HostError, Instance, ReturnValue, Value}; + use super::{EnvironmentDefinitionBuilder, Instance}; + use crate::{Error, HostError, ReturnValue, SandboxEnvironmentBuilder, SandboxInstance, Value}; use assert_matches::assert_matches; fn execute_sandboxed(code: &[u8], args: &[Value]) -> Result { diff --git a/primitives/sandbox/host_executor.rs b/primitives/sandbox/src/host_executor.rs similarity index 78% rename from primitives/sandbox/host_executor.rs rename to primitives/sandbox/src/host_executor.rs index d2836e2ffd1eb..43484dd66a00d 100755 --- a/primitives/sandbox/host_executor.rs +++ b/primitives/sandbox/src/host_executor.rs @@ -15,15 +15,17 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! A WASM executor utilizing the sandbox runtime interface of the host. + +use super::{Error, HostFuncType, ReturnValue, Value}; use codec::{Decode, Encode}; use sp_core::sandbox as sandbox_primitives; use sp_io::sandbox; -use sp_std::{prelude::*, slice, marker, mem, vec, rc::Rc}; -use super::{Error, Value, ReturnValue, HostFuncType}; +use sp_std::{marker, mem, prelude::*, rc::Rc, slice, vec}; mod ffi { - use sp_std::mem; use super::HostFuncType; + use sp_std::mem; /// Index into the default table that points to a `HostFuncType`. pub type HostFuncIndex = usize; @@ -38,8 +40,9 @@ mod ffi { pub unsafe fn coerce_host_index_to_func(idx: HostFuncIndex) -> HostFuncType { // We need to ensure that sizes of a callable function pointer and host function index is // indeed equal. - // We can't use `static_assertions` create because it makes compiler panic, fallback to runtime assert. - // const_assert!(mem::size_of::() == mem::size_of::>()); + // We can't use `static_assertions` create because it makes compiler panic, fallback to + // runtime assert. const_assert!(mem::size_of::() == + // mem::size_of::>()); assert!(mem::size_of::() == mem::size_of::>()); mem::transmute::>(idx) } @@ -55,6 +58,7 @@ impl Drop for MemoryHandle { } } +/// The linear memory used by the sandbox. #[derive(Clone)] pub struct Memory { // Handle to memory instance is wrapped to add reference-counting semantics @@ -62,29 +66,20 @@ pub struct Memory { handle: Rc, } -impl Memory { - pub fn new(initial: u32, maximum: Option) -> Result { - let maximum = if let Some(maximum) = maximum { - maximum - } else { - sandbox_primitives::MEM_UNLIMITED - }; +impl super::SandboxMemory for Memory { + fn new(initial: u32, maximum: Option) -> Result { + let maximum = + if let Some(maximum) = maximum { maximum } else { sandbox_primitives::MEM_UNLIMITED }; match sandbox::memory_new(initial, maximum) { sandbox_primitives::ERR_MODULE => Err(Error::Module), - memory_idx => Ok(Memory { - handle: Rc::new(MemoryHandle { memory_idx, }), - }), + memory_idx => Ok(Memory { handle: Rc::new(MemoryHandle { memory_idx }) }), } } - pub fn get(&self, offset: u32, buf: &mut [u8]) -> Result<(), Error> { - let result = sandbox::memory_get( - self.handle.memory_idx, - offset, - buf.as_mut_ptr(), - buf.len() as u32, - ); + fn get(&self, offset: u32, buf: &mut [u8]) -> Result<(), Error> { + let result = + sandbox::memory_get(self.handle.memory_idx, offset, buf.as_mut_ptr(), buf.len() as u32); match result { sandbox_primitives::ERR_OK => Ok(()), sandbox_primitives::ERR_OUT_OF_BOUNDS => Err(Error::OutOfBounds), @@ -92,11 +87,11 @@ impl Memory { } } - pub fn set(&self, offset: u32, val: &[u8]) -> Result<(), Error> { + fn set(&self, offset: u32, val: &[u8]) -> Result<(), Error> { let result = sandbox::memory_set( self.handle.memory_idx, offset, - val.as_ptr() as _ , + val.as_ptr() as _, val.len() as u32, ); match result { @@ -107,6 +102,7 @@ impl Memory { } } +/// A builder for the environment of the sandboxed WASM module. pub struct EnvironmentDefinitionBuilder { env_def: sandbox_primitives::EnvironmentDefinition, retained_memories: Vec, @@ -114,16 +110,6 @@ pub struct EnvironmentDefinitionBuilder { } impl EnvironmentDefinitionBuilder { - pub fn new() -> EnvironmentDefinitionBuilder { - EnvironmentDefinitionBuilder { - env_def: sandbox_primitives::EnvironmentDefinition { - entries: Vec::new(), - }, - retained_memories: Vec::new(), - _marker: marker::PhantomData::, - } - } - fn add_entry( &mut self, module: N1, @@ -140,8 +126,18 @@ impl EnvironmentDefinitionBuilder { }; self.env_def.entries.push(entry); } +} - pub fn add_host_func(&mut self, module: N1, field: N2, f: HostFuncType) +impl super::SandboxEnvironmentBuilder for EnvironmentDefinitionBuilder { + fn new() -> EnvironmentDefinitionBuilder { + EnvironmentDefinitionBuilder { + env_def: sandbox_primitives::EnvironmentDefinition { entries: Vec::new() }, + retained_memories: Vec::new(), + _marker: marker::PhantomData::, + } + } + + fn add_host_func(&mut self, module: N1, field: N2, f: HostFuncType) where N1: Into>, N2: Into>, @@ -150,7 +146,7 @@ impl EnvironmentDefinitionBuilder { self.add_entry(module, field, f); } - pub fn add_memory(&mut self, module: N1, field: N2, mem: Memory) + fn add_memory(&mut self, module: N1, field: N2, mem: Memory) where N1: Into>, N2: Into>, @@ -163,6 +159,7 @@ impl EnvironmentDefinitionBuilder { } } +/// Sandboxed instance of a WASM module. pub struct Instance { instance_idx: u32, _retained_memories: Vec, @@ -211,8 +208,11 @@ extern "C" fn dispatch_thunk( } } -impl Instance { - pub fn new( +impl super::SandboxInstance for Instance { + type Memory = Memory; + type EnvironmentBuilder = EnvironmentDefinitionBuilder; + + fn new( code: &[u8], env_def_builder: &EnvironmentDefinitionBuilder, state: &mut T, @@ -242,12 +242,7 @@ impl Instance { }) } - pub fn invoke( - &mut self, - name: &str, - args: &[Value], - state: &mut T, - ) -> Result { + fn invoke(&mut self, name: &str, args: &[Value], state: &mut T) -> Result { let serialized_args = args.to_vec().encode(); let mut return_val = vec![0u8; ReturnValue::ENCODED_MAX_SIZE]; @@ -262,16 +257,16 @@ impl Instance { match result { sandbox_primitives::ERR_OK => { - let return_val = ReturnValue::decode(&mut &return_val[..]) - .map_err(|_| Error::Execution)?; + let return_val = + ReturnValue::decode(&mut &return_val[..]).map_err(|_| Error::Execution)?; Ok(return_val) - } + }, sandbox_primitives::ERR_EXECUTION => Err(Error::Execution), _ => unreachable!(), } } - pub fn get_global_val(&self, name: &str) -> Option { + fn get_global_val(&self, name: &str) -> Option { sandbox::get_global_val(self.instance_idx, name) } } diff --git a/primitives/sandbox/src/lib.rs b/primitives/sandbox/src/lib.rs index 1724b4152ff3d..f1a24732b7a0a 100755 --- a/primitives/sandbox/src/lib.rs +++ b/primitives/sandbox/src/lib.rs @@ -48,13 +48,15 @@ pub use sp_wasm_interface::{ReturnValue, Value}; /// The target used for logging. const TARGET: &str = "runtime::sandbox"; -mod imp { - #[cfg(all(feature = "wasmer-sandbox", not(feature = "std")))] - include!("../host_executor.rs"); +pub mod embedded_executor; +#[cfg(not(feature = "std"))] +pub mod host_executor; - #[cfg(not(all(feature = "wasmer-sandbox", not(feature = "std"))))] - include!("../embedded_executor.rs"); -} +#[cfg(all(feature = "wasmer-sandbox", not(feature = "std")))] +pub use host_executor as default_executor; + +#[cfg(not(all(feature = "wasmer-sandbox", not(feature = "std"))))] +pub use embedded_executor as default_executor; /// Error that can occur while using this crate. #[derive(sp_core::RuntimeDebug)] @@ -88,12 +90,7 @@ pub type HostFuncType = fn(&mut T, &[Value]) -> Result) -> Result { - Ok(Memory { inner: imp::Memory::new(initial, maximum)? }) - } + fn new(initial: u32, maximum: Option) -> Result; /// Read a memory area at the address `ptr` with the size of the provided slice `buf`. /// /// Returns `Err` if the range is out-of-bounds. - pub fn get(&self, ptr: u32, buf: &mut [u8]) -> Result<(), Error> { - self.inner.get(ptr, buf) - } + fn get(&self, ptr: u32, buf: &mut [u8]) -> Result<(), Error>; /// Write a memory area at the address `ptr` with contents of the provided slice `buf`. /// /// Returns `Err` if the range is out-of-bounds. - pub fn set(&self, ptr: u32, value: &[u8]) -> Result<(), Error> { - self.inner.set(ptr, value) - } + fn set(&self, ptr: u32, value: &[u8]) -> Result<(), Error>; } /// Struct that can be used for defining an environment for a sandboxed module. /// /// The sandboxed module can access only the entities which were defined and passed /// to the module at the instantiation time. -pub struct EnvironmentDefinitionBuilder { - inner: imp::EnvironmentDefinitionBuilder, -} - -impl EnvironmentDefinitionBuilder { +pub trait SandboxEnvironmentBuilder: Sized { /// Construct a new `EnvironmentDefinitionBuilder`. - pub fn new() -> EnvironmentDefinitionBuilder { - EnvironmentDefinitionBuilder { inner: imp::EnvironmentDefinitionBuilder::new() } - } + fn new() -> Self; /// Register a host function in this environment definition. /// @@ -143,32 +128,28 @@ impl EnvironmentDefinitionBuilder { /// can import function passed here with any signature it wants. It can even import /// the same function (i.e. with same `module` and `field`) several times. It's up to /// the user code to check or constrain the types of signatures. - pub fn add_host_func(&mut self, module: N1, field: N2, f: HostFuncType) + fn add_host_func(&mut self, module: N1, field: N2, f: HostFuncType) where N1: Into>, - N2: Into>, - { - self.inner.add_host_func(module, field, f); - } + N2: Into>; /// Register a memory in this environment definition. - pub fn add_memory(&mut self, module: N1, field: N2, mem: Memory) + fn add_memory(&mut self, module: N1, field: N2, mem: Memory) where N1: Into>, - N2: Into>, - { - self.inner.add_memory(module, field, mem.inner); - } + N2: Into>; } /// Sandboxed instance of a wasm module. /// /// This instance can be used for invoking exported functions. -pub struct Instance { - inner: imp::Instance, -} +pub trait SandboxInstance: Sized { + /// The memory type used for this sandbox. + type Memory: SandboxMemory; + + /// The environment builder used to construct this sandbox. + type EnvironmentBuilder: SandboxEnvironmentBuilder; -impl Instance { /// Instantiate a module with the given [`EnvironmentDefinitionBuilder`]. It will /// run the `start` function (if it is present in the module) with the given `state`. /// @@ -177,13 +158,11 @@ impl Instance { /// will be returned. /// /// [`EnvironmentDefinitionBuilder`]: struct.EnvironmentDefinitionBuilder.html - pub fn new( + fn new( code: &[u8], - env_def_builder: &EnvironmentDefinitionBuilder, - state: &mut T, - ) -> Result, Error> { - Ok(Instance { inner: imp::Instance::new(code, &env_def_builder.inner, state)? }) - } + env_def_builder: &Self::EnvironmentBuilder, + state: &mut State, + ) -> Result; /// Invoke an exported function with the given name. /// @@ -196,19 +175,15 @@ impl Instance { /// - If types of the arguments passed to the function doesn't match function signature then /// trap occurs (as if the exported function was called via call_indirect), /// - Trap occurred at the execution time. - pub fn invoke( + fn invoke( &mut self, name: &str, args: &[Value], - state: &mut T, - ) -> Result { - self.inner.invoke(name, args, state) - } + state: &mut State, + ) -> Result; /// Get the value from a global with the given `name`. /// /// Returns `Some(_)` if the global could be found. - pub fn get_global_val(&self, name: &str) -> Option { - self.inner.get_global_val(name) - } + fn get_global_val(&self, name: &str) -> Option; }