From e8474df6fb3efb9897351874e962d03294ed4b5e Mon Sep 17 00:00:00 2001 From: Nikolay Igotti Date: Tue, 16 Jun 2020 17:00:19 +0300 Subject: [PATCH] Error processing. --- runtime/near-vm-runner/src/wasmtime_runner.rs | 97 ++++++++++++++++--- .../near-vm-runner/tests/test_error_cases.rs | 17 ++-- 2 files changed, 93 insertions(+), 21 deletions(-) diff --git a/runtime/near-vm-runner/src/wasmtime_runner.rs b/runtime/near-vm-runner/src/wasmtime_runner.rs index 81d1d473f7f..04e1a6555c3 100644 --- a/runtime/near-vm-runner/src/wasmtime_runner.rs +++ b/runtime/near-vm-runner/src/wasmtime_runner.rs @@ -1,12 +1,13 @@ use crate::errors::IntoVMError; use crate::{imports, prepare}; use near_runtime_fees::RuntimeFeesConfig; -use near_vm_errors::FunctionCallError::LinkError; +use near_vm_errors::FunctionCallError::{LinkError, WasmUnknownError}; use near_vm_errors::{FunctionCallError, MethodResolveError, VMError, VMLogicError}; use near_vm_logic::types::PromiseResult; use near_vm_logic::{External, MemoryLike, VMConfig, VMContext, VMLogic, VMOutcome}; use std::ffi::c_void; use std::str; +use wasmtime::ExternType::Func; use wasmtime::{Engine, Limits, Linker, Memory, MemoryType, Module, Store}; pub struct WasmtimeMemory(Memory); @@ -59,28 +60,42 @@ impl MemoryLike for WasmtimeMemory { } } +fn trap_to_error(trap: &wasmtime::Trap) -> VMError { + if trap.i32_exit_status() == Some(239) { + match imports::last_wasmtime_error() { + Some(VMLogicError::HostError(h)) => { + VMError::FunctionCallError(FunctionCallError::HostError(h.clone())) + } + Some(VMLogicError::ExternalError(s)) => VMError::ExternalError(s.clone()), + Some(VMLogicError::InconsistentStateError(e)) => { + VMError::InconsistentStateError(e.clone()) + } + None => panic!("Error is not properly set"), + } + } else { + // VMError::FunctionCallError(LinkError { msg: format!("{:#?}", trap) }) + VMError::FunctionCallError(WasmUnknownError) + } +} + impl IntoVMError for anyhow::Error { fn into_vm_error(self) -> VMError { // TODO: incorrect - VMError::FunctionCallError(LinkError { msg: format!("{:#?}", self) }) + let cause = self.root_cause(); + match cause.downcast_ref::() { + Some(trap) => trap_to_error(trap), + None => VMError::FunctionCallError(LinkError { msg: format!("{:#?}", cause) }), + } } } impl IntoVMError for wasmtime::Trap { fn into_vm_error(self) -> VMError { if self.i32_exit_status() == Some(239) { - match imports::last_wasmtime_error() { - Some(VMLogicError::HostError(h)) => { - VMError::FunctionCallError(FunctionCallError::HostError(h.clone())) - } - Some(VMLogicError::ExternalError(s)) => VMError::ExternalError(s.clone()), - Some(VMLogicError::InconsistentStateError(e)) => { - VMError::InconsistentStateError(e.clone()) - } - None => panic!("Error is not properly set"), - } + trap_to_error(&self) } else { - VMError::FunctionCallError(LinkError { msg: format!("{:#?}", self) }) + // VMError::FunctionCallError(LinkError { msg: format!("{:#?}", self) }) + VMError::FunctionCallError(WasmUnknownError) } } } @@ -103,7 +118,10 @@ pub fn run_wasmtime<'a>( wasm_config.limit_config.max_memory_pages, ) .unwrap(); - let prepared_code = prepare::prepare_contract(code, wasm_config).unwrap(); + let prepared_code = match prepare::prepare_contract(code, wasm_config) { + Ok(code) => code, + Err(err) => return (None, Some(VMError::from(err))), + }; let module = Module::new(&engine, prepared_code).unwrap(); // Note that we don't clone the actual backing memory, just increase the RC. let memory_copy = memory.clone(); @@ -114,8 +132,57 @@ pub fn run_wasmtime<'a>( // lifetimes of the logic instance and pass raw pointers here. let raw_logic = &mut logic as *mut _ as *mut c_void; imports::link_wasmtime(&mut linker, memory_copy, raw_logic); + let func_name = match str::from_utf8(method_name) { + Ok(name) => name, + Err(_) => { + return ( + None, + Some(VMError::FunctionCallError(FunctionCallError::MethodResolveError( + MethodResolveError::MethodUTF8Error, + ))), + ) + } + }; + if method_name.is_empty() { + return ( + None, + Some(VMError::FunctionCallError(FunctionCallError::MethodResolveError( + MethodResolveError::MethodEmptyName, + ))), + ); + } + match module.get_export(func_name) { + Some(export) => match export { + Func(func_type) => { + if !func_type.params().is_empty() || !func_type.results().is_empty() { + return ( + None, + Some(VMError::FunctionCallError(FunctionCallError::MethodResolveError( + MethodResolveError::MethodInvalidSignature, + ))), + ); + } + } + _ => { + return ( + None, + Some(VMError::FunctionCallError(FunctionCallError::MethodResolveError( + MethodResolveError::MethodNotFound, + ))), + ) + } + }, + None => { + return ( + None, + Some(VMError::FunctionCallError(FunctionCallError::MethodResolveError( + MethodResolveError::MethodNotFound, + ))), + ) + } + } match linker.instantiate(&module) { - Ok(instance) => match instance.get_func(str::from_utf8(method_name).unwrap()) { + Ok(instance) => match instance.get_func(func_name) { Some(func) => match func.get0::<()>() { Ok(run) => match run() { Ok(_) => (Some(logic.outcome()), None), diff --git a/runtime/near-vm-runner/tests/test_error_cases.rs b/runtime/near-vm-runner/tests/test_error_cases.rs index a00a804ce61..863df50a318 100644 --- a/runtime/near-vm-runner/tests/test_error_cases.rs +++ b/runtime/near-vm-runner/tests/test_error_cases.rs @@ -348,26 +348,31 @@ fn test_bad_import_2() { #[test] fn test_bad_import_3() { + let msg = match VMKind::default() { + VMKind::Wasmer => "link error: Incorrect import type, namespace: env, name: input, expected type: global, found type: function", + VMKind::Wasmtime => "\"incompatible import type for `env::input` specified\\ndesired signature was: Global(GlobalType { content: I32, mutability: Const })\\nsignatures available:\\n\\n * Func(FuncType { params: [I64], results: [] })\\n\"", + }.to_string(); assert_eq!( make_simple_contract_call(&bad_import_global("env"), b"hello"), ( Some(vm_outcome_with_gas(0)), - Some(VMError::FunctionCallError(FunctionCallError::LinkError{ - msg: "link error: Incorrect import type, namespace: env, name: input, expected type: global, found type: function".to_string() - })) + Some(VMError::FunctionCallError(FunctionCallError::LinkError { msg: msg })) ) ); } #[test] fn test_bad_import_4() { + let msg = match VMKind::default() { + VMKind::Wasmer => "link error: Import not found, namespace: env, name: wtf", + VMKind::Wasmtime => "\"unknown import: `env::wtf` has not been defined\"", + } + .to_string(); assert_eq!( make_simple_contract_call(&bad_import_func("env"), b"hello"), ( Some(vm_outcome_with_gas(0)), - Some(VMError::FunctionCallError(FunctionCallError::LinkError { - msg: "link error: Import not found, namespace: env, name: wtf".to_string() - })) + Some(VMError::FunctionCallError(FunctionCallError::LinkError { msg: msg })) ) ); }