From e0a21e5aaec4e04d783e9ab6d2574a928a35da39 Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Mon, 12 Mar 2018 14:37:32 +0300 Subject: [PATCH] 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 --- Cargo.lock | 25 +++++--- ethcore/vm/src/schedule.rs | 10 ++-- ethcore/wasm/Cargo.toml | 4 +- ethcore/wasm/src/env.rs | 14 +++-- ethcore/wasm/src/lib.rs | 74 ++++++++++++----------- ethcore/wasm/src/parser.rs | 27 ++++++--- ethcore/wasm/src/runtime.rs | 114 +++++++++++++++++++++++------------- 7 files changed, 164 insertions(+), 104 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0d61c1ace..c2304b2c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1553,6 +1553,11 @@ dependencies = [ "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]] name = "memorydb" version = "0.1.1" @@ -2292,7 +2297,7 @@ dependencies = [ [[package]] name = "parity-wasm" -version = "0.23.0" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "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)", "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)", - "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", "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]] name = "wasm-utils" version = "0.1.0" -source = "git+https://github.com/paritytech/wasm-utils#6fdc1c4ed47a6acb0a4774da505a416dd637bc6d" +source = "git+https://github.com/paritytech/wasm-utils#9527b969a4928f6293369380defc7982493a678c" dependencies = [ "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)", @@ -3517,16 +3522,17 @@ dependencies = [ "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)", "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]] name = "wasmi" version = "0.0.0" -source = "git+https://github.com/pepyakin/wasmi#551c99273042deaad869c17798060e2212deacab" +source = "git+https://github.com/paritytech/wasmi#7c88c6ad651ec6a6aa00b08e61c70853161a0a99" dependencies = [ "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]] @@ -3728,6 +3734,7 @@ dependencies = [ "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 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.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" @@ -3764,7 +3771,7 @@ dependencies = [ "checksum parity-ui-old-dev 1.9.0 (git+https://github.com/parity-js/dapp-wallet.git?rev=1a58bf4836c84e1632e27ef607b5a388abd2bf2d)" = "" "checksum parity-ui-old-precompiled 1.9.0 (git+https://github.com/js-dist-paritytech/parity-master-1-10-wallet.git?rev=4c067dfa1a17fe71ab2ca26b18c52dcbd0f4fc04)" = "" "checksum parity-ui-precompiled 1.9.0 (git+https://github.com/js-dist-paritytech/parity-master-1-10-shell.git?rev=ac243b5ce9ce10d5f9f6137e974f195e8403b68e)" = "" -"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 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" @@ -3886,7 +3893,7 @@ dependencies = [ "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 wasm-utils 0.1.0 (git+https://github.com/paritytech/wasm-utils)" = "" -"checksum wasmi 0.0.0 (git+https://github.com/pepyakin/wasmi)" = "" +"checksum wasmi 0.0.0 (git+https://github.com/paritytech/wasmi)" = "" "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-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" diff --git a/ethcore/vm/src/schedule.rs b/ethcore/vm/src/schedule.rs index cc184825c..c64f6159f 100644 --- a/ethcore/vm/src/schedule.rs +++ b/ethcore/vm/src/schedule.rs @@ -119,8 +119,8 @@ pub struct Schedule { /// Wasm cost table pub struct WasmCosts { - /// Arena allocator cost, per byte - pub alloc: u32, + /// Default opcode cost + pub regular: u32, /// Div operations multiplier. pub div: u32, /// Div operations multiplier. @@ -135,17 +135,18 @@ pub struct WasmCosts { pub initial_mem: u32, /// Grow memory cost, per page (64kb) 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` pub opcodes_mul: u32, /// Cost of wasm opcode is calculated as TABLE_ENTRY_COST * `opcodes_mul` / `opcodes_div` pub opcodes_div: u32, - } impl Default for WasmCosts { fn default() -> Self { WasmCosts { - alloc: 2, + regular: 1, div: 16, mul: 4, mem: 2, @@ -153,6 +154,7 @@ impl Default for WasmCosts { static_address: 40, initial_mem: 4096, grow_mem: 8192, + max_stack_height: 64*1024, opcodes_mul: 3, opcodes_div: 8, } diff --git a/ethcore/wasm/Cargo.toml b/ethcore/wasm/Cargo.toml index d3f2852c6..38ec7f268 100644 --- a/ethcore/wasm/Cargo.toml +++ b/ethcore/wasm/Cargo.toml @@ -7,9 +7,9 @@ authors = ["Parity Technologies "] byteorder = "1.0" ethereum-types = "0.2" log = "0.3" -parity-wasm = "0.23" +parity-wasm = "0.27" libc = "0.2" wasm-utils = { git = "https://github.com/paritytech/wasm-utils" } vm = { path = "../vm" } ethcore-logger = { path = "../../logger" } -wasmi = { git = "https://github.com/pepyakin/wasmi" } \ No newline at end of file +wasmi = { git = "https://github.com/paritytech/wasmi" } \ No newline at end of file diff --git a/ethcore/wasm/src/env.rs b/ethcore/wasm/src/env.rs index c5b30df84..7ffaaf98a 100644 --- a/ethcore/wasm/src/env.rs +++ b/ethcore/wasm/src/env.rs @@ -19,7 +19,7 @@ use std::cell::RefCell; use wasmi::{ 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 @@ -219,7 +219,10 @@ impl ImportResolver { let mut mem_ref = self.memory.borrow_mut(); if mem_ref.is_none() { *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 pub fn memory_size(&self) -> Result { - 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())) } 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()); Ok(mem) } diff --git a/ethcore/wasm/src/lib.rs b/ethcore/wasm/src/lib.rs index 322a59132..d6082e018 100644 --- a/ethcore/wasm/src/lib.rs +++ b/ethcore/wasm/src/lib.rs @@ -34,7 +34,7 @@ mod panic_payload; mod parser; use vm::{GasLeft, ReturnData, ActionParams}; -use wasmi::Error as InterpreterError; +use wasmi::{Error as InterpreterError, Trap}; use runtime::{Runtime, RuntimeContext}; @@ -42,17 +42,29 @@ use ethereum_types::U256; /// Wrapped interpreter error #[derive(Debug)] -pub struct Error(InterpreterError); +pub enum Error { + Interpreter(InterpreterError), + Trap(Trap), +} impl From for Error { fn from(e: InterpreterError) -> Self { - Error(e) + Error::Interpreter(e) + } +} + +impl From for Error { + fn from(e: Trap) -> Self { + Error::Trap(e) } } impl From for vm::Error { 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 { let (module, data) = parser::payload(¶ms, 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( &loaded_module, - &wasmi::ImportsBuilder::new().with_resolver("env", &instantiation_resolover) - ).map_err(Error)?; + &wasmi::ImportsBuilder::new().with_resolver("env", &instantiation_resolver) + ).map_err(Error::Interpreter)?; let adjusted_gas = params.gas * U256::from(ext.schedule().wasm().opcodes_div) / 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())); } - 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); let (gas_left, result) = { let mut runtime = Runtime::with_params( ext, - instantiation_resolover.memory_ref(), + instantiation_resolver.memory_ref(), // cannot overflow, checked above adjusted_gas.low_u64(), data.to_vec(), @@ -114,33 +126,27 @@ impl vm::Vm for WasmInterpreter { assert!(runtime.schedule().wasm().initial_mem < 1 << 16); 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) { - Ok(_) => { }, - Err(InterpreterError::Host(boxed)) => { - match boxed.downcast_ref::() { - None => { - return Err(vm::Error::Wasm("Invalid user error used in interpreter".to_owned())); - } - Some(runtime_err) => { - match *runtime_err { - 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()))))); - } - } - } - } - }, - Err(err) => { - trace!(target: "wasm", "Error executing contract: {:?}", err); - return Err(vm::Error::from(Error::from(err))) + let invoke_result = module_instance.invoke_export("call", &[], &mut runtime); + + let mut suicide = false; + if let Err(InterpreterError::Trap(ref trap)) = invoke_result { + if let wasmi::TrapKind::Host(ref boxed) = *trap.kind() { + let ref runtime_err = boxed.downcast_ref::() + .expect("Host errors other than runtime::Error never produced; qed"); + + if let runtime::Error::Suicide = **runtime_err { suicide = true; } } } + + if !suicide { + if let Err(e) = invoke_result { + trace!(target: "wasm", "Error executing contract: {:?}", e); + return Err(vm::Error::from(Error::from(e))); + } + } + ( runtime.gas_left().expect("Cannot fail since it was not updated since last charge"), runtime.into_result(), diff --git a/ethcore/wasm/src/parser.rs b/ethcore/wasm/src/parser.rs index 8ba7a7098..62cd66cb9 100644 --- a/ethcore/wasm/src/parser.rs +++ b/ethcore/wasm/src/parser.rs @@ -22,14 +22,18 @@ use parity_wasm::elements::{self, Deserialize}; use parity_wasm::peek_size; fn gas_rules(wasm_costs: &vm::WasmCosts) -> rules::Set { - rules::Set::new({ - let mut vals = ::std::collections::HashMap::with_capacity(4); - vals.insert(rules::InstructionType::Load, wasm_costs.mem as u32); - vals.insert(rules::InstructionType::Store, wasm_costs.mem as u32); - vals.insert(rules::InstructionType::Div, wasm_costs.div as u32); - vals.insert(rules::InstructionType::Mul, wasm_costs.mul as u32); - vals - }).with_grow_cost(wasm_costs.grow_mem) + rules::Set::new( + wasm_costs.regular, + { + let mut vals = ::std::collections::HashMap::with_capacity(8); + vals.insert(rules::InstructionType::Load, rules::Metering::Fixed(wasm_costs.mem 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 + }) + .with_grow_cost(wasm_costs.grow_mem) + .with_forbidden_floats() } /// 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( deserialized_module, &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 { vm::ParamsType::Embedded => { diff --git a/ethcore/wasm/src/runtime.rs b/ethcore/wasm/src/runtime.rs index adac12089..be5c57d99 100644 --- a/ethcore/wasm/src/runtime.rs +++ b/ethcore/wasm/src/runtime.rs @@ -1,6 +1,6 @@ use ethereum_types::{U256, H256, Address}; 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; pub struct RuntimeContext { @@ -52,15 +52,40 @@ pub enum Error { Other, /// Syscall signature mismatch 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(String), } impl wasmi::HostError for Error { } +impl From 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 for Error { - fn from(interpreter_err: InterpreterError) -> Self { - match interpreter_err { + fn from(err: InterpreterError) -> Self { + match err { InterpreterError::Value(_) => Error::InvalidSyscall, InterpreterError::Memory(_) => Error::MemoryAccessViolation, _ => Error::Other, @@ -85,6 +110,11 @@ impl ::std::fmt::Display for Error { Error::Log => write!(f, "Error occured while logging an event"), Error::InvalidSyscall => write!(f, "Invalid syscall signature encountered at runtime"), 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), } } @@ -203,8 +233,8 @@ impl<'a> Runtime<'a> { /// Read from the storage to wasm memory pub fn storage_read(&mut self, args: RuntimeArgs) -> Result<()> { - let key = self.h256_at(args.nth(0)?)?; - let val_ptr: u32 = args.nth(1)?; + let key = self.h256_at(args.nth_checked(0)?)?; + let val_ptr: u32 = args.nth_checked(1)?; 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 pub fn storage_write(&mut self, args: RuntimeArgs) -> Result<()> { - let key = self.h256_at(args.nth(0)?)?; - let val_ptr: u32 = args.nth(1)?; + let key = self.h256_at(args.nth_checked(0)?)?; + let val_ptr: u32 = args.nth_checked(1)?; let val = self.h256_at(val_ptr)?; 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 /// * the length of the result pub fn ret(&mut self, args: RuntimeArgs) -> Result<()> { - let ptr: u32 = args.nth(0)?; - let len: u32 = args.nth(1)?; + let ptr: u32 = args.nth_checked(0)?; + let len: u32 = args.nth_checked(1)?; 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 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) { Ok(()) } else { @@ -288,7 +318,7 @@ impl<'a> Runtime<'a> { /// Write input bytes to the memory location using the passed pointer 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[..])?; Ok(()) } @@ -298,8 +328,8 @@ impl<'a> Runtime<'a> { /// Contract can invoke this when he encounters unrecoverable error. fn panic(&mut self, args: RuntimeArgs) -> Result<()> { - let payload_ptr: u32 = args.nth(0)?; - let payload_len: u32 = args.nth(1)?; + let payload_ptr: u32 = args.nth_checked(0)?; + let payload_len: u32 = args.nth_checked(1)?; let raw_payload = self.memory.get(payload_ptr, payload_len as usize)?; let payload = panic_payload::decode(&raw_payload); @@ -333,26 +363,26 @@ impl<'a> Runtime<'a> { { 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); - let address = self.address_at(args.nth(1)?)?; + let address = self.address_at(args.nth_checked(1)?)?; trace!(target: "wasm", " address: {:?}", address); 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); - let input_ptr: u32 = args.nth(2 + vofs)?; + let input_ptr: u32 = args.nth_checked(2 + vofs)?; 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); - let result_ptr: u32 = args.nth(4 + vofs)?; + let result_ptr: u32 = args.nth_checked(4 + vofs)?; 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); if let Some(ref val) = val { @@ -453,7 +483,7 @@ impl<'a> Runtime<'a> { /// Returns value (in Wei) passed to contract pub fn value(&mut self, args: RuntimeArgs) -> Result<()> { 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 @@ -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; // 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); - let code_ptr: u32 = args.nth(1)?; + let code_ptr: u32 = args.nth_checked(1)?; 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); - let result_ptr: u32 = args.nth(3)?; + let result_ptr: u32 = args.nth_checked(3)?; trace!(target: "wasm", "result_ptr: {:?}", result_ptr); 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<()> { - let msg_ptr: u32 = args.nth(0)?; - let msg_len: u32 = args.nth(1)?; + let msg_ptr: u32 = args.nth_checked(0)?; + let msg_len: u32 = args.nth_checked(1)?; let msg = String::from_utf8(self.memory.get(msg_ptr, msg_len as usize)?) .map_err(|_| Error::BadUtf8)?; @@ -532,7 +562,7 @@ impl<'a> Runtime<'a> { /// Pass suicide to state runtime 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)? { 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)` pub fn blockhash(&mut self, args: RuntimeArgs) -> Result<()> { self.adjusted_charge(|schedule| schedule.blockhash_gas as u64)?; - let hash = self.ext.blockhash(&U256::from(args.nth::(0)?)); - self.memory.set(args.nth(1)?, &*hash)?; + let hash = self.ext.blockhash(&U256::from(args.nth_checked::(0)?)); + self.memory.set(args.nth_checked(1)?, &*hash)?; Ok(()) } @@ -565,37 +595,37 @@ impl<'a> Runtime<'a> { /// Signature: `fn coinbase(dest: *mut u8)` pub fn coinbase(&mut self, args: RuntimeArgs) -> Result<()> { 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)` pub fn difficulty(&mut self, args: RuntimeArgs) -> Result<()> { 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)` pub fn gaslimit(&mut self, args: RuntimeArgs) -> Result<()> { 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)` pub fn address(&mut self, args: RuntimeArgs) -> Result<()> { 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)` pub fn sender(&mut self, args: RuntimeArgs) -> Result<()> { 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)` pub fn origin(&mut self, args: RuntimeArgs) -> Result<()> { 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` @@ -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)` pub fn elog(&mut self, args: RuntimeArgs) -> Result<()> { - let topic_ptr: u32 = args.nth(0)?; - let topic_count: u32 = args.nth(1)?; - let data_ptr: u32 = args.nth(2)?; - let data_len: u32 = args.nth(3)?; + let topic_ptr: u32 = args.nth_checked(0)?; + let topic_count: u32 = args.nth_checked(1)?; + let data_ptr: u32 = args.nth_checked(2)?; + let data_len: u32 = args.nth_checked(3)?; if topic_count > 4 { return Err(Error::Log.into()); @@ -643,7 +673,7 @@ impl<'a> Runtime<'a> { mod ext_impl { - use wasmi::{Externals, RuntimeArgs, RuntimeValue, Error}; + use wasmi::{Externals, RuntimeArgs, RuntimeValue, Trap}; use env::ids::*; macro_rules! void { @@ -663,7 +693,7 @@ mod ext_impl { &mut self, index: usize, args: RuntimeArgs, - ) -> Result, Error> { + ) -> Result, Trap> { match index { STORAGE_WRITE_FUNC => void!(self.storage_write(args)), STORAGE_READ_FUNC => void!(self.storage_read(args)),