Merge pull request #7097 from paritytech/panic-payload-with-msg

WASM parse payload from panics
This commit is contained in:
Marek Kotewicz 2017-11-21 15:01:32 +01:00 committed by GitHub
commit 2650154cd7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 210 additions and 26 deletions

@ -1 +1 @@
Subproject commit 9a1fcbf0d4e73bea437577e807bc38c7ba243d80 Subproject commit d4920761f959a785c3ec15cf2848fc75795eb2d4

View File

@ -31,6 +31,7 @@ mod result;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
mod env; mod env;
mod panic_payload;
const DEFAULT_STACK_SPACE: u32 = 5 * 1024 * 1024; const DEFAULT_STACK_SPACE: u32 = 5 * 1024 * 1024;

View File

@ -0,0 +1,168 @@
// 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 <http://www.gnu.org/licenses/>.
use byteorder::{LittleEndian, ReadBytesExt};
use std::io::{self, Read};
#[derive(Debug, PartialEq, Eq)]
pub struct PanicPayload {
pub msg: Option<String>,
pub file: Option<String>,
pub line: Option<u32>,
pub col: Option<u32>,
}
fn read_string(rdr: &mut io::Cursor<&[u8]>) -> io::Result<Option<String>> {
let string_len = rdr.read_u32::<LittleEndian>()?;
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::<LittleEndian>().ok();
let col = rdr.read_u32::<LittleEndian>().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<u8>, val: u32) {
payload.write_u32::<LittleEndian>(val).unwrap();
}
fn write_bytes(payload: &mut Vec<u8>, 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("<EFBFBD>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,
}
);
}
}

View File

