def get_constr_glbstate(contract, address): mstate = MachineState(gas=10000000) minimal_const_byte_len = get_minimal_constructor_param_encoding_len(abi_json_to_abi(contract.abi)) # better would be to append symbolic params to the bytecode such that the codecopy instruction that copies the # params into memory takes care of placing them onto the memory with the respective size. for i in range(int(minimal_const_byte_len / 32)): mstate.mem_extend(128 + 32 * i, 32) mstate.memory.insert(128 + 32 * i, BitVec('calldata_' + contract.name + '[' + str(i * 32)+ "]", 256)) # Todo Replace pure placement of enough symbolic 32 Byte-words with placement of symbolic variables that contain # the name of the solidity variables accounts = {address: Account(address, contract.disassembly, contract_name=contract.name)} environment = Environment( accounts[address], BitVec("caller", 256), [], BitVec("gasprice", 256), BitVec("callvalue", 256), BitVec("origin", 256), calldata_type=CalldataType.SYMBOLIC, ) # Todo find source for account info, maybe the std statespace? return GlobalState(accounts, environment, None, mstate)
def test_stack_pop_too_many(initial_size, overflow): # Arrange machine_state = MachineState(0) machine_state.stack = [42] * initial_size # Act + Assert with pytest.raises(StackUnderflowException): machine_state.pop(initial_size + overflow)
def test_stack_single_pop(): # Arrange machine_state = MachineState(0) machine_state.stack = [1, 2, 3] # Act result = machine_state.pop() # Assert assert isinstance(result, int)
def test_stack_multiple_pop_(): # Arrange machine_state = MachineState(0) machine_state.stack = [1, 2, 3] # Act a, b = machine_state.pop(2) # Assert assert a == 3 assert b == 2
def test_memory_write(initial_size, memory_offset, data): # Arrange machine_state = MachineState(0) machine_state.memory = [0]*initial_size # Act machine_state.memory_write(memory_offset, data) # Assert assert len(machine_state.memory) == max(initial_size, memory_offset+len(data)) assert machine_state.memory[memory_offset:memory_offset+len(data)] == data
def test_memory_extension(initial_size, start, extension_size): # Arrange machine_state = MachineState(0) machine_state.memory = [0] * initial_size # Act machine_state.mem_extend(start, extension_size) # Assert assert machine_state.memory_size == len(machine_state.memory) assert machine_state.memory_size == max(initial_size, start + extension_size)
def test_stack_multiple_pop(initial_stack, amount, expected): # Arrange machine_state = MachineState(0) machine_state.stack = initial_stack[:] # Act results = machine_state.pop(amount) # Assert assert results == initial_stack[-amount:][::-1] assert results == expected assert len(machine_state.stack) == len(initial_stack) - amount
def callcode_(self, global_state): instr = global_state.get_current_instruction() environment = global_state.environment try: callee_address, callee_account, call_data, value, call_data_type, gas, _, _ = get_call_parameters( global_state, self.dynamic_loader, True) except ValueError as e: logging.info( "Could not determine required parameters for call, putting fresh symbol on the stack. \n{}" .format(e)) global_state.mstate.stack.append( BitVec("retval_" + str(instr['address']), 256)) return [global_state] global_state.call_stack.append(instr['address']) environment = deepcopy(environment) environment.callvalue = value environment.caller = environment.address environment.calldata = call_data new_global_state = GlobalState(global_state.accounts, environment, MachineState(gas)) new_global_state.mstate.depth = global_state.mstate.depth + 1 new_global_state.mstate.constraints = copy( global_state.mstate.constraints) return [new_global_state]
def test_execute(mocker): active_account = Account('0x00') environment = Environment(active_account, None, None, None, None, None) state_1 = GlobalState(None, environment, None, MachineState(gas=10000000)) state_1.mstate.stack = [1, 2] mocker.patch.object(state_1, 'get_current_instruction') state_1.get_current_instruction.return_value = {"opcode": "PUSH"} state_2 = GlobalState(None, environment, None, MachineState(gas=10000000)) state_2.mstate.stack = [1, 2, 3] mocker.patch.object(state_2, 'get_current_instruction') state_2.get_current_instruction.return_value = {"opcode": "ADD"} node_1 = Node("Test contract") node_1.states = [state_1, state_2] state_3 = GlobalState(None, environment, None, MachineState(gas=10000000)) state_3.mstate.stack = [1, 2] mocker.patch.object(state_3, 'get_current_instruction') state_3.get_current_instruction.return_value = {"opcode": "ADD"} node_2 = Node("Test contract") node_2.states = [state_3] edge = Edge(node_1.uid, node_2.uid) statespace = LaserEVM(None) statespace.edges = [edge] statespace.nodes[node_1.uid] = node_1 statespace.nodes[node_2.uid] = node_2 # Act result = TaintRunner.execute(statespace, node_1, state_1, [True, True]) # Assert print(result) assert len(result.records) == 3 assert result.records[2].states == [] assert state_3 in result.records[1].states
def test_codecopy_concrete(): # Arrange active_account = Account("0x0", code=Disassembly("60606040")) environment = Environment(active_account, None, None, None, None, None) og_state = GlobalState(None, environment, None, MachineState(gas=10000000)) og_state.mstate.stack = [2, 2, 2] instruction = Instruction("codecopy", dynamic_loader=None) # Act new_state = instruction.evaluate(og_state)[0] # Assert assert new_state.mstate.memory[2] == 96 assert new_state.mstate.memory[3] == 64
def call_(self, global_state): instr = global_state.get_current_instruction() environment = global_state.environment try: 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, True) except ValueError as e: logging.info( "Could not determine required parameters for call, putting fresh symbol on the stack. \n{}" .format(e)) # TODO: decide what to do in this case global_state.mstate.stack.append( BitVec("retval_" + str(instr['address']), 256)) return [global_state] if 0 < int(callee_address, 16) < 5: logging.info("Native contract called: " + callee_address) if call_data == [] and call_data_type == CalldataType.SYMBOLIC: logging.debug("CALL with symbolic data not supported") global_state.mstate.stack.append( BitVec("retval_" + str(instr['address']), 256)) return [global_state] data = natives.native_contracts(int(callee_address, 16), call_data) try: mem_out_start = helper.get_concrete_int(memory_out_offset) mem_out_sz = memory_out_size.as_long() except AttributeError: logging.debug( "CALL with symbolic start or offset not supported") global_state.mstate.stack.append( BitVec("retval_" + str(instr['address']), 256)) return [global_state] global_state.mstate.mem_extend(mem_out_start, mem_out_sz) try: 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] except: global_state.mstate.memory[mem_out_start] = BitVec(data, 256) # TODO: maybe use BitVec here constrained to 1 global_state.mstate.stack.append( BitVec("retval_" + str(instr['address']), 256)) return [global_state] global_state.call_stack.append(instr['address']) callee_environment = Environment( callee_account, BitVecVal(int(environment.active_account.address, 16), 256), call_data, environment.gasprice, value, environment.origin, calldata_type=call_data_type) new_global_state = GlobalState(global_state.accounts, callee_environment, MachineState(gas)) new_global_state.mstate.depth = global_state.mstate.depth + 1 new_global_state.mstate.constraints = copy( global_state.mstate.constraints) return [global_state]