Add get_global for Sandbox (#4756)

* Add `get_global` for `Sandbox`

This pr adds `get_global` to retrieve a `global` variable from an
instantiated sandbox wasm blob.

* Bump `spec_version`

* Update primitives/wasm-interface/src/lib.rs

Co-Authored-By: Sergei Pepyakin <sergei@parity.io>

* `get_global` -> `get_global_val`

Co-authored-by: Sergei Pepyakin <s.pepyakin@gmail.com>
Co-authored-by: Gavin Wood <github@gavwood.com>
This commit is contained in:
Bastian Köcher
2020-01-29 16:24:40 +01:00
committed by GitHub
parent ae1e9002d7
commit 4c36143375
23 changed files with 275 additions and 195 deletions
@@ -224,7 +224,8 @@ fn trap(msg: &'static str) -> Trap {
}
fn deserialize_result(serialized_result: &[u8]) -> std::result::Result<Option<RuntimeValue>, Trap> {
use self::sandbox_primitives::{HostError, ReturnValue};
use self::sandbox_primitives::HostError;
use sp_wasm_interface::ReturnValue;
let result_val = std::result::Result::<ReturnValue, HostError>::decode(&mut &serialized_result[..])
.map_err(|_| trap("Decoding Result<ReturnValue, HostError> failed!"))?;
@@ -260,7 +261,7 @@ impl<'a, FE: SandboxCapabilities + 'a> Externals for GuestExternals<'a, FE> {
let invoke_args_data: Vec<u8> = args.as_ref()
.iter()
.cloned()
.map(sandbox_primitives::TypedValue::from)
.map(sp_wasm_interface::Value::from)
.collect::<Vec<_>>()
.encode();
@@ -362,6 +363,18 @@ impl<FR> SandboxInstance<FR> {
},
)
}
/// 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<sp_wasm_interface::Value> {
let global = self.instance
.export_by_name(name)?
.as_global()?
.get();
Some(global.into())
}
}
/// Error occurred during instantiation of a sandboxed module.
@@ -17,6 +17,8 @@ use sp_io::{
use sp_runtime::{print, traits::{BlakeTwo256, Hash}};
#[cfg(not(feature = "std"))]
use sp_core::{ed25519, sr25519};
#[cfg(not(feature = "std"))]
use sp_sandbox::Value;
extern "C" {
#[allow(dead_code)]
@@ -133,8 +135,8 @@ sp_core::wasm_export_functions! {
execute_sandboxed(
&code,
&[
sp_sandbox::TypedValue::I32(0x12345678),
sp_sandbox::TypedValue::I64(0x1234567887654321),
Value::I32(0x12345678),
Value::I64(0x1234567887654321),
],
).is_ok()
}
@@ -143,10 +145,10 @@ sp_core::wasm_export_functions! {
let ok = match execute_sandboxed(
&code,
&[
sp_sandbox::TypedValue::I32(0x1336),
Value::I32(0x1336),
]
) {
Ok(sp_sandbox::ReturnValue::Value(sp_sandbox::TypedValue::I32(0x1337))) => true,
Ok(sp_sandbox::ReturnValue::Value(Value::I32(0x1337))) => true,
_ => false,
};
@@ -165,6 +167,22 @@ sp_core::wasm_export_functions! {
code
}
fn test_sandbox_get_global_val(code: Vec<u8>) -> 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,
val => 40,
}
}
fn test_offchain_local_storage() -> bool {
let kind = sp_core::offchain::StorageKind::PERSISTENT;
assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), None);
@@ -262,7 +280,7 @@ sp_core::wasm_export_functions! {
#[cfg(not(feature = "std"))]
fn execute_sandboxed(
code: &[u8],
args: &[sp_sandbox::TypedValue],
args: &[Value],
) -> Result<sp_sandbox::ReturnValue, sp_sandbox::HostError> {
struct State {
counter: u32,
@@ -270,7 +288,7 @@ fn execute_sandboxed(
fn env_assert(
_e: &mut State,
args: &[sp_sandbox::TypedValue],
args: &[Value],
) -> Result<sp_sandbox::ReturnValue, sp_sandbox::HostError> {
if args.len() != 1 {
return Err(sp_sandbox::HostError);
@@ -284,14 +302,14 @@ fn execute_sandboxed(
}
fn env_inc_counter(
e: &mut State,
args: &[sp_sandbox::TypedValue],
args: &[Value],
) -> Result<sp_sandbox::ReturnValue, sp_sandbox::HostError> {
if args.len() != 1 {
return Err(sp_sandbox::HostError);
}
let inc_by = args[0].as_i32().ok_or_else(|| sp_sandbox::HostError)?;
e.counter += inc_by as u32;
Ok(sp_sandbox::ReturnValue::Value(sp_sandbox::TypedValue::I32(e.counter as i32)))
Ok(sp_sandbox::ReturnValue::Value(Value::I32(e.counter as i32)))
}
let mut state = State { counter: 0 };
@@ -88,7 +88,7 @@ fn call_not_existing_function(wasm_method: WasmExecutionMethod) {
#[cfg(feature = "wasmtime")]
WasmExecutionMethod::Compiled => assert_eq!(
&format!("{:?}", e),
"Other(\"call to undefined external function with index 67\")"
"Other(\"call to undefined external function with index 68\")"
),
}
}
@@ -117,7 +117,7 @@ fn call_yet_another_not_existing_function(wasm_method: WasmExecutionMethod) {
#[cfg(feature = "wasmtime")]
WasmExecutionMethod::Compiled => assert_eq!(
&format!("{:?}", e),
"Other(\"call to undefined external function with index 68\")"
"Other(\"call to undefined external function with index 69\")"
),
}
}
@@ -302,3 +302,26 @@ fn start_fn_traps(wasm_method: WasmExecutionMethod) {
2u8.encode(),
);
}
#[test_case(WasmExecutionMethod::Interpreted)]
#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))]
fn get_global_val_works(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let code = wabt::wat2wasm(r#"
(module
(global (export "test_global") i64 (i64.const 500))
)
"#).unwrap().encode();
assert_eq!(
call_in_wasm(
"test_sandbox_get_global_val",
&code,
wasm_method,
&mut ext,
).unwrap(),
500i64.encode(),
);
}
+13 -2
View File
@@ -213,7 +213,7 @@ impl<'a> Sandbox for FunctionExecutor<'a> {
trace!(target: "sp-sandbox", "invoke, instance_idx={}", instance_id);
// Deserialize arguments and convert them into wasmi types.
let args = Vec::<sandbox_primitives::TypedValue>::decode(&mut &args[..])
let args = Vec::<sp_wasm_interface::Value>::decode(&mut &args[..])
.map_err(|_| "Can't decode serialized arguments for the invocation")?
.into_iter()
.map(Into::into)
@@ -226,7 +226,7 @@ impl<'a> Sandbox for FunctionExecutor<'a> {
Ok(None) => Ok(sandbox_primitives::ERR_OK),
Ok(Some(val)) => {
// Serialize return value and write it back into the memory.
sandbox_primitives::ReturnValue::Value(val.into()).using_encoded(|val| {
sp_wasm_interface::ReturnValue::Value(val.into()).using_encoded(|val| {
if val.len() > return_val_len as usize {
Err("Return value buffer is too small")?;
}
@@ -269,6 +269,17 @@ impl<'a> Sandbox for FunctionExecutor<'a> {
Ok(instance_idx_or_err_code as u32)
}
fn get_global_val(
&self,
instance_idx: u32,
name: &str,
) -> WResult<Option<sp_wasm_interface::Value>> {
self.sandbox_store
.instance(instance_idx)
.map(|i| i.get_global_val(name))
.map_err(|e| e.to_string())
}
}
/// Will be used on initialization of a module to resolve function and memory imports.
@@ -286,7 +286,7 @@ impl<'a> Sandbox for FunctionExecutor<'a> {
trace!(target: "sp-sandbox", "invoke, instance_idx={}", instance_id);
// Deserialize arguments and convert them into wasmi types.
let args = Vec::<sandbox_primitives::TypedValue>::decode(&mut &args[..])
let args = Vec::<sp_wasm_interface::Value>::decode(&mut &args[..])
.map_err(|_| "Can't decode serialized arguments for the invocation")?
.into_iter()
.map(Into::into)
@@ -299,7 +299,7 @@ impl<'a> Sandbox for FunctionExecutor<'a> {
Ok(None) => Ok(sandbox_primitives::ERR_OK),
Ok(Some(val)) => {
// Serialize return value and write it back into the memory.
sandbox_primitives::ReturnValue::Value(val.into()).using_encoded(|val| {
sp_wasm_interface::ReturnValue::Value(val.into()).using_encoded(|val| {
if val.len() > return_val_len as usize {
Err("Return value buffer is too small")?;
}
@@ -337,6 +337,17 @@ impl<'a> Sandbox for FunctionExecutor<'a> {
Ok(instance_idx_or_err_code as u32)
}
fn get_global_val(
&self,
instance_idx: u32,
name: &str,
) -> WResult<Option<sp_wasm_interface::Value>> {
self.sandbox_store
.instance(instance_idx)
.map(|i| i.get_global_val(name))
.map_err(|e| e.to_string())
}
}
// The storage for a Wasmtime invocation argument.