@ -21,6 +21,7 @@ use std::sync::Arc;
use byteorder::{LittleEndian, ByteOrder}; use byteorder::{LittleEndian, ByteOrder};
use vm; use vm;
use panic_payload;
use parity_wasm::interpreter; use parity_wasm::interpreter;
use wasm_utils::rules; use wasm_utils::rules;
use bigint::prelude::U256; use bigint::prelude::U256;
@ -626,12 +627,26 @@ impl<'a, 'b> Runtime<'a, 'b> {
fn user_panic(&mut self, context: InterpreterCallerContext) fn user_panic(&mut self, context: InterpreterCallerContext)
-> Result<Option<interpreter::RuntimeValue>, InterpreterError> -> Result<Option<interpreter::RuntimeValue>, InterpreterError>
{ {
let msg_len = context.value_stack.pop_as::<i32>()? as u32; let payload_len = context.value_stack.pop_as::<i32>()? as u32;
let msg_ptr = context.value_stack.pop_as::<i32>()? as u32; let payload_ptr = context.value_stack.pop_as::<i32>()? 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("<msg was stripped>"),
file = payload
.file
.as_ref()
.map(String::as_ref)
.unwrap_or("<unknown>"),
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())

View File

@ -180,7 +180,7 @@ fn dispersion() {
result, result,
vec![0u8, 0, 125, 11, 197, 7, 255, 8, 19, 0] 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] #[test]
@ -208,7 +208,7 @@ fn suicide_not() {
result, result,
vec![0u8] vec![0u8]
); );
assert_eq!(gas_left, U256::from(99_724)); assert_eq!(gas_left, U256::from(97_290));
} }
#[test] #[test]
@ -240,7 +240,7 @@ fn suicide() {
}; };
assert!(ext.suicides.contains(&refund)); assert!(ext.suicides.contains(&refund));
assert_eq!(gas_left, U256::from(99_663)); assert_eq!(gas_left, U256::from(97_249));
} }
#[test] #[test]
@ -312,7 +312,7 @@ fn call_code() {
assert!(ext.calls.contains( assert!(ext.calls.contains(
&FakeCall { &FakeCall {
call_type: FakeCallType::Call, call_type: FakeCallType::Call,
gas: U256::from(98_709), gas: U256::from(98_713),
sender_address: Some(sender), sender_address: Some(sender),
receive_address: Some(receiver), receive_address: Some(receiver),
value: None, value: None,
@ -324,7 +324,7 @@ fn call_code() {
// siphash result // siphash result
let res = LittleEndian::read_u32(&result[..]); let res = LittleEndian::read_u32(&result[..]);
assert_eq!(res, 4198595614); assert_eq!(res, 4198595614);
assert_eq!(gas_left, U256::from(93_851)); assert_eq!(gas_left, U256::from(93_855));
} }
#[test] #[test]
@ -357,7 +357,7 @@ fn call_static() {
assert!(ext.calls.contains( assert!(ext.calls.contains(
&FakeCall { &FakeCall {
call_type: FakeCallType::Call, call_type: FakeCallType::Call,
gas: U256::from(98_709), gas: U256::from(98_713),
sender_address: Some(sender), sender_address: Some(sender),
receive_address: Some(receiver), receive_address: Some(receiver),
value: None, value: None,
@ -370,7 +370,7 @@ fn call_static() {
let res = LittleEndian::read_u32(&result[..]); let res = LittleEndian::read_u32(&result[..]);
assert_eq!(res, 317632590); assert_eq!(res, 317632590);
assert_eq!(gas_left, U256::from(93_851)); assert_eq!(gas_left, U256::from(93_855));
} }
// Realloc test // Realloc test
@ -393,7 +393,7 @@ fn realloc() {
} }
}; };
assert_eq!(result, vec![0u8; 2]); 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 // 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!(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 // Tests keccak calculation
@ -445,7 +445,7 @@ fn keccak() {
}; };
assert_eq!(H256::from_slice(&result), H256::from("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87")); 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. // memcpy test.
@ -477,7 +477,7 @@ fn memcpy() {
}; };
assert_eq!(result, test_payload); assert_eq!(result, test_payload);
assert_eq!(gas_left, U256::from(75_324)); assert_eq!(gas_left, U256::from(72_773));
} }
// memmove test. // memmove test.
@ -509,7 +509,7 @@ fn memmove() {
}; };
assert_eq!(result, test_payload); assert_eq!(result, test_payload);
assert_eq!(gas_left, U256::from(75_324)); assert_eq!(gas_left, U256::from(72_773));
} }
// memset test // memset test
@ -534,7 +534,7 @@ fn memset() {
}; };
assert_eq!(result, vec![228u8; 8192]); 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 { macro_rules! reqrep_test {
@ -591,7 +591,7 @@ fn math_add() {
U256::from_dec_str("1888888888888888888888888888887").unwrap(), U256::from_dec_str("1888888888888888888888888888887").unwrap(),
(&result[..]).into() (&result[..]).into()
); );
assert_eq!(gas_left, U256::from(98_576)); assert_eq!(gas_left, U256::from(96_084));
} }
// multiplication // multiplication
@ -613,7 +613,7 @@ fn math_mul() {
U256::from_dec_str("888888888888888888888888888887111111111111111111111111111112").unwrap(), U256::from_dec_str("888888888888888888888888888887111111111111111111111111111112").unwrap(),
(&result[..]).into() (&result[..]).into()
); );
assert_eq!(gas_left, U256::from(97_726)); assert_eq!(gas_left, U256::from(95_234));
} }
// subtraction // subtraction
@ -635,7 +635,7 @@ fn math_sub() {
U256::from_dec_str("111111111111111111111111111111").unwrap(), U256::from_dec_str("111111111111111111111111111111").unwrap(),
(&result[..]).into() (&result[..]).into()
); );
assert_eq!(gas_left, U256::from(98_568)); assert_eq!(gas_left, U256::from(96_076));
} }
// subtraction with overflow // subtraction with overflow
@ -677,7 +677,7 @@ fn math_div() {
U256::from_dec_str("1125000").unwrap(), U256::from_dec_str("1125000").unwrap(),
(&result[..]).into() (&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 // This test checks the ability of wasm contract to invoke
@ -765,7 +765,7 @@ fn externs() {
"Gas limit requested and returned does not match" "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] #[test]
@ -791,7 +791,7 @@ fn embedded_keccak() {
}; };
assert_eq!(H256::from_slice(&result), H256::from("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87")); 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 /// This test checks the correctness of log extern
@ -826,5 +826,5 @@ fn events() {
assert_eq!(&log_entry.data, b"gnihtemos"); assert_eq!(&log_entry.data, b"gnihtemos");
assert_eq!(&result, b"gnihtemos"); assert_eq!(&result, b"gnihtemos");
assert_eq!(gas_left, U256::from(82_721)); assert_eq!(gas_left, U256::from(80_199));
} }