Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Commit

Permalink
Argument passing and returning values when invoking sandboxed funcs (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
pepyakin authored Jun 19, 2018
1 parent d4649d2 commit fea8f05
Show file tree
Hide file tree
Showing 18 changed files with 291 additions and 46 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
85 changes: 74 additions & 11 deletions substrate/executor/src/sandbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ use primitives::sandbox as sandbox_primitives;
use wasm_utils::DummyUserError;
use wasmi;
use wasmi::memory_units::Pages;
use wasmi::{Externals, FuncRef, ImportResolver, MemoryInstance, MemoryRef, Module, ModuleInstance,
ModuleRef, RuntimeArgs, RuntimeValue, Trap, TrapKind};
use wasmi::{
Externals, FuncRef, ImportResolver, MemoryInstance, MemoryRef, Module, ModuleInstance,
ModuleRef, RuntimeArgs, RuntimeValue, Trap, TrapKind
};

/// Index of a function inside the supervisor.
///
Expand Down Expand Up @@ -111,22 +113,26 @@ impl ImportResolver for Imports {

fn resolve_global(
&self,
_module_name: &str,
_field_name: &str,
module_name: &str,
field_name: &str,
_global_type: &::wasmi::GlobalDescriptor,
) -> Result<::wasmi::GlobalRef, ::wasmi::Error> {
// TODO:
unimplemented!()
Err(::wasmi::Error::Instantiation(format!(
"Export {}:{} not found",
module_name, field_name
)))
}

fn resolve_table(
&self,
_module_name: &str,
_field_name: &str,
module_name: &str,
field_name: &str,
_table_type: &::wasmi::TableDescriptor,
) -> Result<::wasmi::TableRef, ::wasmi::Error> {
// TODO:
unimplemented!()
Err(::wasmi::Error::Instantiation(format!(
"Export {}:{} not found",
module_name, field_name
)))
}
}

Expand Down Expand Up @@ -259,7 +265,8 @@ impl<'a, FE: SandboxCapabilities + Externals + 'a> Externals for GuestExternals<
self.supervisor_externals
.deallocate(serialized_result_val_ptr);

// TODO: check the signature?
// We do not have to check the signature here, because it's automatically
// checked by wasmi.

deserialize_result(&serialized_result_val)
}
Expand Down Expand Up @@ -610,4 +617,60 @@ mod tests {
vec![1],
);
}

#[test]
fn invoke_args() {
let mut ext = TestExternalities::default();
let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm");

let code = wabt::wat2wasm(r#"
(module
(import "env" "assert" (func $assert (param i32)))
(func (export "call") (param $x i32) (param $y i64)
;; assert that $x = 0x12345678
(call $assert
(i32.eq
(get_local $x)
(i32.const 0x12345678)
)
)
(call $assert
(i64.eq
(get_local $y)
(i64.const 0x1234567887654321)
)
)
)
)
"#).unwrap();

assert_eq!(
WasmExecutor.call(&mut ext, &test_code[..], "test_sandbox_args", &code).unwrap(),
vec![1],
);
}

#[test]
fn return_val() {
let mut ext = TestExternalities::default();
let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm");

let code = wabt::wat2wasm(r#"
(module
(func (export "call") (param $x i32) (result i32)
(i32.add
(get_local $x)
(i32.const 1)
)
)
)
"#).unwrap();

assert_eq!(
WasmExecutor.call(&mut ext, &test_code[..], "test_sandbox_return_val", &code).unwrap(),
vec![1],
);
}
}
30 changes: 26 additions & 4 deletions substrate/executor/src/wasm_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,9 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
this.sandbox_store.instance_teardown(instance_idx)?;
Ok(())
},
ext_sandbox_invoke(instance_idx: u32, export_ptr: *const u8, export_len: usize, state: usize) -> u32 => {
ext_sandbox_invoke(instance_idx: u32, export_ptr: *const u8, export_len: usize, args_ptr: *const u8, args_len: usize, return_val_ptr: *const u8, return_val_len: usize, state: usize) -> u32 => {
use codec::Slicable;

trace!(target: "runtime-sandbox", "invoke, instance_idx={}", instance_idx);
let export = this.memory.get(export_ptr, export_len as usize)
.map_err(|_| DummyUserError)
Expand All @@ -369,12 +371,32 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
.map_err(|_| DummyUserError)
)?;

// Deserialize arguments and convert them into wasmi types.
let serialized_args = this.memory.get(args_ptr, args_len as usize)
.map_err(|_| DummyUserError)?;
let args = Vec::<sandbox_primitives::TypedValue>::decode(&mut &serialized_args[..])
.ok_or_else(|| DummyUserError)?
.into_iter()
.map(Into::into)
.collect::<Vec<_>>();

let instance = this.sandbox_store.instance(instance_idx)?;
let result = instance.invoke(&export, &[], this, state);
let result = instance.invoke(&export, &args, this, state);

match result {
Ok(None) => Ok(sandbox_primitives::ERR_OK),
// TODO: Return value
Ok(_) => unimplemented!(),
Ok(Some(val)) => {
// Serialize return value and write it back into the memory.
sandbox_primitives::ReturnValue::Value(val.into()).using_encoded(|val| {
if val.len() > return_val_len as usize {
Err(DummyUserError)?;
}
this.memory
.set(return_val_ptr, val)
.map_err(|_| DummyUserError)?;
Ok(sandbox_primitives::ERR_OK)
})
}
Err(_) => Ok(sandbox_primitives::ERR_EXECUTION),
}
},
Expand Down
28 changes: 24 additions & 4 deletions substrate/executor/wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,32 @@ impl_stubs!(
enumerated_trie_root(&[&b"zero"[..], &b"one"[..], &b"two"[..]]).to_vec()
},
test_sandbox NO_DECODE => |code: &[u8]| {
let result = execute_sandboxed(code).is_ok();
[result as u8].to_vec()
let ok = execute_sandboxed(code, &[]).is_ok();
[ok as u8].to_vec()
},
test_sandbox_args NO_DECODE => |code: &[u8]| {
let ok = execute_sandboxed(
code,
&[
sandbox::TypedValue::I32(0x12345678),
sandbox::TypedValue::I64(0x1234567887654321),
]
).is_ok();
[ok as u8].to_vec()
},
test_sandbox_return_val NO_DECODE => |code: &[u8]| {
let result = execute_sandboxed(
code,
&[
sandbox::TypedValue::I32(0x1336),
]
);
let ok = if let Ok(sandbox::ReturnValue::Value(sandbox::TypedValue::I32(0x1337))) = result { true } else { false };
[ok as u8].to_vec()
}
);

