Add support for native contracts called through staticcall

pull/835/head
Nathan 6 years ago
parent 02c5715e8c
commit 4aa15fef28
  1. 47
      mythril/laser/ethereum/call.py
  2. 63
      mythril/laser/ethereum/instructions.py
  3. 6
      tests/native_test.py

@ -1,5 +1,8 @@
import logging
from typing import Union
from typing import Union, List
from mythril.laser.ethereum import natives
from mythril.laser.ethereum.gas import OPCODE_GAS
from mythril.laser.smt import simplify, Expression, symbol_factory
import mythril.laser.ethereum.util as util
from mythril.laser.ethereum.state.account import Account
@ -7,6 +10,7 @@ from mythril.laser.ethereum.state.calldata import (
CalldataType,
SymbolicCalldata,
ConcreteCalldata,
BaseCalldata,
)
from mythril.laser.ethereum.state.global_state import GlobalState
from mythril.support.loader import DynLoader
@ -189,3 +193,44 @@ def get_call_data(
call_data = SymbolicCalldata("{}_internalcall".format(transaction_id))
return call_data, call_data_type
def native_call(
global_state: GlobalState,
callee_address: str,
call_data: BaseCalldata,
memory_out_offset: Union[int, Expression],
memory_out_size: Union[int, Expression],
) -> List[GlobalState]:
log.debug("Native contract called: " + callee_address)
try:
mem_out_start = util.get_concrete_int(memory_out_offset)
mem_out_sz = util.get_concrete_int(memory_out_size)
except TypeError:
log.debug("CALL with symbolic start or offset not supported")
return [global_state]
contract_list = ["ecrecover", "sha256", "ripemd160", "identity"]
call_address_int = int(callee_address, 16)
native_gas_min, native_gas_max = OPCODE_GAS["NATIVE_COST"](
global_state.mstate.calculate_extension_size(mem_out_start, mem_out_sz),
contract_list[call_address_int - 1],
)
global_state.mstate.min_gas_used += native_gas_min
global_state.mstate.max_gas_used += native_gas_max
global_state.mstate.mem_extend(mem_out_start, mem_out_sz)
try:
data = natives.native_contracts(call_address_int, call_data)
except natives.NativeContractException:
for i in range(mem_out_sz):
global_state.mstate.memory[mem_out_start + i] = global_state.new_bitvec(
contract_list[call_address_int - 1] + "(" + str(call_data) + ")", 8
)
return [global_state]
for i in range(
min(len(data), mem_out_sz)
): # If more data is used then it's chopped off
global_state.mstate.memory[mem_out_start + i] = data[i]
# TODO: maybe use BitVec here constrained to 1
return [global_state]

@ -27,10 +27,9 @@ from mythril.laser.smt import (
)
from mythril.laser.smt import symbol_factory
import mythril.laser.ethereum.natives as natives
import mythril.laser.ethereum.util as helper
from mythril.laser.ethereum import util
from mythril.laser.ethereum.call import get_call_parameters
from mythril.laser.ethereum.call import get_call_parameters, native_call
from mythril.laser.ethereum.evm_exceptions import (
VmException,
StackUnderflowException,
@ -40,7 +39,6 @@ from mythril.laser.ethereum.evm_exceptions import (
)
from mythril.laser.ethereum.gas import OPCODE_GAS
from mythril.laser.ethereum.keccak import KeccakFunctionManager
from mythril.laser.ethereum.state.calldata import CalldataType
from mythril.laser.ethereum.state.global_state import GlobalState
from mythril.laser.ethereum.transaction import (
MessageCallTransaction,
@ -49,7 +47,6 @@ from mythril.laser.ethereum.transaction import (
)
from mythril.support.loader import DynLoader
from mythril.analysis.solver import get_model
log = logging.getLogger(__name__)
@ -1297,48 +1294,13 @@ class Instruction:
)
if 0 < int(callee_address, 16) < 5:
log.debug("Native contract called: " + callee_address)
if call_data == [] and call_data_type == CalldataType.SYMBOLIC:
log.debug("CALL with symbolic data not supported")
return [global_state]
try:
mem_out_start = helper.get_concrete_int(memory_out_offset)
mem_out_sz = helper.get_concrete_int(memory_out_size)
except TypeError:
log.debug("CALL with symbolic start or offset not supported")
return [global_state]
contract_list = ["ecrecover", "sha256", "ripemd160", "identity"]
call_address_int = int(callee_address, 16)
native_gas_min, native_gas_max = OPCODE_GAS["NATIVE_COST"](
global_state.mstate.calculate_extension_size(mem_out_start, mem_out_sz),
contract_list[call_address_int - 1],
return native_call(
global_state,
callee_address,
call_data,
memory_out_offset,
memory_out_size,
)
global_state.mstate.min_gas_used += native_gas_min
global_state.mstate.max_gas_used += native_gas_max
global_state.mstate.mem_extend(mem_out_start, mem_out_sz)
try:
data = natives.native_contracts(call_address_int, call_data)
except natives.NativeContractException:
for i in range(mem_out_sz):
global_state.mstate.memory[
mem_out_start + i
] = global_state.new_bitvec(
contract_list[call_address_int - 1]
+ "("
+ str(call_data)
+ ")",
8,
)
return [global_state]
for i in range(
min(len(data), mem_out_sz)
): # If more data is used then it's chopped off
global_state.mstate.memory[mem_out_start + i] = data[i]
# TODO: maybe use BitVec here constrained to 1
return [global_state]
transaction = MessageCallTransaction(
world_state=global_state.world_state,
@ -1610,7 +1572,7 @@ class Instruction:
# TODO: implement me
instr = global_state.get_current_instruction()
try:
callee_address, callee_account, call_data, value, call_data_type, gas, _, _ = get_call_parameters(
callee_address, callee_account, call_data, value, call_data_type, gas, memory_out_offset, memory_out_size = get_call_parameters(
global_state, self.dynamic_loader
)
except ValueError as e:
@ -1627,4 +1589,13 @@ class Instruction:
global_state.mstate.stack.append(
global_state.new_bitvec("retval_" + str(instr["address"]), 256)
)
if 0 < int(callee_address, 16) < 5:
return native_call(
global_state,
callee_address,
call_data,
memory_out_offset,
memory_out_size,
)
return [global_state]

@ -14,12 +14,12 @@ IDENTITY_TEST = [(0, False) for _ in range(2)]
# These are Random numbers to check whether the 'if condition' is entered or not
# (True means entered)
SHA256_TEST[0] = (hex(5555555555555555), True)
SHA256_TEST[1] = (hex(323232325445454546), True)
SHA256_TEST[1] = (hex(323232325445454546), False)
SHA256_TEST[2] = (hex(34756834765834658), True)
SHA256_TEST[3] = (hex(8756476956956795876987), True)
SHA256_TEST[3] = (hex(8756476956956795876987), False)
RIPEMD160_TEST[0] = (hex(999999999999999999993), True)
RIPEMD160_TEST[1] = (hex(1111111111112), True)
RIPEMD160_TEST[1] = (hex(1111111111112), False)
ECRECOVER_TEST[0] = (hex(674837568743979857398564869), True)
ECRECOVER_TEST[1] = (hex(3487683476979311), False)

Loading…
Cancel
Save