more dos protection (#8104)

This commit is contained in:
Nikolay Volf 2018-03-14 15:27:56 +03:00 committed by GitHub
parent 113c35af0a
commit 1bad20ae38
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 42 additions and 24 deletions

View File

@ -135,6 +135,8 @@ pub struct WasmCosts {
pub initial_mem: u32, pub initial_mem: u32,
/// Grow memory cost, per page (64kb) /// Grow memory cost, per page (64kb)
pub grow_mem: u32, pub grow_mem: u32,
/// Memory copy cost, per byte
pub memcpy: u32,
/// Max stack height (native WebAssembly stack limiter) /// Max stack height (native WebAssembly stack limiter)
pub max_stack_height: u32, pub max_stack_height: u32,
/// Cost of wasm opcode is calculated as TABLE_ENTRY_COST * `opcodes_mul` / `opcodes_div` /// Cost of wasm opcode is calculated as TABLE_ENTRY_COST * `opcodes_mul` / `opcodes_div`
@ -154,6 +156,7 @@ impl Default for WasmCosts {
static_address: 40, static_address: 40,
initial_mem: 4096, initial_mem: 4096,
grow_mem: 8192, grow_mem: 8192,
memcpy: 1,
max_stack_height: 64*1024, max_stack_height: 64*1024,
opcodes_mul: 3, opcodes_mul: 3,
opcodes_div: 8, opcodes_div: 8,

View File

@ -77,6 +77,12 @@ impl From<runtime::Error> for vm::Error {
} }
} }
enum ExecutionOutcome {
Suicide,
Return,
NotSpecial,
}
impl vm::Vm for WasmInterpreter { impl vm::Vm for WasmInterpreter {
fn exec(&mut self, params: ActionParams, ext: &mut vm::Ext) -> vm::Result<GasLeft> { fn exec(&mut self, params: ActionParams, ext: &mut vm::Ext) -> vm::Result<GasLeft> {
@ -130,22 +136,24 @@ impl vm::Vm for WasmInterpreter {
let invoke_result = module_instance.invoke_export("call", &[], &mut runtime); let invoke_result = module_instance.invoke_export("call", &[], &mut runtime);
let mut suicide = false; let mut execution_outcome = ExecutionOutcome::NotSpecial;
if let Err(InterpreterError::Trap(ref trap)) = invoke_result { if let Err(InterpreterError::Trap(ref trap)) = invoke_result {
if let wasmi::TrapKind::Host(ref boxed) = *trap.kind() { if let wasmi::TrapKind::Host(ref boxed) = *trap.kind() {
let ref runtime_err = boxed.downcast_ref::<runtime::Error>() let ref runtime_err = boxed.downcast_ref::<runtime::Error>()
.expect("Host errors other than runtime::Error never produced; qed"); .expect("Host errors other than runtime::Error never produced; qed");
if let runtime::Error::Suicide = **runtime_err { suicide = true; } match **runtime_err {
runtime::Error::Suicide => { execution_outcome = ExecutionOutcome::Suicide; },
runtime::Error::Return => { execution_outcome = ExecutionOutcome::Return; },
_ => {}
}
} }
} }
if !suicide { if let (ExecutionOutcome::NotSpecial, Err(e)) = (execution_outcome, invoke_result) {
if let Err(e) = invoke_result {
trace!(target: "wasm", "Error executing contract: {:?}", e); trace!(target: "wasm", "Error executing contract: {:?}", e);
return Err(vm::Error::from(Error::from(e))); return Err(vm::Error::from(Error::from(e)));
} }
}
( (
runtime.gas_left().expect("Cannot fail since it was not updated since last charge"), runtime.gas_left().expect("Cannot fail since it was not updated since last charge"),

View File

@ -32,6 +32,8 @@ pub enum Error {
MemoryAccessViolation, MemoryAccessViolation,
/// Native code resulted in suicide /// Native code resulted in suicide
Suicide, Suicide,
/// Native code requested execution to finish
Return,
/// Suicide was requested but coudn't complete /// Suicide was requested but coudn't complete
SuicideAbort, SuicideAbort,
/// Invalid gas state inside interpreter /// Invalid gas state inside interpreter
@ -103,6 +105,7 @@ impl ::std::fmt::Display for Error {
Error::InvalidGasState => write!(f, "Invalid gas state"), Error::InvalidGasState => write!(f, "Invalid gas state"),
Error::BalanceQueryError => write!(f, "Balance query resulted in an error"), Error::BalanceQueryError => write!(f, "Balance query resulted in an error"),
Error::Suicide => write!(f, "Suicide result"), Error::Suicide => write!(f, "Suicide result"),
Error::Return => write!(f, "Return result"),
Error::Unknown => write!(f, "Unknown runtime function invoked"), Error::Unknown => write!(f, "Unknown runtime function invoked"),
Error::AllocationFailed => write!(f, "Memory allocation failed (OOM)"), Error::AllocationFailed => write!(f, "Memory allocation failed (OOM)"),
Error::BadUtf8 => write!(f, "String encoding is bad utf-8 sequence"), Error::BadUtf8 => write!(f, "String encoding is bad utf-8 sequence"),
@ -289,7 +292,7 @@ impl<'a> Runtime<'a> {
self.result = self.memory.get(ptr, len as usize)?; self.result = self.memory.get(ptr, len as usize)?;
Ok(()) Err(Error::Return)
} }
/// Destroy the runtime, returning currently recorded result of the execution /// Destroy the runtime, returning currently recorded result of the execution
@ -321,6 +324,10 @@ impl<'a> Runtime<'a> {
/// Write input bytes to the memory location using the passed pointer /// Write input bytes to the memory location using the passed pointer
fn fetch_input(&mut self, args: RuntimeArgs) -> Result<()> { fn fetch_input(&mut self, args: RuntimeArgs) -> Result<()> {
let ptr: u32 = args.nth_checked(0)?; let ptr: u32 = args.nth_checked(0)?;
let args_len = self.args.len() as u64;
self.charge(|s| args_len * s.wasm().memcpy as u64)?;
self.memory.set(ptr, &self.args[..])?; self.memory.set(ptr, &self.args[..])?;
Ok(()) Ok(())
} }

View File

@ -207,7 +207,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(93_972)); assert_eq!(gas_left, U256::from(94_013));
} }
#[test] #[test]
@ -235,7 +235,7 @@ fn suicide_not() {
result, result,
vec![0u8] vec![0u8]
); );
assert_eq!(gas_left, U256::from(94_970)); assert_eq!(gas_left, U256::from(94_984));
} }
#[test] #[test]
@ -267,7 +267,7 @@ fn suicide() {
}; };
assert!(ext.suicides.contains(&refund)); assert!(ext.suicides.contains(&refund));
assert_eq!(gas_left, U256::from(94_933)); assert_eq!(gas_left, U256::from(94_925));
} }
#[test] #[test]
@ -297,7 +297,7 @@ fn create() {
assert!(ext.calls.contains( assert!(ext.calls.contains(
&FakeCall { &FakeCall {
call_type: FakeCallType::Create, call_type: FakeCallType::Create,
gas: U256::from(60_917), gas: U256::from(60_914),
sender_address: None, sender_address: None,
receive_address: None, receive_address: None,
value: Some(1_000_000_000.into()), value: Some(1_000_000_000.into()),
@ -305,7 +305,7 @@ fn create() {
code_address: None, code_address: None,
} }
)); ));
assert_eq!(gas_left, U256::from(60_903)); assert_eq!(gas_left, U256::from(60_900));
} }
#[test] #[test]
@ -465,7 +465,7 @@ fn realloc() {
} }
}; };
assert_eq!(result, vec![0u8; 2]); assert_eq!(result, vec![0u8; 2]);
assert_eq!(gas_left, U256::from(94_352)); assert_eq!(gas_left, U256::from(94_372));
} }
#[test] #[test]
@ -541,7 +541,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_223)); assert_eq!(gas_left, U256::from(84_240));
} }
// math_* tests check the ability of wasm contract to perform big integer operations // math_* tests check the ability of wasm contract to perform big integer operations
@ -570,7 +570,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(93_818)); assert_eq!(gas_left, U256::from(93_814));
} }
// multiplication // multiplication
@ -592,7 +592,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(93_304)); assert_eq!(gas_left, U256::from(93_300));
} }
// subtraction // subtraction
@ -614,7 +614,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(93_831)); assert_eq!(gas_left, U256::from(93_826));
} }
// subtraction with overflow // subtraction with overflow
@ -656,7 +656,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(90_607)); assert_eq!(gas_left, U256::from(90_603));
} }
#[test] #[test]
@ -684,7 +684,7 @@ fn storage_metering() {
}; };
// 0 -> not 0 // 0 -> not 0
assert_eq!(gas_left, U256::from(74_410)); assert_eq!(gas_left, U256::from(74_338));
// #2 // #2
@ -703,7 +703,7 @@ fn storage_metering() {
}; };
// not 0 -> not 0 // not 0 -> not 0
assert_eq!(gas_left, U256::from(89_410)); assert_eq!(gas_left, U256::from(89_338));
} }
// This test checks the ability of wasm contract to invoke // This test checks the ability of wasm contract to invoke
@ -791,7 +791,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(92_089)); assert_eq!(gas_left, U256::from(92_110));
} }
#[test] #[test]
@ -817,7 +817,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_223)); assert_eq!(gas_left, U256::from(84_240));
} }
/// This test checks the correctness of log extern /// This test checks the correctness of log extern
@ -852,5 +852,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(81_235)); assert_eq!(gas_left, U256::from(81_292));
} }