From a9755b8c67ee6a6fc5f782f3d31c0d703235585b Mon Sep 17 00:00:00 2001 From: Eric N Date: Sun, 15 Sep 2019 23:50:14 -0700 Subject: [PATCH] Modified codecopy. Debugging issue with contract function calls. --- mythril/laser/ethereum/gas.py | 2 +- mythril/laser/ethereum/instructions.py | 171 +++++++++++-------------- 2 files changed, 77 insertions(+), 96 deletions(-) diff --git a/mythril/laser/ethereum/gas.py b/mythril/laser/ethereum/gas.py index 4d8de488..0a0c0a38 100644 --- a/mythril/laser/ethereum/gas.py +++ b/mythril/laser/ethereum/gas.py @@ -180,7 +180,7 @@ OPCODE_GAS = { "LOG3": (4 * 375, 4 * 375 + 8 * 32), "LOG4": (5 * 375, 5 * 375 + 8 * 32), "CREATE": (32000, 32000), - "CREATE2": (32000, 32000), # TODO: Make the gas value is dynamic + "CREATE2": (32000, 32000), # TODO: Make the gas values dynamic "CALL": (700, 700 + 9000 + 25000), "NATIVE_COST": calculate_native_gas, "CALLCODE": (700, 700 + 9000 + 25000), diff --git a/mythril/laser/ethereum/instructions.py b/mythril/laser/ethereum/instructions.py index e5c62f2a..903fe1ec 100644 --- a/mythril/laser/ethereum/instructions.py +++ b/mythril/laser/ethereum/instructions.py @@ -50,6 +50,7 @@ from mythril.laser.ethereum.transaction import ( MessageCallTransaction, TransactionStartSignal, ContractCreationTransaction, + get_next_transaction_id, ) from mythril.support.support_utils import get_code_hash @@ -939,13 +940,13 @@ class Instruction: if isinstance(global_state.current_transaction, ContractCreationTransaction): # Hacky way to ensure constructor arguments work - Pick some reasonably large size. no_of_bytes = len(disassembly.bytecode) // 2 - if isinstance(calldata, ConcreteCalldata): - no_of_bytes += calldata.size - else: - no_of_bytes += 0x200 # space for 16 32-byte arguments - global_state.mstate.constraints.append( - global_state.environment.calldata.size == no_of_bytes - ) + # if isinstance(calldata, ConcreteCalldata): + # no_of_bytes += calldata.size + 5000 + # else: + no_of_bytes += 0x200 # space for 16 32-byte arguments + # global_state.mstate.constraints.append( + # global_state.environment.calldata.size == no_of_bytes + # ) else: no_of_bytes = len(disassembly.bytecode) // 2 @@ -1096,7 +1097,6 @@ class Instruction: global_state, global_state.mstate, memory_offset, offset, size ) else: - # creation code and arguments may be in same object. # Copy from both code and calldata appropriately. state = global_state.mstate environment = global_state.environment @@ -1105,80 +1105,67 @@ class Instruction: concrete_size = helper.get_concrete_int(size) calldata = global_state.environment.calldata - calldata_size = helper.get_concrete_int(calldata.size) - - total_size = code_size + calldata_size - - if concrete_size > 0: - # borrow some logic from codecopyhelper and calldatacopyhelper - try: - state.mem_extend(concrete_memory_offset, concrete_size) - except TypeError as e: - log.debug("Memory allocation error: {}".format(e)) - state.mem_extend(concrete_memory_offset, 1) - state.memory[mstart] = global_state.new_bitvec( - "calldata_" - + str(environment.active_account.contract_name) - + "[" - + str(offset) - + ": + " - + str(concrete_size) - + "]", - 8, + + code_copy_offset = concrete_code_offset + code_copy_size = ( + concrete_size + if concrete_code_offset + concrete_size <= code_size + else code_size - concrete_code_offset + ) + code_copy_size = code_copy_size if code_copy_size >= 0 else 0 + + calldata_copy_offset = ( + concrete_code_offset - code_size + if concrete_code_offset - code_size > 0 + else 0 + ) + calldata_copy_size = concrete_code_offset + concrete_size - code_size + calldata_copy_size = ( + calldata_copy_size if calldata_copy_size >= 0 else 0 + ) + + try: + global_state.mstate.mem_extend( + concrete_memory_offset, concrete_size + calldata_copy_size + ) + except TypeError: + global_state.mstate.mem_extend(concrete_memory_offset, 1) + global_state.mstate.memory[ + concrete_memory_offset + ] = global_state.new_bitvec( + "code({})".format( + global_state.environment.active_account.contract_name + ), + 8, + ) + return [global_state] + + for i in range(code_copy_size + calldata_copy_size): + if i < code_copy_size: + global_state.mstate.memory[concrete_memory_offset + i] = int( + code[ + 2 + * (code_copy_offset + i) : 2 + * (code_copy_offset + i + 1) + ], + 16, ) - return [global_state] - - try: - i_data = code_size - new_memory = [] - for i in range(concrete_size): - if concrete_code_offset + i < code_size: - # copy from code - state.memory[concrete_memory_offset + i] = int( - code[ - 2 - * (concrete_code_offset + i) : 2 - * (concrete_code_offset + i + 1) - ], - 16, - ) - elif concrete_code_offset + i < total_size: - # copy from calldata - value = environment.calldata[i_data] - new_memory.append(value) - - i_data = ( - i_data + 1 - if isinstance(i_data, int) - else simplify(cast(BitVec, i_data) + 1) - ) - else: - raise IndexError - if new_memory: - for i in range(len(new_memory)): - state.memory[i + mstart] = new_memory[i] - except IndexError: - log.debug("Exception copying code to memory") - - state.memory[mstart] = global_state.new_bitvec( - "code_" - + str(environment.active_account.contract_name) - + "[" - + str(code_offset) - + ": + " - + str(concrete_size) - + "]", + elif i < code_copy_size + calldata_copy_size: + # copy from calldata + global_state.mstate.memory[ + concrete_memory_offset + i + ] = calldata[calldata_copy_offset + i] + else: + global_state.mstate.memory[ + concrete_memory_offset + i + ] = global_state.new_bitvec( + "code({})".format( + global_state.environment.active_account.contract_name + ), 8, ) - elif concrete_size == 0 and isinstance( - global_state.current_transaction, ContractCreationTransaction - ): - if concrete_code_offset >= total_size: - Instruction._handle_symbolic_args( - global_state, concrete_memory_offset - ) - return [global_state] + return self._code_copy_helper( code=global_state.environment.code.bytecode, memory_offset=memory_offset, @@ -1750,26 +1737,20 @@ class Instruction: call_data = get_call_data(global_state, mem_offset, mem_offset + mem_size) - # code_raw = [] - # code_end = call_data.size - # for i in range(call_data.size): - # # Proper way to delimit init_bytecode? Seems to work. - # if call_data[i].symbolic: - # code_end = i - # break - # code_raw.append(call_data[i].value) - call_data = call_data.concrete(None) - - code_end = len(call_data) - for i in range(len(call_data)): - if not isinstance(call_data[i], int): + code_raw = [] + code_end = call_data.size + for i in range(call_data.size): + if call_data[i].symbolic: code_end = i break + code_raw.append(call_data[i].value) - # code_str = bytes.hex(bytes(code_raw)) - code_str = bytes.hex(bytes(call_data[0:code_end])) + code_str = bytes.hex(bytes(code_raw)) - constructor_arguments = call_data[code_end:] + next_transaction_id = get_next_transaction_id() + constructor_arguments = ConcreteCalldata( + next_transaction_id, call_data[code_end:] + ) code = Disassembly(code_str) caller = environment.active_account.address @@ -1838,7 +1819,7 @@ class Instruction: @StateTransition() def create2_post(self, global_state: GlobalState) -> List[GlobalState]: - addr, call_value, mem_offset, mem_size = global_state.mstate.pop(4) + addr, call_value, mem_offset, mem_size, salt = global_state.mstate.pop(5) global_state.mstate.stack.append(addr) return [global_state]