Fixing possible gas-memory calculation overflows

This commit is contained in:
Tomusdrw 2016-01-15 21:46:08 +01:00
parent 7bb963f866
commit 24e86d4479
2 changed files with 66 additions and 23 deletions

View File

@ -165,11 +165,17 @@ impl<'a> CodeReader<'a> {
} }
} }
enum RequiredMem {
Mem(U256),
OutOfMemory
}
enum InstructionCost { enum InstructionCost {
Gas(U256), Gas(U256),
GasMem(U256, U256), GasMem(U256, RequiredMem),
GasMemCopy(U256, U256, U256) GasMemCopy(U256, RequiredMem, U256)
} }
enum InstructionResult { enum InstructionResult {
AdditionalGasCost(U256), AdditionalGasCost(U256),
JumpToPosition(U256), JumpToPosition(U256),
@ -279,13 +285,13 @@ impl Interpreter {
InstructionCost::Gas(U256::from(schedule.sload_gas)) InstructionCost::Gas(U256::from(schedule.sload_gas))
}, },
instructions::MSTORE => { instructions::MSTORE => {
InstructionCost::GasMem(default_gas, add_u256_usize(stack.peek(0), 32)) InstructionCost::GasMem(default_gas, self.mem_needed_const(stack.peek(0), 32))
}, },
instructions::MLOAD => { instructions::MLOAD => {
InstructionCost::GasMem(default_gas, add_u256_usize(stack.peek(0), 32)) InstructionCost::GasMem(default_gas, self.mem_needed_const(stack.peek(0), 32))
}, },
instructions::MSTORE8 => { instructions::MSTORE8 => {
InstructionCost::GasMem(default_gas, add_u256_usize(stack.peek(0), 1)) InstructionCost::GasMem(default_gas, self.mem_needed_const(stack.peek(0), 1))
}, },
instructions::RETURN => { instructions::RETURN => {
InstructionCost::GasMem(default_gas, self.mem_needed(stack.peek(0), stack.peek(1))) InstructionCost::GasMem(default_gas, self.mem_needed(stack.peek(0), stack.peek(1)))
@ -311,13 +317,13 @@ impl Interpreter {
let no_of_topics = instructions::get_log_topics(instruction); let no_of_topics = instructions::get_log_topics(instruction);
let log_gas = schedule.log_gas + schedule.log_topic_gas * no_of_topics; let log_gas = schedule.log_gas + schedule.log_topic_gas * no_of_topics;
let data_gas = stack.peek(1).clone() * U256::from(schedule.log_data_gas); let data_gas = stack.peek(1).clone() * U256::from(schedule.log_data_gas);
let gas = data_gas + U256::from(log_gas); let gas = try!(self.gas_add(data_gas, U256::from(log_gas)));
InstructionCost::GasMem(gas, self.mem_needed(stack.peek(0), stack.peek(1))) InstructionCost::GasMem(gas, self.mem_needed(stack.peek(0), stack.peek(1)))
}, },
instructions::CALL | instructions::CALLCODE => { instructions::CALL | instructions::CALLCODE => {
// [todr] we actuall call gas_cost is calculated in ext // [todr] we actuall call gas_cost is calculated in ext
let gas = U256::from(schedule.call_gas); let gas = U256::from(schedule.call_gas);
let mem = cmp::max( let mem = self.mem_max(
self.mem_needed(stack.peek(5), stack.peek(6)), self.mem_needed(stack.peek(5), stack.peek(6)),
self.mem_needed(stack.peek(3), stack.peek(4)) self.mem_needed(stack.peek(3), stack.peek(4))
); );
@ -325,7 +331,8 @@ impl Interpreter {
}, },
instructions::DELEGATECALL => { instructions::DELEGATECALL => {
let gas = add_u256_usize(stack.peek(0), schedule.call_gas); let gas = add_u256_usize(stack.peek(0), schedule.call_gas);
let mem = cmp::max(
let mem = self.mem_max(
self.mem_needed(stack.peek(4), stack.peek(5)), self.mem_needed(stack.peek(4), stack.peek(5)),
self.mem_needed(stack.peek(2), stack.peek(3)) self.mem_needed(stack.peek(2), stack.peek(3))
); );
@ -349,22 +356,35 @@ impl Interpreter {
InstructionCost::Gas(gas) => { InstructionCost::Gas(gas) => {
Ok(gas) Ok(gas)
}, },
InstructionCost::GasMem(gas, mem_size) => { InstructionCost::GasMem(gas, mem_size) => match mem_size {
RequiredMem::Mem(mem_size) => {
let (mem_gas, new_mem_size) = self.mem_gas_cost(schedule, mem.size(), &mem_size); let (mem_gas, new_mem_size) = self.mem_gas_cost(schedule, mem.size(), &mem_size);
// Expand after calculating the cost // Expand after calculating the cost
mem.expand(new_mem_size); mem.expand(new_mem_size);
Ok(gas + mem_gas) self.gas_add(gas, mem_gas)
}, },
InstructionCost::GasMemCopy(gas, mem_size, copy) => { RequiredMem::OutOfMemory => Err(evm::Error::OutOfGas)
},
InstructionCost::GasMemCopy(gas, mem_size, copy) => match mem_size {
RequiredMem::Mem(mem_size) => {
let (mem_gas, new_mem_size) = self.mem_gas_cost(schedule, mem.size(), &mem_size); let (mem_gas, new_mem_size) = self.mem_gas_cost(schedule, mem.size(), &mem_size);
let copy_gas = U256::from(schedule.copy_gas) * (add_u256_usize(&copy, 31) / U256::from(32)); let copy_gas = U256::from(schedule.copy_gas) * (add_u256_usize(&copy, 31) / U256::from(32));
// Expand after calculating the cost // Expand after calculating the cost
mem.expand(new_mem_size); mem.expand(new_mem_size);
Ok(gas + copy_gas + mem_gas) self.gas_add(try!(self.gas_add(gas, copy_gas)), mem_gas)
},
RequiredMem::OutOfMemory => Err(evm::Error::OutOfGas)
} }
} }
} }
fn gas_add(&self, a: U256, b: U256) -> Result<U256, evm::Error> {
match a.overflowing_add(b) {
(_val, true) => Err(evm::Error::OutOfGas),
(val, false) => Ok(val)
}
}
fn mem_gas_cost(&self, schedule: &evm::Schedule, current_mem_size: usize, mem_size: &U256) -> (U256, usize) { fn mem_gas_cost(&self, schedule: &evm::Schedule, current_mem_size: usize, mem_size: &U256) -> (U256, usize) {
let gas_for_mem = |mem_size: usize| { let gas_for_mem = |mem_size: usize| {
let s = mem_size / 32; let s = mem_size / 32;
@ -384,11 +404,34 @@ impl Interpreter {
} }
fn mem_needed(&self, offset: &U256, size: &U256) -> U256 { fn mem_max(&self, m_a: RequiredMem, m_b: RequiredMem) -> RequiredMem {
match (m_a, m_b) {
(RequiredMem::Mem(a), RequiredMem::Mem(b)) => {
RequiredMem::Mem(cmp::max(a, b))
},
(RequiredMem::OutOfMemory, _) | (_, RequiredMem::OutOfMemory) => {
RequiredMem::OutOfMemory
}
}
}
fn mem_needed_const(&self, mem: &U256, add: usize) -> RequiredMem {
match mem.overflowing_add(U256::from(add)) {
(_, true) => RequiredMem::OutOfMemory,
(mem, false) => RequiredMem::Mem(mem)
}
}
fn mem_needed(&self, offset: &U256, size: &U256) -> RequiredMem {
if self.is_zero(size) { if self.is_zero(size) {
U256::zero() RequiredMem::Mem(U256::zero())
} else { } else {
offset.clone() + size.clone() let (result, overflow) = offset.clone().overflowing_add(size.clone());
if overflow {
RequiredMem::OutOfMemory
} else {
RequiredMem::Mem(result)
}
} }
} }

View File

@ -242,7 +242,7 @@ fn do_json_test_for(vm: &VMType, json_data: &[u8]) -> Vec<String> {
match res { match res {
Err(_) => fail_unless(out_of_gas, "didn't expect to run out of gas."), Err(_) => fail_unless(out_of_gas, "didn't expect to run out of gas."),
Ok(gas_left) => { Ok(gas_left) => {
//println!("name: {}, gas_left : {:?}, expected: {:?}", name, gas_left, U256::from(&test["gas"])); println!("name: {}, gas_left : {:?}", name, gas_left);
fail_unless(!out_of_gas, "expected to run out of gas."); fail_unless(!out_of_gas, "expected to run out of gas.");
fail_unless(gas_left == xjson!(&test["gas"]), "gas_left is incorrect"); fail_unless(gas_left == xjson!(&test["gas"]), "gas_left is incorrect");
fail_unless(output == Bytes::from_json(&test["out"]), "output is incorrect"); fail_unless(output == Bytes::from_json(&test["out"]), "output is incorrect");