Fixing possible gas-memory calculation overflows
This commit is contained in:
parent
7bb963f866
commit
24e86d4479
@ -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 {
|
||||||
let (mem_gas, new_mem_size) = self.mem_gas_cost(schedule, mem.size(), &mem_size);
|
RequiredMem::Mem(mem_size) => {
|
||||||
// Expand after calculating the cost
|
let (mem_gas, new_mem_size) = self.mem_gas_cost(schedule, mem.size(), &mem_size);
|
||||||
mem.expand(new_mem_size);
|
// Expand after calculating the cost
|
||||||
Ok(gas + mem_gas)
|
mem.expand(new_mem_size);
|
||||||
|
self.gas_add(gas, mem_gas)
|
||||||
|
},
|
||||||
|
RequiredMem::OutOfMemory => Err(evm::Error::OutOfGas)
|
||||||
},
|
},
|
||||||
InstructionCost::GasMemCopy(gas, mem_size, copy) => {
|
InstructionCost::GasMemCopy(gas, mem_size, copy) => match mem_size {
|
||||||
let (mem_gas, new_mem_size) = self.mem_gas_cost(schedule, mem.size(), &mem_size);
|
RequiredMem::Mem(mem_size) => {
|
||||||
let copy_gas = U256::from(schedule.copy_gas) * (add_u256_usize(©, 31) / U256::from(32));
|
let (mem_gas, new_mem_size) = self.mem_gas_cost(schedule, mem.size(), &mem_size);
|
||||||
// Expand after calculating the cost
|
let copy_gas = U256::from(schedule.copy_gas) * (add_u256_usize(©, 31) / U256::from(32));
|
||||||
mem.expand(new_mem_size);
|
// Expand after calculating the cost
|
||||||
Ok(gas + copy_gas + mem_gas)
|
mem.expand(new_mem_size);
|
||||||
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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");
|
||||||
|
Loading…
Reference in New Issue
Block a user