diff --git a/mythril/laser/ethereum/instructions.py b/mythril/laser/ethereum/instructions.py index 903fe1ec..5d37350b 100644 --- a/mythril/laser/ethereum/instructions.py +++ b/mythril/laser/ethereum/instructions.py @@ -940,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 + 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 - # ) + 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 + ) else: no_of_bytes = len(disassembly.bytecode) // 2 @@ -1139,9 +1139,9 @@ class Instruction: 8, ) return [global_state] - - for i in range(code_copy_size + calldata_copy_size): + for i in range(concrete_size): if i < code_copy_size: + # copy from code global_state.mstate.memory[concrete_memory_offset + i] = int( code[ 2 @@ -1154,7 +1154,7 @@ class Instruction: # copy from calldata global_state.mstate.memory[ concrete_memory_offset + i - ] = calldata[calldata_copy_offset + i] + ] = calldata[calldata_copy_offset + i - code_copy_size] else: global_state.mstate.memory[ concrete_memory_offset + i @@ -1726,11 +1726,9 @@ class Instruction: # Not supported return [global_state] - @staticmethod def _create_transaction_helper( - global_state, call_value, mem_offset, mem_size, op_code, create2_salt=None + self, global_state, call_value, mem_offset, mem_size, create2_salt=None ): - mstate = global_state.mstate environment = global_state.environment world_state = global_state.world_state @@ -1782,7 +1780,7 @@ class Instruction: call_value=call_value, contract_address=contract_address, ) - raise TransactionStartSignal(transaction, op_code, global_state) + raise TransactionStartSignal(transaction, self.op_code, global_state) @StateTransition(is_state_mutation_instruction=True) def create_(self, global_state: GlobalState) -> List[GlobalState]: @@ -1795,13 +1793,20 @@ class Instruction: call_value, mem_offset, mem_size = global_state.mstate.pop(3) return self._create_transaction_helper( - global_state, call_value, mem_offset, mem_size, self.op_code + global_state, call_value, mem_offset, mem_size ) @StateTransition() def create_post(self, global_state: GlobalState) -> List[GlobalState]: - addr, call_value, mem_offset, mem_size = global_state.mstate.pop(4) - global_state.mstate.stack.append(addr) + call_value, mem_offset, mem_size = global_state.mstate.pop(3) + call_data = get_call_data(global_state, mem_offset, mem_offset + mem_size) + if global_state.last_return_data: + global_state.mstate.stack.append( + symbol_factory.BitVecVal(int(global_state.last_return_data, 16), 256) + ) + else: + global_state.mstate.stack.append(symbol_factory.BitVecVal(0, 256)) + return [global_state] @StateTransition(is_state_mutation_instruction=True) @@ -1814,13 +1819,19 @@ class Instruction: call_value, mem_offset, mem_size, salt = global_state.mstate.pop(4) return self._create_transaction_helper( - global_state, call_value, mem_offset, mem_size, self.op_code, salt + global_state, call_value, mem_offset, mem_size, salt ) @StateTransition() def create2_post(self, global_state: GlobalState) -> List[GlobalState]: - addr, call_value, mem_offset, mem_size, salt = global_state.mstate.pop(5) - global_state.mstate.stack.append(addr) + call_value, mem_offset, mem_size, salt = global_state.mstate.pop(4) + call_data = get_call_data(global_state, mem_offset, mem_offset + mem_size) + if global_state.last_return_data: + global_state.mstate.stack.append( + symbol_factory.BitVecVal(int(global_state.last_return_data), 256) + ) + else: + global_state.mstate.stack.append(symbol_factory.BitVecVal(0, 256)) return [global_state] @StateTransition() diff --git a/mythril/laser/ethereum/svm.py b/mythril/laser/ethereum/svm.py index fa78c840..e2b53199 100644 --- a/mythril/laser/ethereum/svm.py +++ b/mythril/laser/ethereum/svm.py @@ -380,13 +380,6 @@ class LaserEVM: :param return_data: :return: """ - if isinstance(global_state.current_transaction, ContractCreationTransaction): - # is this the proper place to put CREATE handle? - return_global_state.mstate.stack.append( - global_state.environment.active_account.address - ) - return_global_state.mstate.min_gas_used += global_state.mstate.min_gas_used - return_global_state.mstate.max_gas_used += global_state.mstate.max_gas_used return_global_state.mstate.constraints += global_state.mstate.constraints # Resume execution of the transaction initializing instruction @@ -401,6 +394,15 @@ class LaserEVM: return_global_state.environment.active_account = global_state.accounts[ return_global_state.environment.active_account.address.value ] + if isinstance( + global_state.current_transaction, ContractCreationTransaction + ): + return_global_state.mstate.min_gas_used += ( + global_state.mstate.min_gas_used + ) + return_global_state.mstate.max_gas_used += ( + global_state.mstate.max_gas_used + ) # Execute the post instruction handler new_global_states = Instruction( diff --git a/tests/instructions/create_test.py b/tests/instructions/create_test.py index 5941949d..0a97565e 100644 --- a/tests/instructions/create_test.py +++ b/tests/instructions/create_test.py @@ -6,30 +6,67 @@ from mythril.laser.ethereum.state.global_state import GlobalState from mythril.laser.ethereum.state.world_state import WorldState from mythril.laser.ethereum.instructions import Instruction from mythril.laser.ethereum.transaction.transaction_models import MessageCallTransaction - +from mythril.laser.ethereum.state.calldata import ConcreteCalldata from mythril.laser.ethereum.svm import LaserEVM - from mythril.laser.smt import symbol_factory +# contract A { +# uint256 public val = 10; +# } +contract_init_code = "6080604052600a600055348015601457600080fd5b506084806100236000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80633c6bb43614602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000548156fea265627a7a72315820d3cfe7a909450a953cbd7e47d8c17477f2609baa5208d043e965efec69d1ed9864736f6c634300050b0032" +contract_runtime_code = "6080604052348015600f57600080fd5b506004361060285760003560e01c80633c6bb43614602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000548156fea265627a7a72315820d3cfe7a909450a953cbd7e47d8c17477f2609baa5208d043e965efec69d1ed9864736f6c634300050b0032" -def test_create(): - creating_contract_runtime_code = "608060405260043610601c5760003560e01c8063efc81a8c146021575b600080fd5b60276029565b005b60006040516035906056565b604051809103906000f0801580156050573d6000803e3d6000fd5b50905050565b605b806100638339019056fe6080604052348015600f57600080fd5b50603e80601d6000396000f3fe6080604052600080fdfea265627a7a72315820f4040cbd444dcd2f49f1daf46a116eb32396f1cc84a9e79e3836d4bfe84d6bca64736f6c634300050b0032a265627a7a72315820e2350a73a28ed02b4dac678f2b77a330dc512c3cce8ca53fa1c30869f443553d64736f6c634300050b0032" - created_contract_init_code = "6080604052348015600f57600080fd5b50603e80601d6000396000f3fe6080604052600080fdfea265627a7a72315820f4040cbd444dcd2f49f1daf46a116eb32396f1cc84a9e79e3836d4bfe84d6bca64736f6c634300050b0032" - created_contract_runtime_code = "6080604052600080fdfea265627a7a72315820f4040cbd444dcd2f49f1daf46a116eb32396f1cc84a9e79e3836d4bfe84d6bca64736f6c634300050b0032" - world_state = WorldState() - account = world_state.create_account(balance=1000, address=101) - account.code = Disassembly(creating_contract_runtime_code) - environment = Environment(account, None, None, None, None, None) - og_state = GlobalState( - world_state, environment, None, MachineState(gas_limit=8000000) - ) - og_state.transaction_stack.append( - (MessageCallTransaction(world_state=WorldState(), gas_limit=8000000), None) - ) +last_state = None +created_contract_account = None + + +def execute_create(): + global last_state + global created_contract_account + if not last_state and not created_contract_account: + code_raw = [] + for i in range(len(contract_init_code) // 2): + code_raw.append(int(contract_init_code[2 * i : 2 * (i + 1)], 16)) + calldata = ConcreteCalldata(0, code_raw) - laser_evm = LaserEVM() + world_state = WorldState() + account = world_state.create_account(balance=1000000, address=101) + account.code = Disassembly("60a760006000f000") + environment = Environment(account, None, calldata, None, None, None) + og_state = GlobalState( + world_state, environment, None, MachineState(gas_limit=8000000) + ) + og_state.transaction_stack.append( + (MessageCallTransaction(world_state=WorldState(), gas_limit=8000000), None) + ) - new_states, op_code = laser_evm.execute_state(og_state) - # checks + laser = LaserEVM() + states = [og_state] + last_state = og_state + for state in states: + new_states, op_code = laser.execute_state(state) + last_state = state + if op_code == "STOP": + break + states.extend(new_states) - # TODO: come up with sequence, then grab an address from stack and check that its code is created. + created_contract_address = last_state.mstate.stack[-1].value + created_contract_account = last_state.world_state.accounts[ + created_contract_address + ] + + return last_state, created_contract_account + + +def test_create_has_code(): + last_state, created_contract_account = execute_create() + assert created_contract_account.code.bytecode == contract_runtime_code + + +def test_create_has_storage(): + last_state, created_contract_account = execute_create() + storage = created_contract_account.storage + # From contract, val = 10. + assert storage[symbol_factory.BitVecVal(0, 256)] == symbol_factory.BitVecVal( + 10, 256 + )