fn execute_sandboxed(code: &[u8]) -> Result<sandbox::ReturnValue, sandbox::HostError> {
fn execute_sandboxed(code: &[u8], args: &[sandbox::TypedValue]) -> Result<sandbox::ReturnValue, sandbox::HostError> {
struct State {
counter: u32,
}
Expand Down Expand Up @@ -91,7 +111,7 @@ fn execute_sandboxed(code: &[u8]) -> Result<sandbox::ReturnValue, sandbox::HostE
env_builder.add_host_func("env", "inc_counter", env_inc_counter);

let mut instance = sandbox::Instance::new(code, &env_builder, &mut state)?;
let result = instance.invoke(b"call", &[], &mut state);
let result = instance.invoke(b"call", args, &mut state);

result.map_err(|_| sandbox::HostError)
}
Binary file not shown.
Binary file not shown.
17 changes: 17 additions & 0 deletions substrate/primitives/src/sandbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,23 @@ impl Slicable for ReturnValue {
}
}

impl ReturnValue {
/// Maximum number of bytes `ReturnValue` might occupy when serialized with
/// `Slicable`.
///
/// Breakdown:
/// 1 byte for encoding unit/value variant
/// 1 byte for encoding value type
/// 8 bytes for encoding the biggest value types available in wasm: f64, i64.
pub const ENCODED_MAX_SIZE: usize = 10;
}

#[test]
fn return_value_encoded_max_size() {
let encoded = ReturnValue::Value(TypedValue::I64(-1)).encode();
assert_eq!(encoded.len(), ReturnValue::ENCODED_MAX_SIZE);
}

#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "std", derive(Debug))]
#[repr(i8)]
Expand Down
10 changes: 5 additions & 5 deletions substrate/runtime-io/without_std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ pub use rstd::{mem, slice};

#[panic_implementation]
#[no_mangle]
pub fn panic(_info: &core::panic::PanicInfo) -> ! {
pub fn panic(info: &::core::panic::PanicInfo) -> ! {
unsafe {
if let Some(location) = _info.location() {
ext_print_utf8(location.file().as_ptr() as *const u8, location.file().len() as u32);
ext_print_num(location.line() as u64);
ext_print_num(location.column() as u64);
if let Some(loc) = info.location() {
ext_print_utf8(loc.file().as_ptr() as *const u8, loc.file().len() as u32);
ext_print_num(loc.line() as u64);
ext_print_num(loc.column() as u64);
}
intrinsics::abort()
}
Expand Down
3 changes: 3 additions & 0 deletions substrate/runtime-sandbox/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ substrate-runtime-std = { path = "../runtime-std", default_features = false }
substrate-runtime-io = { path = "../runtime-io", default_features = false }
substrate-codec = { path = "../codec", default_features = false }

[dev-dependencies]
wabt = "0.1.7"

[features]
default = ["std"]
std = [
Expand Down
4 changes: 4 additions & 0 deletions substrate/runtime-sandbox/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,13 @@

extern crate substrate_codec as codec;
extern crate substrate_runtime_io as runtime_io;
#[cfg_attr(not(feature = "std"), macro_use)]
extern crate substrate_runtime_std as rstd;
extern crate substrate_primitives as primitives;

#[cfg(test)]
extern crate wabt;

use rstd::prelude::*;

pub use primitives::sandbox::{TypedValue, ReturnValue, HostError};
Expand Down
Loading

0 comments on commit fea8f05

Please sign in to comment.