diff --git a/ethcore/res/wasm-tests b/ethcore/res/wasm-tests index 9a1fcbf0d..d4920761f 160000 --- a/ethcore/res/wasm-tests +++ b/ethcore/res/wasm-tests @@ -1 +1 @@ -Subproject commit 9a1fcbf0d4e73bea437577e807bc38c7ba243d80 +Subproject commit d4920761f959a785c3ec15cf2848fc75795eb2d4 diff --git a/ethcore/wasm/src/lib.rs b/ethcore/wasm/src/lib.rs index 70601144d..30ce03665 100644 --- a/ethcore/wasm/src/lib.rs +++ b/ethcore/wasm/src/lib.rs @@ -31,6 +31,7 @@ mod result; #[cfg(test)] mod tests; mod env; +mod panic_payload; const DEFAULT_STACK_SPACE: u32 = 5 * 1024 * 1024; diff --git a/ethcore/wasm/src/panic_payload.rs b/ethcore/wasm/src/panic_payload.rs new file mode 100644 index 000000000..e79c13a24 --- /dev/null +++ b/ethcore/wasm/src/panic_payload.rs @@ -0,0 +1,169 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use byteorder::{LittleEndian, ReadBytesExt}; +use std::io; +use std::io::Read; + +#[derive(Debug, PartialEq, Eq)] +pub struct PanicPayload { + pub msg: Option, + pub file: Option, + pub line: Option, + pub col: Option, +} + +fn read_string(rdr: &mut io::Cursor<&[u8]>) -> io::Result> { + let string_len = rdr.read_u32::()?; + let string = if string_len == 0 { + None + } else { + let mut content = vec![0; string_len as usize]; + rdr.read_exact(&mut content)?; + Some(String::from_utf8_lossy(&content).into_owned()) + }; + Ok(string) +} + +pub fn decode(raw: &[u8]) -> PanicPayload { + let mut rdr = io::Cursor::new(raw); + let msg = read_string(&mut rdr).ok().and_then(|x| x); + let file = read_string(&mut rdr).ok().and_then(|x| x); + let line = rdr.read_u32::().ok(); + let col = rdr.read_u32::().ok(); + PanicPayload { + msg: msg, + file: file, + line: line, + col: col, + } +} + +#[cfg(test)] +mod tests { + use super::*; + use byteorder::WriteBytesExt; + + fn write_u32(payload: &mut Vec, val: u32) { + payload.write_u32::(val).unwrap(); + } + + fn write_bytes(payload: &mut Vec, bytes: &[u8]) { + write_u32(payload, bytes.len() as u32); + payload.extend(bytes); + } + + #[test] + fn it_works() { + let mut raw = Vec::new(); + write_bytes(&mut raw, b"msg"); + write_bytes(&mut raw, b"file"); + write_u32(&mut raw, 1); + write_u32(&mut raw, 2); + + let payload = decode(&raw); + + assert_eq!( + payload, + PanicPayload { + msg: Some("msg".to_string()), + file: Some("file".to_string()), + line: Some(1), + col: Some(2), + } + ); + } + + #[test] + fn only_msg() { + let mut raw = Vec::new(); + write_bytes(&mut raw, b"msg"); + + let payload = decode(&raw); + + assert_eq!( + payload, + PanicPayload { + msg: Some("msg".to_string()), + file: None, + line: None, + col: None, + } + ); + } + + #[test] + fn invalid_utf8() { + let mut raw = Vec::new(); + write_bytes(&mut raw, b"\xF0\x90\x80msg"); + write_bytes(&mut raw, b"file"); + write_u32(&mut raw, 1); + write_u32(&mut raw, 2); + + let payload = decode(&raw); + + assert_eq!( + payload, + PanicPayload { + msg: Some("�msg".to_string()), + file: Some("file".to_string()), + line: Some(1), + col: Some(2), + } + ); + } + + #[test] + fn trailing_data() { + let mut raw = Vec::new(); + write_bytes(&mut raw, b"msg"); + write_bytes(&mut raw, b"file"); + write_u32(&mut raw, 1); + write_u32(&mut raw, 2); + write_u32(&mut raw, 0xdeadbeef); + + let payload = decode(&raw); + + assert_eq!( + payload, + PanicPayload { + msg: Some("msg".to_string()), + file: Some("file".to_string()), + line: Some(1), + col: Some(2), + } + ); + } + + #[test] + fn empty_str_is_none() { + let mut raw = Vec::new(); + write_bytes(&mut raw, b"msg"); + write_bytes(&mut raw, b""); + + let payload = decode(&raw); + + assert_eq!( + payload, + PanicPayload { + msg: Some("msg".to_string()), + file: None, + line: None, + col: None, + } + ); + } +} diff --git a/ethcore/wasm/src/runtime.rs b/ethcore/wasm/src/runtime.rs index e5a6dc910..3dfaffad0 100644 --- a/ethcore/wasm/src/runtime.rs +++ b/ethcore/wasm/src/runtime.rs @@ -21,6 +21,7 @@ use std::sync::Arc; use byteorder::{LittleEndian, ByteOrder}; use vm; +use panic_payload; use parity_wasm::interpreter; use wasm_utils::rules; use bigint::prelude::U256; @@ -626,16 +627,30 @@ impl<'a, 'b> Runtime<'a, 'b> { fn user_panic(&mut self, context: InterpreterCallerContext) -> Result, InterpreterError> { - let msg_len = context.value_stack.pop_as::()? as u32; - let msg_ptr = context.value_stack.pop_as::()? as u32; + let payload_len = context.value_stack.pop_as::()? as u32; + let payload_ptr = context.value_stack.pop_as::()? as u32; - let msg = String::from_utf8(self.memory.get(msg_ptr, msg_len as usize)?) - .map_err(|_| UserTrap::BadUtf8)?; + let raw_payload = self.memory.get(payload_ptr, payload_len as usize)?; + let payload = panic_payload::decode(&raw_payload); + let msg = format!( + "{msg}, {file}:{line}:{col}", + msg = payload + .msg + .as_ref() + .map(String::as_ref) + .unwrap_or(""), + file = payload + .file + .as_ref() + .map(String::as_ref) + .unwrap_or(""), + line = payload.line.unwrap_or(0), + col = payload.col.unwrap_or(0) + ); + trace!(target: "wasm", "Contract custom panic message: {}", msg); - trace!(target: "wasm", "Contract custom panic message: {}", msg); - - Err(UserTrap::Panic(msg).into()) - } + Err(UserTrap::Panic(msg).into()) + } fn block_hash(&mut self, context: InterpreterCallerContext) -> Result, InterpreterError> diff --git a/ethcore/wasm/src/tests.rs b/ethcore/wasm/src/tests.rs index 0a93be11c..9f8b7ddec 100644 --- a/ethcore/wasm/src/tests.rs +++ b/ethcore/wasm/src/tests.rs @@ -180,7 +180,7 @@ fn dispersion() { result, vec![0u8, 0, 125, 11, 197, 7, 255, 8, 19, 0] ); - assert_eq!(gas_left, U256::from(99_469)); + assert_eq!(gas_left, U256::from(96_961)); } #[test] @@ -208,7 +208,7 @@ fn suicide_not() { result, vec![0u8] ); - assert_eq!(gas_left, U256::from(99_724)); + assert_eq!(gas_left, U256::from(97_290)); } #[test] @@ -240,7 +240,7 @@ fn suicide() { }; assert!(ext.suicides.contains(&refund)); - assert_eq!(gas_left, U256::from(99_663)); + assert_eq!(gas_left, U256::from(97_249)); } #[test] @@ -312,7 +312,7 @@ fn call_code() { assert!(ext.calls.contains( &FakeCall { call_type: FakeCallType::Call, - gas: U256::from(98_709), + gas: U256::from(98_713), sender_address: Some(sender), receive_address: Some(receiver), value: None, @@ -324,7 +324,7 @@ fn call_code() { // siphash result let res = LittleEndian::read_u32(&result[..]); assert_eq!(res, 4198595614); - assert_eq!(gas_left, U256::from(93_851)); + assert_eq!(gas_left, U256::from(93_855)); } #[test] @@ -357,7 +357,7 @@ fn call_static() { assert!(ext.calls.contains( &FakeCall { call_type: FakeCallType::Call, - gas: U256::from(98_709), + gas: U256::from(98_713), sender_address: Some(sender), receive_address: Some(receiver), value: None, @@ -370,7 +370,7 @@ fn call_static() { let res = LittleEndian::read_u32(&result[..]); assert_eq!(res, 317632590); - assert_eq!(gas_left, U256::from(93_851)); + assert_eq!(gas_left, U256::from(93_855)); } // Realloc test @@ -393,7 +393,7 @@ fn realloc() { } }; assert_eq!(result, vec![0u8; 2]); - assert_eq!(gas_left, U256::from(99_787)); + assert_eq!(gas_left, U256::from(97_278)); } // Tests that contract's ability to read from a storage @@ -419,7 +419,7 @@ fn storage_read() { }; assert_eq!(Address::from(&result[12..32]), address); - assert_eq!(gas_left, U256::from(99_702)); + assert_eq!(gas_left, U256::from(99_706)); } // Tests keccak calculation @@ -445,7 +445,7 @@ fn keccak() { }; assert_eq!(H256::from_slice(&result), H256::from("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87")); - assert_eq!(gas_left, U256::from(84_520)); + assert_eq!(gas_left, U256::from(82_009)); } // memcpy test. @@ -477,7 +477,7 @@ fn memcpy() { }; assert_eq!(result, test_payload); - assert_eq!(gas_left, U256::from(75_324)); + assert_eq!(gas_left, U256::from(72_773)); } // memmove test. @@ -509,7 +509,7 @@ fn memmove() { }; assert_eq!(result, test_payload); - assert_eq!(gas_left, U256::from(75_324)); + assert_eq!(gas_left, U256::from(72_773)); } // memset test @@ -534,7 +534,7 @@ fn memset() { }; assert_eq!(result, vec![228u8; 8192]); - assert_eq!(gas_left, U256::from(75_324)); + assert_eq!(gas_left, U256::from(72_763)); } macro_rules! reqrep_test { @@ -591,7 +591,7 @@ fn math_add() { U256::from_dec_str("1888888888888888888888888888887").unwrap(), (&result[..]).into() ); - assert_eq!(gas_left, U256::from(98_576)); + assert_eq!(gas_left, U256::from(96_084)); } // multiplication @@ -613,7 +613,7 @@ fn math_mul() { U256::from_dec_str("888888888888888888888888888887111111111111111111111111111112").unwrap(), (&result[..]).into() ); - assert_eq!(gas_left, U256::from(97_726)); + assert_eq!(gas_left, U256::from(95_234)); } // subtraction @@ -635,7 +635,7 @@ fn math_sub() { U256::from_dec_str("111111111111111111111111111111").unwrap(), (&result[..]).into() ); - assert_eq!(gas_left, U256::from(98_568)); + assert_eq!(gas_left, U256::from(96_076)); } // subtraction with overflow @@ -677,7 +677,7 @@ fn math_div() { U256::from_dec_str("1125000").unwrap(), (&result[..]).into() ); - assert_eq!(gas_left, U256::from(91_564)); + assert_eq!(gas_left, U256::from(89_074)); } // This test checks the ability of wasm contract to invoke @@ -765,7 +765,7 @@ fn externs() { "Gas limit requested and returned does not match" ); - assert_eq!(gas_left, U256::from(97_740)); + assert_eq!(gas_left, U256::from(95_291)); } #[test] @@ -791,7 +791,7 @@ fn embedded_keccak() { }; assert_eq!(H256::from_slice(&result), H256::from("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87")); - assert_eq!(gas_left, U256::from(84_520)); + assert_eq!(gas_left, U256::from(82_009)); } /// This test checks the correctness of log extern @@ -826,5 +826,5 @@ fn events() { assert_eq!(&log_entry.data, b"gnihtemos"); assert_eq!(&result, b"gnihtemos"); - assert_eq!(gas_left, U256::from(82_721)); + assert_eq!(gas_left, U256::from(80_199)); }