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
+2
View File
@@ -10,6 +10,7 @@ wasmi = { version = "0.6.2", optional = true }
sp-core = { version = "2.0.0", default-features = false, path = "../core" }
sp-std = { version = "2.0.0", default-features = false, path = "../std" }
sp-io = { version = "2.0.0", default-features = false, path = "../io" }
sp-wasm-interface = { version = "2.0.0", default-features = false, path = "../wasm-interface" }
codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false }
[dev-dependencies]
@@ -24,5 +25,6 @@ std = [
"sp-std/std",
"codec/std",
"sp-io/std",
"sp-wasm-interface/std",
]
strict = []
+11 -3
View File
@@ -40,7 +40,8 @@
use sp_std::prelude::*;
pub use sp_core::sandbox::{TypedValue, ReturnValue, HostError};
pub use sp_core::sandbox::HostError;
pub use sp_wasm_interface::{Value, ReturnValue};
mod imp {
#[cfg(feature = "std")]
@@ -75,7 +76,7 @@ impl From<Error> for HostError {
/// supervisor in [`EnvironmentDefinitionBuilder`].
///
/// [`EnvironmentDefinitionBuilder`]: struct.EnvironmentDefinitionBuilder.html
pub type HostFuncType<T> = fn(&mut T, &[TypedValue]) -> Result<ReturnValue, HostError>;
pub type HostFuncType<T> = fn(&mut T, &[Value]) -> Result<ReturnValue, HostError>;
/// Reference to a sandboxed linear memory, that
/// will be used by the guest module.
@@ -197,9 +198,16 @@ impl<T> Instance<T> {
pub fn invoke(
&mut self,
name: &str,
args: &[TypedValue],
args: &[Value],
state: &mut T,
) -> Result<ReturnValue, Error> {
self.inner.invoke(name, args, state)
}
/// 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<Value> {
self.inner.get_global_val(name)
}
}
+28 -39
View File
@@ -23,7 +23,7 @@ use wasmi::{
RuntimeArgs, RuntimeValue, Signature, TableDescriptor, TableRef, Trap, TrapKind
};
use wasmi::memory_units::Pages;
use super::{Error, TypedValue, ReturnValue, HostFuncType, HostError};
use super::{Error, Value, ReturnValue, HostFuncType, HostError};
#[derive(Clone)]
pub struct Memory {
@@ -88,27 +88,7 @@ impl fmt::Display for DummyHostError {
}
}
impl wasmi::HostError for DummyHostError {
}
fn from_runtime_value(v: RuntimeValue) -> TypedValue {
match v {
RuntimeValue::I32(v) => TypedValue::I32(v),
RuntimeValue::I64(v) => TypedValue::I64(v),
RuntimeValue::F32(v) => TypedValue::F32(v.to_bits() as i32),
RuntimeValue::F64(v) => TypedValue::F64(v.to_bits() as i64),
}
}
fn to_runtime_value(v: TypedValue) -> RuntimeValue {
use wasmi::nan_preserving_float::{F32, F64};
match v {
TypedValue::I32(v) => RuntimeValue::I32(v as i32),
TypedValue::I64(v) => RuntimeValue::I64(v as i64),
TypedValue::F32(v_bits) => RuntimeValue::F32(F32::from_bits(v_bits as u32)),
TypedValue::F64(v_bits) => RuntimeValue::F64(F64::from_bits(v_bits as u64)),
}
}
impl wasmi::HostError for DummyHostError {}
struct GuestExternals<'a, T: 'a> {
state: &'a mut T,
@@ -124,13 +104,13 @@ impl<'a, T> Externals for GuestExternals<'a, T> {
let args = args.as_ref()
.iter()
.cloned()
.map(from_runtime_value)
.map(Into::into)
.collect::<Vec<_>>();
let result = (self.defined_host_functions.funcs[index])(self.state, &args);
match result {
Ok(value) => Ok(match value {
ReturnValue::Value(v) => Some(to_runtime_value(v)),
ReturnValue::Value(v) => Some(v.into()),
ReturnValue::Unit => None,
}),
Err(HostError) => Err(TrapKind::Host(Box::new(DummyHostError)).into()),
@@ -253,7 +233,7 @@ impl<T> ImportResolver for EnvironmentDefinitionBuilder<T> {
pub struct Instance<T> {
instance: ModuleRef,
defined_host_functions: DefinedHostFunctions<T>,
_marker: ::std::marker::PhantomData<T>,
_marker: std::marker::PhantomData<T>,
}
impl<T> Instance<T> {
@@ -281,14 +261,14 @@ impl<T> Instance<T> {
Ok(Instance {
instance,
defined_host_functions,
_marker: ::std::marker::PhantomData::<T>,
_marker: std::marker::PhantomData::<T>,
})
}
pub fn invoke(
&mut self,
name: &str,
args: &[TypedValue],
args: &[Value],
state: &mut T,
) -> Result<ReturnValue, Error> {
let args = args.iter().cloned().map(Into::into).collect::<Vec<_>>();
@@ -306,20 +286,29 @@ impl<T> Instance<T> {
Err(_err) => Err(Error::Execution),
}
}
pub fn get_global_val(&self, name: &str) -> Option<Value> {
let global = self.instance
.export_by_name(name)?
.as_global()?
.get();
Some(global.into())
}
}
#[cfg(test)]
mod tests {
use wabt;
use crate::{Error, TypedValue, ReturnValue, HostError, EnvironmentDefinitionBuilder, Instance};
use crate::{Error, Value, ReturnValue, HostError, EnvironmentDefinitionBuilder, Instance};
use assert_matches::assert_matches;
fn execute_sandboxed(code: &[u8], args: &[TypedValue]) -> Result<ReturnValue, HostError> {
fn execute_sandboxed(code: &[u8], args: &[Value]) -> Result<ReturnValue, HostError> {
struct State {
counter: u32,
}
fn env_assert(_e: &mut State, args: &[TypedValue]) -> Result<ReturnValue, HostError> {
fn env_assert(_e: &mut State, args: &[Value]) -> Result<ReturnValue, HostError> {
if args.len() != 1 {
return Err(HostError);
}
@@ -330,16 +319,16 @@ mod tests {
Err(HostError)
}
}
fn env_inc_counter(e: &mut State, args: &[TypedValue]) -> Result<ReturnValue, HostError> {
fn env_inc_counter(e: &mut State, args: &[Value]) -> Result<ReturnValue, HostError> {
if args.len() != 1 {
return Err(HostError);
}
let inc_by = args[0].as_i32().ok_or_else(|| HostError)?;
e.counter += inc_by as u32;
Ok(ReturnValue::Value(TypedValue::I32(e.counter as i32)))
Ok(ReturnValue::Value(Value::I32(e.counter as i32)))
}
/// Function that takes one argument of any type and returns that value.
fn env_polymorphic_id(_e: &mut State, args: &[TypedValue]) -> Result<ReturnValue, HostError> {
fn env_polymorphic_id(_e: &mut State, args: &[Value]) -> Result<ReturnValue, HostError> {
if args.len() != 1 {
return Err(HostError);
}
@@ -387,8 +376,8 @@ mod tests {
let result = execute_sandboxed(
&code,
&[
TypedValue::I32(0x12345678),
TypedValue::I64(0x1234567887654321),
Value::I32(0x12345678),
Value::I64(0x1234567887654321),
]
);
assert!(result.is_ok());
@@ -410,10 +399,10 @@ mod tests {
let return_val = execute_sandboxed(
&code,
&[
TypedValue::I32(0x1336),
Value::I32(0x1336),
]
).unwrap();
assert_eq!(return_val, ReturnValue::Value(TypedValue::I32(0x1337)));
assert_eq!(return_val, ReturnValue::Value(Value::I32(0x1337)));
}
#[test]
@@ -453,8 +442,8 @@ mod tests {
#[test]
fn cant_return_unmatching_type() {
fn env_returns_i32(_e: &mut (), _args: &[TypedValue]) -> Result<ReturnValue, HostError> {
Ok(ReturnValue::Value(TypedValue::I32(42)))
fn env_returns_i32(_e: &mut (), _args: &[Value]) -> Result<ReturnValue, HostError> {
Ok(ReturnValue::Value(Value::I32(42)))
}
let mut env_builder = EnvironmentDefinitionBuilder::new();
+9 -5
View File
@@ -18,7 +18,7 @@ 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, TypedValue, ReturnValue, HostFuncType};
use super::{Error, Value, ReturnValue, HostFuncType};
mod ffi {
use sp_std::mem;
@@ -183,7 +183,7 @@ extern "C" fn dispatch_thunk<T>(
slice::from_raw_parts(serialized_args_ptr, serialized_args_len)
}
};
let args = Vec::<TypedValue>::decode(&mut &serialized_args[..]).expect(
let args = Vec::<Value>::decode(&mut &serialized_args[..]).expect(
"serialized args should be provided by the runtime;
correctly serialized data should be deserializable;
qed",
@@ -244,11 +244,11 @@ impl<T> Instance<T> {
pub fn invoke(
&mut self,
name: &str,
args: &[TypedValue],
args: &[Value],
state: &mut T,
) -> Result<ReturnValue, Error> {
let serialized_args = args.to_vec().encode();
let mut return_val = vec![0u8; sandbox_primitives::ReturnValue::ENCODED_MAX_SIZE];
let mut return_val = vec![0u8; ReturnValue::ENCODED_MAX_SIZE];
let result = sandbox::invoke(
self.instance_idx,
@@ -261,7 +261,7 @@ impl<T> Instance<T> {
match result {
sandbox_primitives::ERR_OK => {
let return_val = sandbox_primitives::ReturnValue::decode(&mut &return_val[..])
let return_val = ReturnValue::decode(&mut &return_val[..])
.map_err(|_| Error::Execution)?;
Ok(return_val)
}
@@ -269,6 +269,10 @@ impl<T> Instance<T> {
_ => unreachable!(),
}
}
pub fn get_global_val(&self, name: &str) -> Option<Value> {
sandbox::get_global_val(self.instance_idx, name)
}
}
impl<T> Drop for Instance<T> {