WASM libraries bump (#7970)

* update wasmi, parity-wasm, wasm-utils to latest version

* Update to new wasmi & error handling

* also utilize new stack limiter

* fix typo

* replace dependency url

* Cargo.lock update
This commit is contained in:
Nikolay Volf 2018-03-12 14:37:32 +03:00 committed by André Silva
parent 3f33370e7d
commit e0a21e5aae
7 changed files with 164 additions and 104 deletions

25
Cargo.lock generated
View File

@ -1553,6 +1553,11 @@ dependencies = [
"lru-cache 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "lru-cache 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "memory_units"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "memorydb" name = "memorydb"
version = "0.1.1" version = "0.1.1"
@ -2292,7 +2297,7 @@ dependencies = [
[[package]] [[package]]
name = "parity-wasm" name = "parity-wasm"
version = "0.23.0" version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -3500,16 +3505,16 @@ dependencies = [
"ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-wasm 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-wasm 0.27.2 (registry+https://github.com/rust-lang/crates.io-index)",
"vm 0.1.0", "vm 0.1.0",
"wasm-utils 0.1.0 (git+https://github.com/paritytech/wasm-utils)", "wasm-utils 0.1.0 (git+https://github.com/paritytech/wasm-utils)",
"wasmi 0.0.0 (git+https://github.com/pepyakin/wasmi)", "wasmi 0.0.0 (git+https://github.com/paritytech/wasmi)",
] ]
[[package]] [[package]]
name = "wasm-utils" name = "wasm-utils"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/paritytech/wasm-utils#6fdc1c4ed47a6acb0a4774da505a416dd637bc6d" source = "git+https://github.com/paritytech/wasm-utils#9527b969a4928f6293369380defc7982493a678c"
dependencies = [ dependencies = [
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.29.1 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.29.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -3517,16 +3522,17 @@ dependencies = [
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-wasm 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-wasm 0.27.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "wasmi" name = "wasmi"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/pepyakin/wasmi#551c99273042deaad869c17798060e2212deacab" source = "git+https://github.com/paritytech/wasmi#7c88c6ad651ec6a6aa00b08e61c70853161a0a99"
dependencies = [ dependencies = [
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-wasm 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)", "memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-wasm 0.27.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -3728,6 +3734,7 @@ dependencies = [
"checksum matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "100aabe6b8ff4e4a7e32c1c13523379802df0772b82466207ac25b013f193376" "checksum matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "100aabe6b8ff4e4a7e32c1c13523379802df0772b82466207ac25b013f193376"
"checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d" "checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d"
"checksum memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2ffa2c986de11a9df78620c01eeaaf27d94d3ff02bf81bfcca953102dd0c6ff" "checksum memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2ffa2c986de11a9df78620c01eeaaf27d94d3ff02bf81bfcca953102dd0c6ff"
"checksum memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882"
"checksum mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0" "checksum mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0"
"checksum mime 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e3d709ffbb330e1566dc2f2a3c9b58a5ad4a381f740b810cd305dc3f089bc160" "checksum mime 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e3d709ffbb330e1566dc2f2a3c9b58a5ad4a381f740b810cd305dc3f089bc160"
"checksum mime_guess 2.0.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)" = "27a5e6679a0614e25adc14c6434ba84e41632b765a6d9cb2031a0cca682699ae" "checksum mime_guess 2.0.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)" = "27a5e6679a0614e25adc14c6434ba84e41632b765a6d9cb2031a0cca682699ae"
@ -3764,7 +3771,7 @@ dependencies = [
"checksum parity-ui-old-dev 1.9.0 (git+https://github.com/parity-js/dapp-wallet.git?rev=1a58bf4836c84e1632e27ef607b5a388abd2bf2d)" = "<none>" "checksum parity-ui-old-dev 1.9.0 (git+https://github.com/parity-js/dapp-wallet.git?rev=1a58bf4836c84e1632e27ef607b5a388abd2bf2d)" = "<none>"
"checksum parity-ui-old-precompiled 1.9.0 (git+https://github.com/js-dist-paritytech/parity-master-1-10-wallet.git?rev=4c067dfa1a17fe71ab2ca26b18c52dcbd0f4fc04)" = "<none>" "checksum parity-ui-old-precompiled 1.9.0 (git+https://github.com/js-dist-paritytech/parity-master-1-10-wallet.git?rev=4c067dfa1a17fe71ab2ca26b18c52dcbd0f4fc04)" = "<none>"
"checksum parity-ui-precompiled 1.9.0 (git+https://github.com/js-dist-paritytech/parity-master-1-10-shell.git?rev=ac243b5ce9ce10d5f9f6137e974f195e8403b68e)" = "<none>" "checksum parity-ui-precompiled 1.9.0 (git+https://github.com/js-dist-paritytech/parity-master-1-10-shell.git?rev=ac243b5ce9ce10d5f9f6137e974f195e8403b68e)" = "<none>"
"checksum parity-wasm 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1ba4b1d4236b76694f6ab8d8d00cdbe1e37c6dd1b5c803d26721f27e097d4d9" "checksum parity-wasm 0.27.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9d3c21b85fed4e8490e716b7fcb247185ec201f28845be6e749ab864401463c7"
"checksum parity-wordlist 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d0dec124478845b142f68b446cbee953d14d4b41f1bc0425024417720dce693" "checksum parity-wordlist 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d0dec124478845b142f68b446cbee953d14d4b41f1bc0425024417720dce693"
"checksum parking_lot 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3e7f7c9857874e54afeb950eebeae662b1e51a2493666d2ea4c0a5d91dcf0412" "checksum parking_lot 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3e7f7c9857874e54afeb950eebeae662b1e51a2493666d2ea4c0a5d91dcf0412"
"checksum parking_lot_core 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4f610cb9664da38e417ea3225f23051f589851999535290e077939838ab7a595" "checksum parking_lot_core 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4f610cb9664da38e417ea3225f23051f589851999535290e077939838ab7a595"
@ -3886,7 +3893,7 @@ dependencies = [
"checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d" "checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum wasm-utils 0.1.0 (git+https://github.com/paritytech/wasm-utils)" = "<none>" "checksum wasm-utils 0.1.0 (git+https://github.com/paritytech/wasm-utils)" = "<none>"
"checksum wasmi 0.0.0 (git+https://github.com/pepyakin/wasmi)" = "<none>" "checksum wasmi 0.0.0 (git+https://github.com/paritytech/wasmi)" = "<none>"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" "checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"

View File

@ -119,8 +119,8 @@ pub struct Schedule {
/// Wasm cost table /// Wasm cost table
pub struct WasmCosts { pub struct WasmCosts {
/// Arena allocator cost, per byte /// Default opcode cost
pub alloc: u32, pub regular: u32,
/// Div operations multiplier. /// Div operations multiplier.
pub div: u32, pub div: u32,
/// Div operations multiplier. /// Div operations multiplier.
@ -135,17 +135,18 @@ pub struct WasmCosts {
pub initial_mem: u32, pub initial_mem: u32,
/// Grow memory cost, per page (64kb) /// Grow memory cost, per page (64kb)
pub grow_mem: u32, pub grow_mem: u32,
/// Max stack height (native WebAssembly stack limiter)
pub max_stack_height: u32,
/// Cost of wasm opcode is calculated as TABLE_ENTRY_COST * `opcodes_mul` / `opcodes_div` /// Cost of wasm opcode is calculated as TABLE_ENTRY_COST * `opcodes_mul` / `opcodes_div`
pub opcodes_mul: u32, pub opcodes_mul: u32,
/// Cost of wasm opcode is calculated as TABLE_ENTRY_COST * `opcodes_mul` / `opcodes_div` /// Cost of wasm opcode is calculated as TABLE_ENTRY_COST * `opcodes_mul` / `opcodes_div`
pub opcodes_div: u32, pub opcodes_div: u32,
} }
impl Default for WasmCosts { impl Default for WasmCosts {
fn default() -> Self { fn default() -> Self {
WasmCosts { WasmCosts {
alloc: 2, regular: 1,
div: 16, div: 16,
mul: 4, mul: 4,
mem: 2, mem: 2,
@ -153,6 +154,7 @@ impl Default for WasmCosts {
static_address: 40, static_address: 40,
initial_mem: 4096, initial_mem: 4096,
grow_mem: 8192, grow_mem: 8192,
max_stack_height: 64*1024,
opcodes_mul: 3, opcodes_mul: 3,
opcodes_div: 8, opcodes_div: 8,
} }

View File

@ -7,9 +7,9 @@ authors = ["Parity Technologies <admin@parity.io>"]
byteorder = "1.0" byteorder = "1.0"
ethereum-types = "0.2" ethereum-types = "0.2"
log = "0.3" log = "0.3"
parity-wasm = "0.23" parity-wasm = "0.27"
libc = "0.2" libc = "0.2"
wasm-utils = { git = "https://github.com/paritytech/wasm-utils" } wasm-utils = { git = "https://github.com/paritytech/wasm-utils" }
vm = { path = "../vm" } vm = { path = "../vm" }
ethcore-logger = { path = "../../logger" } ethcore-logger = { path = "../../logger" }
wasmi = { git = "https://github.com/pepyakin/wasmi" } wasmi = { git = "https://github.com/paritytech/wasmi" }

View File

@ -19,7 +19,7 @@
use std::cell::RefCell; use std::cell::RefCell;
use wasmi::{ use wasmi::{
self, Signature, Error, FuncRef, FuncInstance, MemoryDescriptor, self, Signature, Error, FuncRef, FuncInstance, MemoryDescriptor,
MemoryRef, MemoryInstance, MemoryRef, MemoryInstance, memory_units,
}; };
/// Internal ids all functions runtime supports. This is just a glue for wasmi interpreter /// Internal ids all functions runtime supports. This is just a glue for wasmi interpreter
@ -219,7 +219,10 @@ impl ImportResolver {
let mut mem_ref = self.memory.borrow_mut(); let mut mem_ref = self.memory.borrow_mut();
if mem_ref.is_none() { if mem_ref.is_none() {
*mem_ref = Some( *mem_ref = Some(
MemoryInstance::alloc(0, Some(0)).expect("Memory allocation (0, 0) should not fail; qed") MemoryInstance::alloc(
memory_units::Pages(0),
Some(memory_units::Pages(0)),
).expect("Memory allocation (0, 0) should not fail; qed")
); );
} }
} }
@ -229,7 +232,7 @@ impl ImportResolver {
/// Returns memory size module initially requested /// Returns memory size module initially requested
pub fn memory_size(&self) -> Result<u32, Error> { pub fn memory_size(&self) -> Result<u32, Error> {
Ok(self.memory_ref().size()) Ok(self.memory_ref().current_size().0 as u32)
} }
} }
@ -281,7 +284,10 @@ impl wasmi::ModuleImportResolver for ImportResolver {
{ {
Err(Error::Instantiation("Module requested too much memory".to_owned())) Err(Error::Instantiation("Module requested too much memory".to_owned()))
} else { } else {
let mem = MemoryInstance::alloc(descriptor.initial(), descriptor.maximum())?; let mem = MemoryInstance::alloc(
memory_units::Pages(descriptor.initial() as usize),
descriptor.maximum().map(|x| memory_units::Pages(x as usize)),
)?;
*self.memory.borrow_mut() = Some(mem.clone()); *self.memory.borrow_mut() = Some(mem.clone());
Ok(mem) Ok(mem)
} }

View File

@ -34,7 +34,7 @@ mod panic_payload;
mod parser; mod parser;
use vm::{GasLeft, ReturnData, ActionParams}; use vm::{GasLeft, ReturnData, ActionParams};
use wasmi::Error as InterpreterError; use wasmi::{Error as InterpreterError, Trap};
use runtime::{Runtime, RuntimeContext}; use runtime::{Runtime, RuntimeContext};
@ -42,17 +42,29 @@ use ethereum_types::U256;
/// Wrapped interpreter error /// Wrapped interpreter error
#[derive(Debug)] #[derive(Debug)]
pub struct Error(InterpreterError); pub enum Error {
Interpreter(InterpreterError),
Trap(Trap),
}
impl From<InterpreterError> for Error { impl From<InterpreterError> for Error {
fn from(e: InterpreterError) -> Self { fn from(e: InterpreterError) -> Self {
Error(e) Error::Interpreter(e)
}
}
impl From<Trap> for Error {
fn from(e: Trap) -> Self {
Error::Trap(e)
} }
} }
impl From<Error> for vm::Error { impl From<Error> for vm::Error {
fn from(e: Error) -> Self { fn from(e: Error) -> Self {
vm::Error::Wasm(format!("Wasm runtime error: {:?}", e.0)) match e {
Error::Interpreter(e) => vm::Error::Wasm(format!("Wasm runtime error: {:?}", e)),
Error::Trap(e) => vm::Error::Wasm(format!("Wasm contract trap: {:?}", e)),
}
} }
} }
@ -70,14 +82,14 @@ impl vm::Vm for WasmInterpreter {
fn exec(&mut self, params: ActionParams, ext: &mut vm::Ext) -> vm::Result<GasLeft> { fn exec(&mut self, params: ActionParams, ext: &mut vm::Ext) -> vm::Result<GasLeft> {
let (module, data) = parser::payload(&params, ext.schedule().wasm())?; let (module, data) = parser::payload(&params, ext.schedule().wasm())?;
let loaded_module = wasmi::Module::from_parity_wasm_module(module).map_err(Error)?; let loaded_module = wasmi::Module::from_parity_wasm_module(module).map_err(Error::Interpreter)?;
let instantiation_resolover = env::ImportResolver::with_limit(16); let instantiation_resolver = env::ImportResolver::with_limit(16);
let module_instance = wasmi::ModuleInstance::new( let module_instance = wasmi::ModuleInstance::new(
&loaded_module, &loaded_module,
&wasmi::ImportsBuilder::new().with_resolver("env", &instantiation_resolover) &wasmi::ImportsBuilder::new().with_resolver("env", &instantiation_resolver)
).map_err(Error)?; ).map_err(Error::Interpreter)?;
let adjusted_gas = params.gas * U256::from(ext.schedule().wasm().opcodes_div) / let adjusted_gas = params.gas * U256::from(ext.schedule().wasm().opcodes_div) /
U256::from(ext.schedule().wasm().opcodes_mul); U256::from(ext.schedule().wasm().opcodes_mul);
@ -87,13 +99,13 @@ impl vm::Vm for WasmInterpreter {
return Err(vm::Error::Wasm("Wasm interpreter cannot run contracts with gas (wasm adjusted) >= 2^64".to_owned())); return Err(vm::Error::Wasm("Wasm interpreter cannot run contracts with gas (wasm adjusted) >= 2^64".to_owned()));
} }
let initial_memory = instantiation_resolover.memory_size().map_err(Error)?; let initial_memory = instantiation_resolver.memory_size().map_err(Error::Interpreter)?;
trace!(target: "wasm", "Contract requested {:?} pages of initial memory", initial_memory); trace!(target: "wasm", "Contract requested {:?} pages of initial memory", initial_memory);
let (gas_left, result) = { let (gas_left, result) = {
let mut runtime = Runtime::with_params( let mut runtime = Runtime::with_params(
ext, ext,
instantiation_resolover.memory_ref(), instantiation_resolver.memory_ref(),
// cannot overflow, checked above // cannot overflow, checked above
adjusted_gas.low_u64(), adjusted_gas.low_u64(),
data.to_vec(), data.to_vec(),
@ -114,33 +126,27 @@ impl vm::Vm for WasmInterpreter {
assert!(runtime.schedule().wasm().initial_mem < 1 << 16); assert!(runtime.schedule().wasm().initial_mem < 1 << 16);
runtime.charge(|s| initial_memory as u64 * s.wasm().initial_mem as u64)?; runtime.charge(|s| initial_memory as u64 * s.wasm().initial_mem as u64)?;
let module_instance = module_instance.run_start(&mut runtime).map_err(Error)?; let module_instance = module_instance.run_start(&mut runtime).map_err(Error::Trap)?;
match module_instance.invoke_export("call", &[], &mut runtime) { let invoke_result = module_instance.invoke_export("call", &[], &mut runtime);
Ok(_) => { },
Err(InterpreterError::Host(boxed)) => { let mut suicide = false;
match boxed.downcast_ref::<runtime::Error>() { if let Err(InterpreterError::Trap(ref trap)) = invoke_result {
None => { if let wasmi::TrapKind::Host(ref boxed) = *trap.kind() {
return Err(vm::Error::Wasm("Invalid user error used in interpreter".to_owned())); let ref runtime_err = boxed.downcast_ref::<runtime::Error>()
} .expect("Host errors other than runtime::Error never produced; qed");
Some(runtime_err) => {
match *runtime_err { if let runtime::Error::Suicide = **runtime_err { suicide = true; }
runtime::Error::Suicide => {
// Suicide uses trap to break execution
}
ref any_err => {
trace!(target: "wasm", "Error executing contract: {:?}", boxed);
return Err(vm::Error::from(Error::from(InterpreterError::Host(Box::new(any_err.clone())))));
} }
} }
if !suicide {
if let Err(e) = invoke_result {
trace!(target: "wasm", "Error executing contract: {:?}", e);
return Err(vm::Error::from(Error::from(e)));
} }
} }
},
Err(err) => {
trace!(target: "wasm", "Error executing contract: {:?}", err);
return Err(vm::Error::from(Error::from(err)))
}
}
( (
runtime.gas_left().expect("Cannot fail since it was not updated since last charge"), runtime.gas_left().expect("Cannot fail since it was not updated since last charge"),
runtime.into_result(), runtime.into_result(),

View File

@ -22,14 +22,18 @@ use parity_wasm::elements::{self, Deserialize};
use parity_wasm::peek_size; use parity_wasm::peek_size;
fn gas_rules(wasm_costs: &vm::WasmCosts) -> rules::Set { fn gas_rules(wasm_costs: &vm::WasmCosts) -> rules::Set {
rules::Set::new({ rules::Set::new(
let mut vals = ::std::collections::HashMap::with_capacity(4); wasm_costs.regular,
vals.insert(rules::InstructionType::Load, wasm_costs.mem as u32); {
vals.insert(rules::InstructionType::Store, wasm_costs.mem as u32); let mut vals = ::std::collections::HashMap::with_capacity(8);
vals.insert(rules::InstructionType::Div, wasm_costs.div as u32); vals.insert(rules::InstructionType::Load, rules::Metering::Fixed(wasm_costs.mem as u32));
vals.insert(rules::InstructionType::Mul, wasm_costs.mul as u32); vals.insert(rules::InstructionType::Store, rules::Metering::Fixed(wasm_costs.mem as u32));
vals.insert(rules::InstructionType::Div, rules::Metering::Fixed(wasm_costs.div as u32));
vals.insert(rules::InstructionType::Mul, rules::Metering::Fixed(wasm_costs.mul as u32));
vals vals
}).with_grow_cost(wasm_costs.grow_mem) })
.with_grow_cost(wasm_costs.grow_mem)
.with_forbidden_floats()
} }
/// Splits payload to code and data according to params.params_type, also /// Splits payload to code and data according to params.params_type, also
@ -71,7 +75,12 @@ pub fn payload<'a>(params: &'a vm::ActionParams, wasm_costs: &vm::WasmCosts)
let contract_module = wasm_utils::inject_gas_counter( let contract_module = wasm_utils::inject_gas_counter(
deserialized_module, deserialized_module,
&gas_rules(wasm_costs), &gas_rules(wasm_costs),
); ).map_err(|_| vm::Error::Wasm(format!("Wasm contract error: bytecode invalid")))?;
let contract_module = wasm_utils::stack_height::inject_limiter(
contract_module,
wasm_costs.max_stack_height,
).map_err(|_| vm::Error::Wasm(format!("Wasm contract error: stack limiter failure")))?;
let data = match params.params_type { let data = match params.params_type {
vm::ParamsType::Embedded => { vm::ParamsType::Embedded => {

View File

@ -1,6 +1,6 @@
use ethereum_types::{U256, H256, Address}; use ethereum_types::{U256, H256, Address};
use vm::{self, CallType}; use vm::{self, CallType};
use wasmi::{self, MemoryRef, RuntimeArgs, RuntimeValue, Error as InterpreterError}; use wasmi::{self, MemoryRef, RuntimeArgs, RuntimeValue, Error as InterpreterError, Trap, TrapKind};
use super::panic_payload; use super::panic_payload;
pub struct RuntimeContext { pub struct RuntimeContext {
@ -52,15 +52,40 @@ pub enum Error {
Other, Other,
/// Syscall signature mismatch /// Syscall signature mismatch
InvalidSyscall, InvalidSyscall,
/// Unreachable instruction encountered
Unreachable,
/// Invalid virtual call
InvalidVirtualCall,
/// Division by zero
DivisionByZero,
/// Invalid conversion to integer
InvalidConversionToInt,
/// Stack overflow
StackOverflow,
/// Panic with message /// Panic with message
Panic(String), Panic(String),
} }
impl wasmi::HostError for Error { } impl wasmi::HostError for Error { }
impl From<Trap> for Error {
fn from(trap: Trap) -> Self {
match *trap.kind() {
TrapKind::Unreachable => Error::Unreachable,
TrapKind::MemoryAccessOutOfBounds => Error::MemoryAccessViolation,
TrapKind::TableAccessOutOfBounds | TrapKind::ElemUninitialized => Error::InvalidVirtualCall,
TrapKind::DivisionByZero => Error::DivisionByZero,
TrapKind::InvalidConversionToInt => Error::InvalidConversionToInt,
TrapKind::UnexpectedSignature => Error::InvalidVirtualCall,
TrapKind::StackOverflow => Error::StackOverflow,
TrapKind::Host(_) => Error::Other,
}
}
}
impl From<InterpreterError> for Error { impl From<InterpreterError> for Error {
fn from(interpreter_err: InterpreterError) -> Self { fn from(err: InterpreterError) -> Self {
match interpreter_err { match err {
InterpreterError::Value(_) => Error::InvalidSyscall, InterpreterError::Value(_) => Error::InvalidSyscall,
InterpreterError::Memory(_) => Error::MemoryAccessViolation, InterpreterError::Memory(_) => Error::MemoryAccessViolation,
_ => Error::Other, _ => Error::Other,
@ -85,6 +110,11 @@ impl ::std::fmt::Display for Error {
Error::Log => write!(f, "Error occured while logging an event"), Error::Log => write!(f, "Error occured while logging an event"),
Error::InvalidSyscall => write!(f, "Invalid syscall signature encountered at runtime"), Error::InvalidSyscall => write!(f, "Invalid syscall signature encountered at runtime"),
Error::Other => write!(f, "Other unspecified error"), Error::Other => write!(f, "Other unspecified error"),
Error::Unreachable => write!(f, "Unreachable instruction encountered"),
Error::InvalidVirtualCall => write!(f, "Invalid virtual call"),
Error::DivisionByZero => write!(f, "Division by zero"),
Error::StackOverflow => write!(f, "Stack overflow"),
Error::InvalidConversionToInt => write!(f, "Invalid conversion to integer"),
Error::Panic(ref msg) => write!(f, "Panic: {}", msg), Error::Panic(ref msg) => write!(f, "Panic: {}", msg),
} }
} }
@ -203,8 +233,8 @@ impl<'a> Runtime<'a> {
/// Read from the storage to wasm memory /// Read from the storage to wasm memory
pub fn storage_read(&mut self, args: RuntimeArgs) -> Result<()> pub fn storage_read(&mut self, args: RuntimeArgs) -> Result<()>
{ {
let key = self.h256_at(args.nth(0)?)?; let key = self.h256_at(args.nth_checked(0)?)?;
let val_ptr: u32 = args.nth(1)?; let val_ptr: u32 = args.nth_checked(1)?;
let val = self.ext.storage_at(&key).map_err(|_| Error::StorageReadError)?; let val = self.ext.storage_at(&key).map_err(|_| Error::StorageReadError)?;
@ -218,8 +248,8 @@ impl<'a> Runtime<'a> {
/// Write to storage from wasm memory /// Write to storage from wasm memory
pub fn storage_write(&mut self, args: RuntimeArgs) -> Result<()> pub fn storage_write(&mut self, args: RuntimeArgs) -> Result<()>
{ {
let key = self.h256_at(args.nth(0)?)?; let key = self.h256_at(args.nth_checked(0)?)?;
let val_ptr: u32 = args.nth(1)?; let val_ptr: u32 = args.nth_checked(1)?;
let val = self.h256_at(val_ptr)?; let val = self.h256_at(val_ptr)?;
let former_val = self.ext.storage_at(&key).map_err(|_| Error::StorageUpdateError)?; let former_val = self.ext.storage_at(&key).map_err(|_| Error::StorageUpdateError)?;
@ -250,8 +280,8 @@ impl<'a> Runtime<'a> {
/// * pointer in sandboxed memory where result is /// * pointer in sandboxed memory where result is
/// * the length of the result /// * the length of the result
pub fn ret(&mut self, args: RuntimeArgs) -> Result<()> { pub fn ret(&mut self, args: RuntimeArgs) -> Result<()> {
let ptr: u32 = args.nth(0)?; let ptr: u32 = args.nth_checked(0)?;
let len: u32 = args.nth(1)?; let len: u32 = args.nth_checked(1)?;
trace!(target: "wasm", "Contract ret: {} bytes @ {}", len, ptr); trace!(target: "wasm", "Contract ret: {} bytes @ {}", len, ptr);
@ -273,7 +303,7 @@ impl<'a> Runtime<'a> {
/// Report gas cost with the params passed in wasm stack /// Report gas cost with the params passed in wasm stack
fn gas(&mut self, args: RuntimeArgs) -> Result<()> { fn gas(&mut self, args: RuntimeArgs) -> Result<()> {
let amount: u32 = args.nth(0)?; let amount: u32 = args.nth_checked(0)?;
if self.charge_gas(amount as u64) { if self.charge_gas(amount as u64) {
Ok(()) Ok(())
} else { } else {
@ -288,7 +318,7 @@ impl<'a> Runtime<'a> {
/// Write input bytes to the memory location using the passed pointer /// Write input bytes to the memory location using the passed pointer
fn fetch_input(&mut self, args: RuntimeArgs) -> Result<()> { fn fetch_input(&mut self, args: RuntimeArgs) -> Result<()> {
let ptr: u32 = args.nth(0)?; let ptr: u32 = args.nth_checked(0)?;
self.memory.set(ptr, &self.args[..])?; self.memory.set(ptr, &self.args[..])?;
Ok(()) Ok(())
} }
@ -298,8 +328,8 @@ impl<'a> Runtime<'a> {
/// Contract can invoke this when he encounters unrecoverable error. /// Contract can invoke this when he encounters unrecoverable error.
fn panic(&mut self, args: RuntimeArgs) -> Result<()> fn panic(&mut self, args: RuntimeArgs) -> Result<()>
{ {
let payload_ptr: u32 = args.nth(0)?; let payload_ptr: u32 = args.nth_checked(0)?;
let payload_len: u32 = args.nth(1)?; let payload_len: u32 = args.nth_checked(1)?;
let raw_payload = self.memory.get(payload_ptr, payload_len as usize)?; let raw_payload = self.memory.get(payload_ptr, payload_len as usize)?;
let payload = panic_payload::decode(&raw_payload); let payload = panic_payload::decode(&raw_payload);
@ -333,26 +363,26 @@ impl<'a> Runtime<'a> {
{ {
trace!(target: "wasm", "runtime: CALL({:?})", call_type); trace!(target: "wasm", "runtime: CALL({:?})", call_type);
let gas: u64 = args.nth(0)?; let gas: u64 = args.nth_checked(0)?;
trace!(target: "wasm", " gas: {:?}", gas); trace!(target: "wasm", " gas: {:?}", gas);
let address = self.address_at(args.nth(1)?)?; let address = self.address_at(args.nth_checked(1)?)?;
trace!(target: "wasm", " address: {:?}", address); trace!(target: "wasm", " address: {:?}", address);
let vofs = if use_val { 1 } else { 0 }; let vofs = if use_val { 1 } else { 0 };
let val = if use_val { Some(self.u256_at(args.nth(2)?)?) } else { None }; let val = if use_val { Some(self.u256_at(args.nth_checked(2)?)?) } else { None };
trace!(target: "wasm", " val: {:?}", val); trace!(target: "wasm", " val: {:?}", val);
let input_ptr: u32 = args.nth(2 + vofs)?; let input_ptr: u32 = args.nth_checked(2 + vofs)?;
trace!(target: "wasm", " input_ptr: {:?}", input_ptr); trace!(target: "wasm", " input_ptr: {:?}", input_ptr);
let input_len: u32 = args.nth(3 + vofs)?; let input_len: u32 = args.nth_checked(3 + vofs)?;
trace!(target: "wasm", " input_len: {:?}", input_len); trace!(target: "wasm", " input_len: {:?}", input_len);
let result_ptr: u32 = args.nth(4 + vofs)?; let result_ptr: u32 = args.nth_checked(4 + vofs)?;
trace!(target: "wasm", " result_ptr: {:?}", result_ptr); trace!(target: "wasm", " result_ptr: {:?}", result_ptr);
let result_alloc_len: u32 = args.nth(5 + vofs)?; let result_alloc_len: u32 = args.nth_checked(5 + vofs)?;
trace!(target: "wasm", " result_len: {:?}", result_alloc_len); trace!(target: "wasm", " result_len: {:?}", result_alloc_len);
if let Some(ref val) = val { if let Some(ref val) = val {
@ -453,7 +483,7 @@ impl<'a> Runtime<'a> {
/// Returns value (in Wei) passed to contract /// Returns value (in Wei) passed to contract
pub fn value(&mut self, args: RuntimeArgs) -> Result<()> { pub fn value(&mut self, args: RuntimeArgs) -> Result<()> {
let val = self.context.value; let val = self.context.value;
self.return_u256_ptr(args.nth(0)?, val) self.return_u256_ptr(args.nth_checked(0)?, val)
} }
/// Creates a new contract /// Creates a new contract
@ -470,13 +500,13 @@ impl<'a> Runtime<'a> {
// fn create(endowment: *const u8, code_ptr: *const u8, code_len: u32, result_ptr: *mut u8) -> i32; // fn create(endowment: *const u8, code_ptr: *const u8, code_len: u32, result_ptr: *mut u8) -> i32;
// //
trace!(target: "wasm", "runtime: CREATE"); trace!(target: "wasm", "runtime: CREATE");
let endowment = self.u256_at(args.nth(0)?)?; let endowment = self.u256_at(args.nth_checked(0)?)?;
trace!(target: "wasm", " val: {:?}", endowment); trace!(target: "wasm", " val: {:?}", endowment);
let code_ptr: u32 = args.nth(1)?; let code_ptr: u32 = args.nth_checked(1)?;
trace!(target: "wasm", " code_ptr: {:?}", code_ptr); trace!(target: "wasm", " code_ptr: {:?}", code_ptr);
let code_len: u32 = args.nth(2)?; let code_len: u32 = args.nth_checked(2)?;
trace!(target: "wasm", " code_len: {:?}", code_len); trace!(target: "wasm", " code_len: {:?}", code_len);
let result_ptr: u32 = args.nth(3)?; let result_ptr: u32 = args.nth_checked(3)?;
trace!(target: "wasm", "result_ptr: {:?}", result_ptr); trace!(target: "wasm", "result_ptr: {:?}", result_ptr);
let code = self.memory.get(code_ptr, code_len as usize)?; let code = self.memory.get(code_ptr, code_len as usize)?;
@ -518,8 +548,8 @@ impl<'a> Runtime<'a> {
fn debug(&mut self, args: RuntimeArgs) -> Result<()> fn debug(&mut self, args: RuntimeArgs) -> Result<()>
{ {
let msg_ptr: u32 = args.nth(0)?; let msg_ptr: u32 = args.nth_checked(0)?;
let msg_len: u32 = args.nth(1)?; let msg_len: u32 = args.nth_checked(1)?;
let msg = String::from_utf8(self.memory.get(msg_ptr, msg_len as usize)?) let msg = String::from_utf8(self.memory.get(msg_ptr, msg_len as usize)?)
.map_err(|_| Error::BadUtf8)?; .map_err(|_| Error::BadUtf8)?;
@ -532,7 +562,7 @@ impl<'a> Runtime<'a> {
/// Pass suicide to state runtime /// Pass suicide to state runtime
pub fn suicide(&mut self, args: RuntimeArgs) -> Result<()> pub fn suicide(&mut self, args: RuntimeArgs) -> Result<()>
{ {
let refund_address = self.address_at(args.nth(0)?)?; let refund_address = self.address_at(args.nth_checked(0)?)?;
if self.ext.exists(&refund_address).map_err(|_| Error::SuicideAbort)? { if self.ext.exists(&refund_address).map_err(|_| Error::SuicideAbort)? {
trace!(target: "wasm", "Suicide: refund to existing address {}", refund_address); trace!(target: "wasm", "Suicide: refund to existing address {}", refund_address);
@ -551,8 +581,8 @@ impl<'a> Runtime<'a> {
/// Signature: `fn blockhash(number: i64, dest: *mut u8)` /// Signature: `fn blockhash(number: i64, dest: *mut u8)`
pub fn blockhash(&mut self, args: RuntimeArgs) -> Result<()> { pub fn blockhash(&mut self, args: RuntimeArgs) -> Result<()> {
self.adjusted_charge(|schedule| schedule.blockhash_gas as u64)?; self.adjusted_charge(|schedule| schedule.blockhash_gas as u64)?;
let hash = self.ext.blockhash(&U256::from(args.nth::<u64>(0)?)); let hash = self.ext.blockhash(&U256::from(args.nth_checked::<u64>(0)?));
self.memory.set(args.nth(1)?, &*hash)?; self.memory.set(args.nth_checked(1)?, &*hash)?;
Ok(()) Ok(())
} }
@ -565,37 +595,37 @@ impl<'a> Runtime<'a> {
/// Signature: `fn coinbase(dest: *mut u8)` /// Signature: `fn coinbase(dest: *mut u8)`
pub fn coinbase(&mut self, args: RuntimeArgs) -> Result<()> { pub fn coinbase(&mut self, args: RuntimeArgs) -> Result<()> {
let coinbase = self.ext.env_info().author; let coinbase = self.ext.env_info().author;
self.return_address_ptr(args.nth(0)?, coinbase) self.return_address_ptr(args.nth_checked(0)?, coinbase)
} }
/// Signature: `fn difficulty(dest: *mut u8)` /// Signature: `fn difficulty(dest: *mut u8)`
pub fn difficulty(&mut self, args: RuntimeArgs) -> Result<()> { pub fn difficulty(&mut self, args: RuntimeArgs) -> Result<()> {
let difficulty = self.ext.env_info().difficulty; let difficulty = self.ext.env_info().difficulty;
self.return_u256_ptr(args.nth(0)?, difficulty) self.return_u256_ptr(args.nth_checked(0)?, difficulty)
} }
/// Signature: `fn gaslimit(dest: *mut u8)` /// Signature: `fn gaslimit(dest: *mut u8)`
pub fn gaslimit(&mut self, args: RuntimeArgs) -> Result<()> { pub fn gaslimit(&mut self, args: RuntimeArgs) -> Result<()> {
let gas_limit = self.ext.env_info().gas_limit; let gas_limit = self.ext.env_info().gas_limit;
self.return_u256_ptr(args.nth(0)?, gas_limit) self.return_u256_ptr(args.nth_checked(0)?, gas_limit)
} }
/// Signature: `fn address(dest: *mut u8)` /// Signature: `fn address(dest: *mut u8)`
pub fn address(&mut self, args: RuntimeArgs) -> Result<()> { pub fn address(&mut self, args: RuntimeArgs) -> Result<()> {
let address = self.context.address; let address = self.context.address;
self.return_address_ptr(args.nth(0)?, address) self.return_address_ptr(args.nth_checked(0)?, address)
} }
/// Signature: `sender(dest: *mut u8)` /// Signature: `sender(dest: *mut u8)`
pub fn sender(&mut self, args: RuntimeArgs) -> Result<()> { pub fn sender(&mut self, args: RuntimeArgs) -> Result<()> {
let sender = self.context.sender; let sender = self.context.sender;
self.return_address_ptr(args.nth(0)?, sender) self.return_address_ptr(args.nth_checked(0)?, sender)
} }
/// Signature: `origin(dest: *mut u8)` /// Signature: `origin(dest: *mut u8)`
pub fn origin(&mut self, args: RuntimeArgs) -> Result<()> { pub fn origin(&mut self, args: RuntimeArgs) -> Result<()> {
let origin = self.context.origin; let origin = self.context.origin;
self.return_address_ptr(args.nth(0)?, origin) self.return_address_ptr(args.nth_checked(0)?, origin)
} }
/// Signature: `timestamp() -> i64` /// Signature: `timestamp() -> i64`
@ -607,10 +637,10 @@ impl<'a> Runtime<'a> {
/// Signature: `fn elog(topic_ptr: *const u8, topic_count: u32, data_ptr: *const u8, data_len: u32)` /// Signature: `fn elog(topic_ptr: *const u8, topic_count: u32, data_ptr: *const u8, data_len: u32)`
pub fn elog(&mut self, args: RuntimeArgs) -> Result<()> pub fn elog(&mut self, args: RuntimeArgs) -> Result<()>
{ {
let topic_ptr: u32 = args.nth(0)?; let topic_ptr: u32 = args.nth_checked(0)?;
let topic_count: u32 = args.nth(1)?; let topic_count: u32 = args.nth_checked(1)?;
let data_ptr: u32 = args.nth(2)?; let data_ptr: u32 = args.nth_checked(2)?;
let data_len: u32 = args.nth(3)?; let data_len: u32 = args.nth_checked(3)?;
if topic_count > 4 { if topic_count > 4 {
return Err(Error::Log.into()); return Err(Error::Log.into());
@ -643,7 +673,7 @@ impl<'a> Runtime<'a> {
mod ext_impl { mod ext_impl {
use wasmi::{Externals, RuntimeArgs, RuntimeValue, Error}; use wasmi::{Externals, RuntimeArgs, RuntimeValue, Trap};
use env::ids::*; use env::ids::*;
macro_rules! void { macro_rules! void {
@ -663,7 +693,7 @@ mod ext_impl {
&mut self, &mut self,
index: usize, index: usize,
args: RuntimeArgs, args: RuntimeArgs,
) -> Result<Option<RuntimeValue>, Error> { ) -> Result<Option<RuntimeValue>, Trap> {
match index { match index {
STORAGE_WRITE_FUNC => void!(self.storage_write(args)), STORAGE_WRITE_FUNC => void!(self.storage_write(args)),
STORAGE_READ_FUNC => void!(self.storage_read(args)), STORAGE_READ_FUNC => void!(self.storage_read(args)),