Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

WASM parse payload from panics #7097

Merged
merged 3 commits into from
Nov 21, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ethcore/res/wasm-tests
1 change: 1 addition & 0 deletions ethcore/wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ mod result;
#[cfg(test)]
mod tests;
mod env;
mod panic_payload;

const DEFAULT_STACK_SPACE: u32 = 5 * 1024 * 1024;

Expand Down
168 changes: 168 additions & 0 deletions ethcore/wasm/src/panic_payload.rs
Original file line number Diff line number Diff line change
@@ -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("�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,
}
);
}
}
27 changes: 21 additions & 6 deletions ethcore/wasm/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -626,12 +627,26 @@ impl<'a, 'b> Runtime<'a, 'b> {
fn user_panic(&mut self, context: InterpreterCallerContext)
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
{
let msg_len = context.value_stack.pop_as::<i32>()? as u32;
let msg_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 payload_len = context.value_stack.pop_as::<i32>()? as u32;
let payload_ptr = context.value_stack.pop_as::<i32>()? as u32;

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);

Err(UserTrap::Panic(msg).into())
Expand Down
40 changes: 20 additions & 20 deletions ethcore/wasm/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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,
Expand All @@ -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]
Expand Down Expand Up @@ -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,
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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 {
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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]
Expand All @@ -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
Expand Down Expand Up @@ -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));
}