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

Argument passing and returning values when invoking sandboxed funcs #189

Merged
merged 6 commits into from
Jun 19, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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