Improve Wasm extern errors and increase heap (#299)

* Improve Wasm extern errors

* Increase heap size

* Update heap size further
This commit is contained in:
Gav Wood
2018-07-11 22:40:30 +02:00
committed by GitHub
parent 72b5cd858c
commit c8fe9e86bd
11 changed files with 85 additions and 83 deletions
+24 -24
View File
@@ -22,7 +22,7 @@ use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
use codec::Slicable; use codec::Slicable;
use primitives::sandbox as sandbox_primitives; use primitives::sandbox as sandbox_primitives;
use wasm_utils::DummyUserError; use wasm_utils::UserError;
use wasmi; use wasmi;
use wasmi::memory_units::Pages; use wasmi::memory_units::Pages;
use wasmi::{ use wasmi::{
@@ -161,14 +161,14 @@ pub trait SandboxCapabilities {
/// # Errors /// # Errors
/// ///
/// Returns `Err` if `ptr + data.len()` is out of bounds. /// Returns `Err` if `ptr + data.len()` is out of bounds.
fn write_memory(&mut self, ptr: u32, data: &[u8]) -> Result<(), DummyUserError>; fn write_memory(&mut self, ptr: u32, data: &[u8]) -> Result<(), UserError>;
/// Read `len` bytes from the supervisor memory. /// Read `len` bytes from the supervisor memory.
/// ///
/// # Errors /// # Errors
/// ///
/// Returns `Err` if `ptr + len` is out of bounds. /// Returns `Err` if `ptr + len` is out of bounds.
fn read_memory(&self, ptr: u32, len: u32) -> Result<Vec<u8>, DummyUserError>; fn read_memory(&self, ptr: u32, len: u32) -> Result<Vec<u8>, UserError>;
} }
/// Implementation of [`Externals`] that allows execution of guest module with /// Implementation of [`Externals`] that allows execution of guest module with
@@ -182,7 +182,7 @@ pub struct GuestExternals<'a, FE: SandboxCapabilities + Externals + 'a> {
} }
fn trap() -> Trap { fn trap() -> Trap {
TrapKind::Host(Box::new(DummyUserError)).into() TrapKind::Host(Box::new(UserError("Sandbox error"))).into()
} }
fn deserialize_result(serialized_result: &[u8]) -> Result<Option<RuntimeValue>, Trap> { fn deserialize_result(serialized_result: &[u8]) -> Result<Option<RuntimeValue>, Trap> {
@@ -338,8 +338,8 @@ impl SandboxInstance {
fn decode_environment_definition( fn decode_environment_definition(
raw_env_def: &[u8], raw_env_def: &[u8],
memories: &[Option<MemoryRef>], memories: &[Option<MemoryRef>],
) -> Result<(Imports, GuestToSupervisorFunctionMapping), DummyUserError> { ) -> Result<(Imports, GuestToSupervisorFunctionMapping), UserError> {
let env_def = sandbox_primitives::EnvironmentDefinition::decode(&mut &raw_env_def[..]).ok_or_else(|| DummyUserError)?; let env_def = sandbox_primitives::EnvironmentDefinition::decode(&mut &raw_env_def[..]).ok_or_else(|| UserError("Sandbox error"))?;
let mut func_map = HashMap::new(); let mut func_map = HashMap::new();
let mut memories_map = HashMap::new(); let mut memories_map = HashMap::new();
@@ -359,8 +359,8 @@ fn decode_environment_definition(
let memory_ref = memories let memory_ref = memories
.get(memory_idx as usize) .get(memory_idx as usize)
.cloned() .cloned()
.ok_or_else(|| DummyUserError)? .ok_or_else(|| UserError("Sandbox error"))?
.ok_or_else(|| DummyUserError)?; .ok_or_else(|| UserError("Sandbox error"))?;
memories_map.insert((module, field), memory_ref); memories_map.insert((module, field), memory_ref);
} }
} }
@@ -395,12 +395,12 @@ pub fn instantiate<FE: SandboxCapabilities + Externals>(
wasm: &[u8], wasm: &[u8],
raw_env_def: &[u8], raw_env_def: &[u8],
state: u32, state: u32,
) -> Result<u32, DummyUserError> { ) -> Result<u32, UserError> {
let (imports, guest_to_supervisor_mapping) = let (imports, guest_to_supervisor_mapping) =
decode_environment_definition(raw_env_def, &supervisor_externals.store().memories)?; decode_environment_definition(raw_env_def, &supervisor_externals.store().memories)?;
let module = Module::from_buffer(wasm).map_err(|_| DummyUserError)?; let module = Module::from_buffer(wasm).map_err(|_| UserError("Sandbox error"))?;
let instance = ModuleInstance::new(&module, &imports).map_err(|_| DummyUserError)?; let instance = ModuleInstance::new(&module, &imports).map_err(|_| UserError("Sandbox error"))?;
let sandbox_instance = Rc::new(SandboxInstance { let sandbox_instance = Rc::new(SandboxInstance {
// In general, it's not a very good idea to use `.not_started_instance()` for anything // In general, it's not a very good idea to use `.not_started_instance()` for anything
@@ -418,7 +418,7 @@ pub fn instantiate<FE: SandboxCapabilities + Externals>(
|guest_externals| { |guest_externals| {
instance instance
.run_start(guest_externals) .run_start(guest_externals)
.map_err(|_| DummyUserError) .map_err(|_| UserError("Sandbox error"))
}, },
)?; )?;
@@ -451,14 +451,14 @@ impl Store {
/// ///
/// Returns `Err` if the memory couldn't be created. /// Returns `Err` if the memory couldn't be created.
/// Typically happens if `initial` is more than `maximum`. /// Typically happens if `initial` is more than `maximum`.
pub fn new_memory(&mut self, initial: u32, maximum: u32) -> Result<u32, DummyUserError> { pub fn new_memory(&mut self, initial: u32, maximum: u32) -> Result<u32, UserError> {
let maximum = match maximum { let maximum = match maximum {
sandbox_primitives::MEM_UNLIMITED => None, sandbox_primitives::MEM_UNLIMITED => None,
specified_limit => Some(Pages(specified_limit as usize)), specified_limit => Some(Pages(specified_limit as usize)),
}; };
let mem = let mem =
MemoryInstance::alloc(Pages(initial as usize), maximum).map_err(|_| DummyUserError)?; MemoryInstance::alloc(Pages(initial as usize), maximum).map_err(|_| UserError("Sandbox error"))?;
let mem_idx = self.memories.len(); let mem_idx = self.memories.len();
self.memories.push(Some(mem)); self.memories.push(Some(mem));
Ok(mem_idx as u32) Ok(mem_idx as u32)
@@ -470,12 +470,12 @@ impl Store {
/// ///
/// Returns `Err` If `instance_idx` isn't a valid index of an instance or /// Returns `Err` If `instance_idx` isn't a valid index of an instance or
/// instance is already torndown. /// instance is already torndown.
pub fn instance(&self, instance_idx: u32) -> Result<Rc<SandboxInstance>, DummyUserError> { pub fn instance(&self, instance_idx: u32) -> Result<Rc<SandboxInstance>, UserError> {
self.instances self.instances
.get(instance_idx as usize) .get(instance_idx as usize)
.cloned() .cloned()
.ok_or_else(|| DummyUserError)? .ok_or_else(|| UserError("Sandbox error"))?
.ok_or_else(|| DummyUserError) .ok_or_else(|| UserError("Sandbox error"))
} }
/// Returns reference to a memory instance by `memory_idx`. /// Returns reference to a memory instance by `memory_idx`.
@@ -484,12 +484,12 @@ impl Store {
/// ///
/// Returns `Err` If `memory_idx` isn't a valid index of an memory or /// Returns `Err` If `memory_idx` isn't a valid index of an memory or
/// memory is already torndown. /// memory is already torndown.
pub fn memory(&self, memory_idx: u32) -> Result<MemoryRef, DummyUserError> { pub fn memory(&self, memory_idx: u32) -> Result<MemoryRef, UserError> {
self.memories self.memories
.get(memory_idx as usize) .get(memory_idx as usize)
.cloned() .cloned()
.ok_or_else(|| DummyUserError)? .ok_or_else(|| UserError("Sandbox error"))?
.ok_or_else(|| DummyUserError) .ok_or_else(|| UserError("Sandbox error"))
} }
/// Teardown the memory at the specified index. /// Teardown the memory at the specified index.
@@ -497,18 +497,18 @@ impl Store {
/// # Errors /// # Errors
/// ///
/// Returns `Err` if `memory_idx` isn't a valid index of an memory. /// Returns `Err` if `memory_idx` isn't a valid index of an memory.
pub fn memory_teardown(&mut self, memory_idx: u32) -> Result<(), DummyUserError> { pub fn memory_teardown(&mut self, memory_idx: u32) -> Result<(), UserError> {
if memory_idx as usize >= self.memories.len() { if memory_idx as usize >= self.memories.len() {
return Err(DummyUserError); return Err(UserError("Sandbox error"));
} }
self.memories[memory_idx as usize] = None; self.memories[memory_idx as usize] = None;
Ok(()) Ok(())
} }
/// Teardown the instance at the specified index. /// Teardown the instance at the specified index.
pub fn instance_teardown(&mut self, instance_idx: u32) -> Result<(), DummyUserError> { pub fn instance_teardown(&mut self, instance_idx: u32) -> Result<(), UserError> {
if instance_idx as usize >= self.instances.len() { if instance_idx as usize >= self.instances.len() {
return Err(DummyUserError); return Err(UserError("Sandbox error"));
} }
self.instances[instance_idx as usize] = None; self.instances[instance_idx as usize] = None;
Ok(()) Ok(())
@@ -25,7 +25,7 @@ use wasmi::RuntimeValue::{I32, I64};
use wasmi::memory_units::{Pages, Bytes}; use wasmi::memory_units::{Pages, Bytes};
use state_machine::{Externalities, CodeExecutor}; use state_machine::{Externalities, CodeExecutor};
use error::{Error, ErrorKind, Result}; use error::{Error, ErrorKind, Result};
use wasm_utils::{DummyUserError}; use wasm_utils::UserError;
use primitives::{blake2_256, twox_128, twox_256}; use primitives::{blake2_256, twox_128, twox_256};
use primitives::hexdisplay::HexDisplay; use primitives::hexdisplay::HexDisplay;
use primitives::sandbox as sandbox_primitives; use primitives::sandbox as sandbox_primitives;
@@ -45,7 +45,7 @@ impl Heap {
/// This could mean that wasm binary specifies memory /// This could mean that wasm binary specifies memory
/// limit and we are trying to allocate beyond that limit. /// limit and we are trying to allocate beyond that limit.
fn new(memory: &MemoryRef) -> Result<Self> { fn new(memory: &MemoryRef) -> Result<Self> {
const HEAP_SIZE_IN_PAGES: usize = 8; const HEAP_SIZE_IN_PAGES: usize = 1024;
let prev_page_count = memory let prev_page_count = memory
.grow(Pages(HEAP_SIZE_IN_PAGES)) .grow(Pages(HEAP_SIZE_IN_PAGES))
@@ -98,35 +98,35 @@ impl<'e, E: Externalities> sandbox::SandboxCapabilities for FunctionExecutor<'e,
fn deallocate(&mut self, ptr: u32) { fn deallocate(&mut self, ptr: u32) {
self.heap.deallocate(ptr) self.heap.deallocate(ptr)
} }
fn write_memory(&mut self, ptr: u32, data: &[u8]) -> ::std::result::Result<(), DummyUserError> { fn write_memory(&mut self, ptr: u32, data: &[u8]) -> ::std::result::Result<(), UserError> {
self.memory.set(ptr, data).map_err(|_| DummyUserError) self.memory.set(ptr, data).map_err(|_| UserError("Invalid attempt to write_memory"))
} }
fn read_memory(&self, ptr: u32, len: u32) -> ::std::result::Result<Vec<u8>, DummyUserError> { fn read_memory(&self, ptr: u32, len: u32) -> ::std::result::Result<Vec<u8>, UserError> {
self.memory.get(ptr, len as usize).map_err(|_| DummyUserError) self.memory.get(ptr, len as usize).map_err(|_| UserError("Invalid attempt to write_memory"))
} }
} }
trait WritePrimitive<T: Sized> { trait WritePrimitive<T: Sized> {
fn write_primitive(&self, offset: u32, t: T) -> ::std::result::Result<(), DummyUserError>; fn write_primitive(&self, offset: u32, t: T) -> ::std::result::Result<(), UserError>;
} }
impl WritePrimitive<u32> for MemoryInstance { impl WritePrimitive<u32> for MemoryInstance {
fn write_primitive(&self, offset: u32, t: u32) -> ::std::result::Result<(), DummyUserError> { fn write_primitive(&self, offset: u32, t: u32) -> ::std::result::Result<(), UserError> {
use byteorder::{LittleEndian, ByteOrder}; use byteorder::{LittleEndian, ByteOrder};
let mut r = [0u8; 4]; let mut r = [0u8; 4];
LittleEndian::write_u32(&mut r, t); LittleEndian::write_u32(&mut r, t);
self.set(offset, &r).map_err(|_| DummyUserError) self.set(offset, &r).map_err(|_| UserError("Invalid attempt to write_primitive"))
} }
} }
trait ReadPrimitive<T: Sized> { trait ReadPrimitive<T: Sized> {
fn read_primitive(&self, offset: u32) -> ::std::result::Result<T, DummyUserError>; fn read_primitive(&self, offset: u32) -> ::std::result::Result<T, UserError>;
} }
impl ReadPrimitive<u32> for MemoryInstance { impl ReadPrimitive<u32> for MemoryInstance {
fn read_primitive(&self, offset: u32) -> ::std::result::Result<u32, DummyUserError> { fn read_primitive(&self, offset: u32) -> ::std::result::Result<u32, UserError> {
use byteorder::{LittleEndian, ByteOrder}; use byteorder::{LittleEndian, ByteOrder};
Ok(LittleEndian::read_u32(&self.get(offset, 4).map_err(|_| DummyUserError)?)) Ok(LittleEndian::read_u32(&self.get(offset, 4).map_err(|_| UserError("Invalid attempt to read_primitive"))?))
} }
} }
@@ -168,8 +168,8 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
Ok(()) Ok(())
}, },
ext_memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32 => { ext_memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32 => {
let sl1 = this.memory.get(s1, n as usize).map_err(|_| DummyUserError)?; let sl1 = this.memory.get(s1, n as usize).map_err(|_| UserError("Invalid attempt to read from memory in first arg of ext_memcmp"))?;
let sl2 = this.memory.get(s2, n as usize).map_err(|_| DummyUserError)?; let sl2 = this.memory.get(s2, n as usize).map_err(|_| UserError("Invalid attempt to read from memory in second arg of ext_memcmp"))?;
Ok(match sl1.cmp(&sl2) { Ok(match sl1.cmp(&sl2) {
Ordering::Greater => 1, Ordering::Greater => 1,
Ordering::Less => -1, Ordering::Less => -1,
@@ -178,20 +178,20 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
}, },
ext_memcpy(dest: *mut u8, src: *const u8, count: usize) -> *mut u8 => { ext_memcpy(dest: *mut u8, src: *const u8, count: usize) -> *mut u8 => {
this.memory.copy_nonoverlapping(src as usize, dest as usize, count as usize) this.memory.copy_nonoverlapping(src as usize, dest as usize, count as usize)
.map_err(|_| DummyUserError)?; .map_err(|_| UserError("Invalid attempt to copy_nonoverlapping in ext_memcpy"))?;
trace!(target: "runtime-io", "memcpy {} from {}, {} bytes", dest, src, count); trace!(target: "runtime-io", "memcpy {} from {}, {} bytes", dest, src, count);
Ok(dest) Ok(dest)
}, },
ext_memmove(dest: *mut u8, src: *const u8, count: usize) -> *mut u8 => { ext_memmove(dest: *mut u8, src: *const u8, count: usize) -> *mut u8 => {
this.memory.copy(src as usize, dest as usize, count as usize) this.memory.copy(src as usize, dest as usize, count as usize)
.map_err(|_| DummyUserError)?; .map_err(|_| UserError("Invalid attempt to copy in ext_memmove"))?;
trace!(target: "runtime-io", "memmove {} from {}, {} bytes", dest, src, count); trace!(target: "runtime-io", "memmove {} from {}, {} bytes", dest, src, count);
Ok(dest) Ok(dest)
}, },
ext_memset(dest: *mut u8, val: u32, count: usize) -> *mut u8 => { ext_memset(dest: *mut u8, val: u32, count: usize) -> *mut u8 => {
this.memory.clear(dest as usize, val as u8, count as usize)
.map_err(|_| DummyUserError)?;
trace!(target: "runtime-io", "memset {} with {}, {} bytes", dest, val, count); trace!(target: "runtime-io", "memset {} with {}, {} bytes", dest, val, count);
this.memory.clear(dest as usize, val as u8, count as usize)
.map_err(|_| UserError("Invalid attempt to clear in ext_memset"))?;
Ok(dest) Ok(dest)
}, },
ext_malloc(size: usize) -> *mut u8 => { ext_malloc(size: usize) -> *mut u8 => {
@@ -205,8 +205,8 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
Ok(()) Ok(())
}, },
ext_set_storage(key_data: *const u8, key_len: u32, value_data: *const u8, value_len: u32) => { ext_set_storage(key_data: *const u8, key_len: u32, value_data: *const u8, value_len: u32) => {
let key = this.memory.get(key_data, key_len as usize).map_err(|_| DummyUserError)?; let key = this.memory.get(key_data, key_len as usize).map_err(|_| UserError("Invalid attempt to determine key in ext_set_storage"))?;
let value = this.memory.get(value_data, value_len as usize).map_err(|_| DummyUserError)?; let value = this.memory.get(value_data, value_len as usize).map_err(|_| UserError("Invalid attempt to determine value in ext_set_storage"))?;
if let Some(preimage) = this.hash_lookup.get(&key) { if let Some(preimage) = this.hash_lookup.get(&key) {
trace!(target: "wasm-trace", "*** Setting storage: %{} -> {} [k={}]", ascii_format(&preimage), HexDisplay::from(&value), HexDisplay::from(&key)); trace!(target: "wasm-trace", "*** Setting storage: %{} -> {} [k={}]", ascii_format(&preimage), HexDisplay::from(&value), HexDisplay::from(&key));
} else { } else {
@@ -216,7 +216,7 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
Ok(()) Ok(())
}, },
ext_clear_storage(key_data: *const u8, key_len: u32) => { ext_clear_storage(key_data: *const u8, key_len: u32) => {
let key = this.memory.get(key_data, key_len as usize).map_err(|_| DummyUserError)?; let key = this.memory.get(key_data, key_len as usize).map_err(|_| UserError("Invalid attempt to determine key in ext_clear_storage"))?;
if let Some(preimage) = this.hash_lookup.get(&key) { if let Some(preimage) = this.hash_lookup.get(&key) {
trace!(target: "wasm-trace", "*** Clearing storage: %{} [k={}]", ascii_format(&preimage), HexDisplay::from(&key)); trace!(target: "wasm-trace", "*** Clearing storage: %{} [k={}]", ascii_format(&preimage), HexDisplay::from(&key));
} else { } else {
@@ -226,13 +226,13 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
Ok(()) Ok(())
}, },
ext_clear_prefix(prefix_data: *const u8, prefix_len: u32) => { ext_clear_prefix(prefix_data: *const u8, prefix_len: u32) => {
let prefix = this.memory.get(prefix_data, prefix_len as usize).map_err(|_| DummyUserError)?; let prefix = this.memory.get(prefix_data, prefix_len as usize).map_err(|_| UserError("Invalid attempt to determine prefix in ext_clear_prefix"))?;
this.ext.clear_prefix(&prefix); this.ext.clear_prefix(&prefix);
Ok(()) Ok(())
}, },
// return 0 and place u32::max_value() into written_out if no value exists for the key. // return 0 and place u32::max_value() into written_out if no value exists for the key.
ext_get_allocated_storage(key_data: *const u8, key_len: u32, written_out: *mut u32) -> *mut u8 => { ext_get_allocated_storage(key_data: *const u8, key_len: u32, written_out: *mut u32) -> *mut u8 => {
let key = this.memory.get(key_data, key_len as usize).map_err(|_| DummyUserError)?; let key = this.memory.get(key_data, key_len as usize).map_err(|_| UserError("Invalid attempt to determine key in ext_get_allocated_storage"))?;
let maybe_value = this.ext.storage(&key); let maybe_value = this.ext.storage(&key);
if let Some(preimage) = this.hash_lookup.get(&key) { if let Some(preimage) = this.hash_lookup.get(&key) {
@@ -243,17 +243,19 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
if let Some(value) = maybe_value { if let Some(value) = maybe_value {
let offset = this.heap.allocate(value.len() as u32) as u32; let offset = this.heap.allocate(value.len() as u32) as u32;
this.memory.set(offset, &value).map_err(|_| DummyUserError)?; this.memory.set(offset, &value).map_err(|_| UserError("Invalid attempt to set memory in ext_get_allocated_storage"))?;
this.memory.write_primitive(written_out, value.len() as u32)?; this.memory.write_primitive(written_out, value.len() as u32)
.map_err(|_| UserError("Invalid attempt to write written_out in ext_get_allocated_storage"))?;
Ok(offset) Ok(offset)
} else { } else {
this.memory.write_primitive(written_out, u32::max_value())?; this.memory.write_primitive(written_out, u32::max_value())
.map_err(|_| UserError("Invalid attempt to write failed written_out in ext_get_allocated_storage"))?;
Ok(0) Ok(0)
} }
}, },
// return u32::max_value() if no value exists for the key. // return u32::max_value() if no value exists for the key.
ext_get_storage_into(key_data: *const u8, key_len: u32, value_data: *mut u8, value_len: u32, value_offset: u32) -> u32 => { ext_get_storage_into(key_data: *const u8, key_len: u32, value_data: *mut u8, value_len: u32, value_offset: u32) -> u32 => {
let key = this.memory.get(key_data, key_len as usize).map_err(|_| DummyUserError)?; let key = this.memory.get(key_data, key_len as usize).map_err(|_| UserError("Invalid attempt to get key in ext_get_storage_into"))?;
let maybe_value = this.ext.storage(&key); let maybe_value = this.ext.storage(&key);
if let Some(preimage) = this.hash_lookup.get(&key) { if let Some(preimage) = this.hash_lookup.get(&key) {
trace!(target: "wasm-trace", " Getting storage: %{} == {} [k={}]", ascii_format(&preimage), if let Some(ref b) = maybe_value { format!("{}", HexDisplay::from(b)) } else { "<empty>".to_owned() }, HexDisplay::from(&key)); trace!(target: "wasm-trace", " Getting storage: %{} == {} [k={}]", ascii_format(&preimage), if let Some(ref b) = maybe_value { format!("{}", HexDisplay::from(b)) } else { "<empty>".to_owned() }, HexDisplay::from(&key));
@@ -263,7 +265,7 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
if let Some(value) = maybe_value { if let Some(value) = maybe_value {
let value = &value[value_offset as usize..]; let value = &value[value_offset as usize..];
let written = ::std::cmp::min(value_len as usize, value.len()); let written = ::std::cmp::min(value_len as usize, value.len());
this.memory.set(value_data, &value[..written]).map_err(|_| DummyUserError)?; this.memory.set(value_data, &value[..written]).map_err(|_| UserError("Invalid attempt to set value in ext_get_storage_into"))?;
Ok(written as u32) Ok(written as u32)
} else { } else {
Ok(u32::max_value()) Ok(u32::max_value())
@@ -271,22 +273,22 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
}, },
ext_storage_root(result: *mut u8) => { ext_storage_root(result: *mut u8) => {
let r = this.ext.storage_root(); let r = this.ext.storage_root();
this.memory.set(result, &r[..]).map_err(|_| DummyUserError)?; this.memory.set(result, &r[..]).map_err(|_| UserError("Invalid attempt to set memory in ext_storage_root"))?;
Ok(()) Ok(())
}, },
ext_enumerated_trie_root(values_data: *const u8, lens_data: *const u32, lens_len: u32, result: *mut u8) => { ext_enumerated_trie_root(values_data: *const u8, lens_data: *const u32, lens_len: u32, result: *mut u8) => {
let values = (0..lens_len) let values = (0..lens_len)
.map(|i| this.memory.read_primitive(lens_data + i * 4)) .map(|i| this.memory.read_primitive(lens_data + i * 4))
.collect::<::std::result::Result<Vec<u32>, DummyUserError>>()? .collect::<::std::result::Result<Vec<u32>, UserError>>()?
.into_iter() .into_iter()
.scan(0u32, |acc, v| { let o = *acc; *acc += v; Some((o, v)) }) .scan(0u32, |acc, v| { let o = *acc; *acc += v; Some((o, v)) })
.map(|(offset, len)| .map(|(offset, len)|
this.memory.get(values_data + offset, len as usize) this.memory.get(values_data + offset, len as usize)
.map_err(|_| DummyUserError) .map_err(|_| UserError("Invalid attempt to get memory in ext_enumerated_trie_root"))
) )
.collect::<::std::result::Result<Vec<_>, DummyUserError>>()?; .collect::<::std::result::Result<Vec<_>, UserError>>()?;
let r = ordered_trie_root(values.into_iter()); let r = ordered_trie_root(values.into_iter());
this.memory.set(result, &r[..]).map_err(|_| DummyUserError)?; this.memory.set(result, &r[..]).map_err(|_| UserError("Invalid attempt to set memory in ext_enumerated_trie_root"))?;
Ok(()) Ok(())
}, },
ext_chain_id() -> u64 => { ext_chain_id() -> u64 => {
@@ -299,7 +301,7 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
this.hash_lookup.insert(hashed.to_vec(), vec![]); this.hash_lookup.insert(hashed.to_vec(), vec![]);
hashed hashed
} else { } else {
let key = this.memory.get(data, len as usize).map_err(|_| DummyUserError)?; let key = this.memory.get(data, len as usize).map_err(|_| UserError("Invalid attempt to get key in ext_twox_128"))?;
let hashed_key = twox_128(&key); let hashed_key = twox_128(&key);
if let Ok(skey) = ::std::str::from_utf8(&key) { if let Ok(skey) = ::std::str::from_utf8(&key) {
trace!(target: "xxhash", "XXhash: {} -> {}", skey, HexDisplay::from(&hashed_key)); trace!(target: "xxhash", "XXhash: {} -> {}", skey, HexDisplay::from(&hashed_key));
@@ -310,33 +312,33 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
hashed_key hashed_key
}; };
this.memory.set(out, &result).map_err(|_| DummyUserError)?; this.memory.set(out, &result).map_err(|_| UserError("Invalid attempt to set result in ext_twox_128"))?;
Ok(()) Ok(())
}, },
ext_twox_256(data: *const u8, len: u32, out: *mut u8) => { ext_twox_256(data: *const u8, len: u32, out: *mut u8) => {
let result = if len == 0 { let result = if len == 0 {
twox_256(&[0u8; 0]) twox_256(&[0u8; 0])
} else { } else {
twox_256(&this.memory.get(data, len as usize).map_err(|_| DummyUserError)?) twox_256(&this.memory.get(data, len as usize).map_err(|_| UserError("Invalid attempt to get data in ext_twox_256"))?)
}; };
this.memory.set(out, &result).map_err(|_| DummyUserError)?; this.memory.set(out, &result).map_err(|_| UserError("Invalid attempt to set result in ext_twox_256"))?;
Ok(()) Ok(())
}, },
ext_blake2_256(data: *const u8, len: u32, out: *mut u8) => { ext_blake2_256(data: *const u8, len: u32, out: *mut u8) => {
let result = if len == 0 { let result = if len == 0 {
blake2_256(&[0u8; 0]) blake2_256(&[0u8; 0])
} else { } else {
blake2_256(&this.memory.get(data, len as usize).map_err(|_| DummyUserError)?) blake2_256(&this.memory.get(data, len as usize).map_err(|_| UserError("Invalid attempt to get data in ext_blake2_256"))?)
}; };
this.memory.set(out, &result).map_err(|_| DummyUserError)?; this.memory.set(out, &result).map_err(|_| UserError("Invalid attempt to set result in ext_blake2_256"))?;
Ok(()) Ok(())
}, },
ext_ed25519_verify(msg_data: *const u8, msg_len: u32, sig_data: *const u8, pubkey_data: *const u8) -> u32 => { ext_ed25519_verify(msg_data: *const u8, msg_len: u32, sig_data: *const u8, pubkey_data: *const u8) -> u32 => {
let mut sig = [0u8; 64]; let mut sig = [0u8; 64];
this.memory.get_into(sig_data, &mut sig[..]).map_err(|_| DummyUserError)?; this.memory.get_into(sig_data, &mut sig[..]).map_err(|_| UserError("Invalid attempt to get signature in ext_ed25519_verify"))?;
let mut pubkey = [0u8; 32]; let mut pubkey = [0u8; 32];
this.memory.get_into(pubkey_data, &mut pubkey[..]).map_err(|_| DummyUserError)?; this.memory.get_into(pubkey_data, &mut pubkey[..]).map_err(|_| UserError("Invalid attempt to get pubkey in ext_ed25519_verify"))?;
let msg = this.memory.get(msg_data, msg_len as usize).map_err(|_| DummyUserError)?; let msg = this.memory.get(msg_data, msg_len as usize).map_err(|_| UserError("Invalid attempt to get message in ext_ed25519_verify"))?;
Ok(if ::ed25519::verify(&sig, &msg, &pubkey) { Ok(if ::ed25519::verify(&sig, &msg, &pubkey) {
0 0
@@ -345,15 +347,15 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
}) })
}, },
ext_sandbox_instantiate(dispatch_thunk_idx: usize, wasm_ptr: *const u8, wasm_len: usize, imports_ptr: *const u8, imports_len: usize, state: usize) -> u32 => { ext_sandbox_instantiate(dispatch_thunk_idx: usize, wasm_ptr: *const u8, wasm_len: usize, imports_ptr: *const u8, imports_len: usize, state: usize) -> u32 => {
let wasm = this.memory.get(wasm_ptr, wasm_len as usize).map_err(|_| DummyUserError)?; let wasm = this.memory.get(wasm_ptr, wasm_len as usize).map_err(|_| UserError("Sandbox error"))?;
let raw_env_def = this.memory.get(imports_ptr, imports_len as usize).map_err(|_| DummyUserError)?; let raw_env_def = this.memory.get(imports_ptr, imports_len as usize).map_err(|_| UserError("Sandbox error"))?;
// Extract a dispatch thunk from instance's table by the specified index. // Extract a dispatch thunk from instance's table by the specified index.
let dispatch_thunk = { let dispatch_thunk = {
let table = this.table.as_ref().ok_or_else(|| DummyUserError)?; let table = this.table.as_ref().ok_or_else(|| UserError("Sandbox error"))?;
table.get(dispatch_thunk_idx) table.get(dispatch_thunk_idx)
.map_err(|_| DummyUserError)? .map_err(|_| UserError("Sandbox error"))?
.ok_or_else(|| DummyUserError)? .ok_or_else(|| UserError("Sandbox error"))?
.clone() .clone()
}; };
@@ -368,10 +370,10 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
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, state: usize) -> u32 => {
trace!(target: "runtime-sandbox", "invoke, instance_idx={}", instance_idx); trace!(target: "runtime-sandbox", "invoke, instance_idx={}", instance_idx);
let export = this.memory.get(export_ptr, export_len as usize) let export = this.memory.get(export_ptr, export_len as usize)
.map_err(|_| DummyUserError) .map_err(|_| UserError("Sandbox error"))
.and_then(|b| .and_then(|b|
String::from_utf8(b) String::from_utf8(b)
.map_err(|_| DummyUserError) .map_err(|_| UserError("Sandbox error"))
)?; )?;
let instance = this.sandbox_store.instance(instance_idx)?; let instance = this.sandbox_store.instance(instance_idx)?;
@@ -388,17 +390,17 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
trace!(target: "runtime-sandbox", "invoke, instance_idx={}", instance_idx); trace!(target: "runtime-sandbox", "invoke, instance_idx={}", instance_idx);
let export = this.memory.get(export_ptr, export_len as usize) let export = this.memory.get(export_ptr, export_len as usize)
.map_err(|_| DummyUserError) .map_err(|_| UserError("Sandbox error"))
.and_then(|b| .and_then(|b|
String::from_utf8(b) String::from_utf8(b)
.map_err(|_| DummyUserError) .map_err(|_| UserError("Sandbox error"))
)?; )?;
// Deserialize arguments and convert them into wasmi types. // Deserialize arguments and convert them into wasmi types.
let serialized_args = this.memory.get(args_ptr, args_len as usize) let serialized_args = this.memory.get(args_ptr, args_len as usize)
.map_err(|_| DummyUserError)?; .map_err(|_| UserError("Sandbox error"))?;
let args = Vec::<sandbox_primitives::TypedValue>::decode(&mut &serialized_args[..]) let args = Vec::<sandbox_primitives::TypedValue>::decode(&mut &serialized_args[..])
.ok_or_else(|| DummyUserError)? .ok_or_else(|| UserError("Sandbox error"))?
.into_iter() .into_iter()
.map(Into::into) .map(Into::into)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@@ -412,11 +414,11 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
// Serialize return value and write it back into the memory. // Serialize return value and write it back into the memory.
sandbox_primitives::ReturnValue::Value(val.into()).using_encoded(|val| { sandbox_primitives::ReturnValue::Value(val.into()).using_encoded(|val| {
if val.len() > return_val_len as usize { if val.len() > return_val_len as usize {
Err(DummyUserError)?; Err(UserError("Sandbox error"))?;
} }
this.memory this.memory
.set(return_val_ptr, val) .set(return_val_ptr, val)
.map_err(|_| DummyUserError)?; .map_err(|_| UserError("Sandbox error"))?;
Ok(sandbox_primitives::ERR_OK) Ok(sandbox_primitives::ERR_OK)
}) })
} }
@@ -21,13 +21,13 @@ use wasmi::nan_preserving_float::{F32, F64};
use std::fmt; use std::fmt;
#[derive(Debug)] #[derive(Debug)]
pub struct DummyUserError; pub struct UserError(pub &'static str);
impl fmt::Display for DummyUserError { impl fmt::Display for UserError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "DummyUserError") write!(f, "UserError: {}", self.0)
} }
} }
impl HostError for DummyUserError { impl HostError for UserError {
} }
pub trait ConvertibleToWasm { const VALUE_TYPE: ValueType; type NativeType; fn to_runtime_value(self) -> RuntimeValue; } pub trait ConvertibleToWasm { const VALUE_TYPE: ValueType; type NativeType; fn to_runtime_value(self) -> RuntimeValue; }