diff --git a/mythril/laser/ethereum/instructions.py b/mythril/laser/ethereum/instructions.py index ac6033da..32def7e2 100644 --- a/mythril/laser/ethereum/instructions.py +++ b/mythril/laser/ethereum/instructions.py @@ -517,9 +517,44 @@ class Instruction: @instruction def codecopy_(self, global_state): - # FIXME: not implemented - state = global_state.mstate - start, s1, size = state.stack.pop(), state.stack.pop(), state.stack.pop() + memory_offset, code_offset, size = global_state.mstate.stack.pop(), global_state.mstate.stack.pop(), global_state.mstate.stack.pop() + + try: + concrete_memory_offset = helper.get_concrete_int(memory_offset) + except AttributeError: + logging.debug("Unsupported symbolic memory offset in CODECOPY") + return [global_state] + + try: + concrete_size = helper.get_concrete_int(size) + global_state.mstate.mem_extend(concrete_memory_offset, concrete_size) + except: + # except both attribute error and Exception + global_state.mstate.mem_extend(concrete_memory_offset, 1) + global_state.mstate.memory[concrete_memory_offset] = \ + BitVec("code({})".format(global_state.environment.active_account.contract_name), 256) + return [global_state] + + try: + concrete_code_offset = helper.get_concrete_int(code_offset) + except AttributeError: + logging.debug("Unsupported symbolic code offset in CODECOPY") + global_state.mstate.mem_extend(concrete_memory_offset, concrete_size) + for i in range(concrete_size): + global_state.mstate.memory[concrete_memory_offset + i] = \ + BitVec("code({})".format(global_state.environment.active_account.contract_name), 256) + return [global_state] + + bytecode = global_state.environment.active_account.code.bytecode + + for i in range(concrete_size): + try: + global_state.mstate.memory[concrete_memory_offset + i] =\ + int(bytecode[2*(concrete_code_offset + i): 2*(concrete_code_offset + i + 1)], 16) + except IndexError: + global_state.mstate.memory[concrete_memory_offset + i] = \ + BitVec("code({})".format(global_state.environment.active_account.contract_name), 256) + return [global_state] @instruction diff --git a/mythril/laser/ethereum/state.py b/mythril/laser/ethereum/state.py index 751c1c6e..c5f3a917 100644 --- a/mythril/laser/ethereum/state.py +++ b/mythril/laser/ethereum/state.py @@ -68,6 +68,7 @@ class Environment: calldata_type=CalldataType.SYMBOLIC, ): # Metadata + self.active_account = active_account self.active_function_name = "" diff --git a/tests/instructions/__init__.py b/tests/instructions/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/instructions/codecopy_test.py b/tests/instructions/codecopy_test.py new file mode 100644 index 00000000..53ae049d --- /dev/null +++ b/tests/instructions/codecopy_test.py @@ -0,0 +1,20 @@ +from mythril.disassembler.disassembly import Disassembly +from mythril.laser.ethereum.state import MachineState, GlobalState, Environment, Account +from mythril.laser.ethereum.instructions import Instruction + + +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 diff --git a/tests/report_test.py b/tests/report_test.py index 7f1da181..a5c41f21 100644 --- a/tests/report_test.py +++ b/tests/report_test.py @@ -38,7 +38,7 @@ def _generate_report(input_file): def reports(): """ Fixture that analyses all reports""" pool = Pool(cpu_count()) - input_files = [f for f in TESTDATA_INPUTS.iterdir()] + input_files = sorted([f for f in TESTDATA_INPUTS.iterdir()]) results = pool.map(_generate_report, input_files) return results