From 6e0d775c09f32bb9e757b53b7f8d768f618f452a Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Mon, 1 Oct 2018 20:05:46 +0530 Subject: [PATCH 1/8] Remove all broad exception catches in laser --- mythril/laser/ethereum/call.py | 1 + mythril/laser/ethereum/instructions.py | 80 +++++++++++--------------- mythril/laser/ethereum/util.py | 12 +++- mythril/support/loader.py | 2 +- 4 files changed, 46 insertions(+), 49 deletions(-) diff --git a/mythril/laser/ethereum/call.py b/mythril/laser/ethereum/call.py index 31e6990e..b2f6a9f7 100644 --- a/mythril/laser/ethereum/call.py +++ b/mythril/laser/ethereum/call.py @@ -63,6 +63,7 @@ def get_callee_address(global_state:GlobalState, dynamic_loader: DynLoader, symb # attempt to read the contract address from instance storage try: callee_address = dynamic_loader.read_storage(environment.active_account.address, index) + # TODO: verify whether this happens or not except: logging.debug("Error accessing contract storage.") raise ValueError diff --git a/mythril/laser/ethereum/instructions.py b/mythril/laser/ethereum/instructions.py index 3d5d2bf7..27172f0a 100644 --- a/mythril/laser/ethereum/instructions.py +++ b/mythril/laser/ethereum/instructions.py @@ -264,18 +264,17 @@ class Instruction: try: s0 = util.get_concrete_int(s0) s1 = util.get_concrete_int(s1) + except ValueError: + return [] - if s0 <= 31: - testbit = s0 * 8 + 7 - if s1 & (1 << testbit): - state.stack.append(s1 | (TT256 - (1 << testbit))) - else: - state.stack.append(s1 & ((1 << testbit) - 1)) + if s0 <= 31: + testbit = s0 * 8 + 7 + if s1 & (1 << testbit): + state.stack.append(s1 | (TT256 - (1 << testbit))) else: - state.stack.append(s1) - # TODO: broad exception handler - except: - return [] + state.stack.append(s1 & ((1 << testbit) - 1)) + else: + state.stack.append(s1) return [global_state] @@ -367,16 +366,15 @@ class Instruction: return [global_state] if type(b) == int: - val = b'' try: - for i in range(offset, offset + 32): - val += environment.calldata[i].to_bytes(1, byteorder='big') + val = b''.join([calldata.to_bytes(1, byteorder='big') for calldata in + environment.calldata[offset:offset+32]]) logging.debug("Final value: " + str(int.from_bytes(val, byteorder='big'))) state.stack.append(BitVecVal(int.from_bytes(val, byteorder='big'), 256)) - # FIXME: broad exception catch - except: + + except (util.ConcreteIntException, AttributeError): state.stack.append(global_state.new_bitvec( "calldata_" + str(environment.active_account.contract_name) + "[" + str(simplify(op0)) + "]", 256)) else: @@ -404,16 +402,14 @@ class Instruction: try: mstart = util.get_concrete_int(op0) - # FIXME: broad exception catch - except: + except util.ConcreteIntException: logging.debug("Unsupported symbolic memory offset in CALLDATACOPY") return [global_state] dstart_sym = False try: dstart = util.get_concrete_int(op1) - # FIXME: broad exception catch - except: + except util.ConcreteIntException: logging.debug("Unsupported symbolic calldata offset in CALLDATACOPY") dstart = simplify(op1) dstart_sym = True @@ -421,8 +417,7 @@ class Instruction: size_sym = False try: size = util.get_concrete_int(op2) - # FIXME: broad exception catch - except: + except util.ConcreteIntException: logging.debug("Unsupported symbolic size in CALLDATACOPY") size = simplify(op2) size_sym = True @@ -437,8 +432,7 @@ class Instruction: if size > 0: try: state.mem_extend(mstart, size) - # FIXME: broad exception catch - except: + except TypeError: logging.debug("Memory allocation error: mstart = " + str(mstart) + ", size = " + str(size)) state.mem_extend(mstart, 1) state.memory[mstart] = global_state.new_bitvec( @@ -452,7 +446,7 @@ class Instruction: for i in range(mstart, mstart + size): state.memory[i] = environment.calldata[i_data] i_data += 1 - except: + except IndexError: logging.debug("Exception copying calldata to memory") state.memory[mstart] = global_state.new_bitvec( @@ -507,8 +501,7 @@ class Instruction: try: index, length = util.get_concrete_int(op0), util.get_concrete_int(op1) - # FIXME: broad exception catch - except: + except util.ConcreteIntException: # Can't access symbolic memory offsets if is_expr(op0): op0 = simplify(op0) @@ -520,7 +513,7 @@ class Instruction: data = b''.join([util.get_concrete_int(i).to_bytes(1, byteorder='big') for i in state.memory[index: index + length]]) - except AttributeError: + except util.ConcreteIntException: argument = str(state.memory[index]).replace(" ", "_") result = BitVec("KECCAC[{}]".format(argument), 256) @@ -552,7 +545,7 @@ class Instruction: try: concrete_size = helper.get_concrete_int(size) global_state.mstate.mem_extend(concrete_memory_offset, concrete_size) - except: + except (util.ConcreteIntException, TypeError): # except both attribute error and Exception global_state.mstate.mem_extend(concrete_memory_offset, 1) global_state.mstate.memory[concrete_memory_offset] = \ @@ -694,7 +687,7 @@ class Instruction: try: mstart = util.get_concrete_int(op0) - except AttributeError: + except util.ConcreteIntException: logging.debug("MSTORE to symbolic index. Not supported") return [global_state] @@ -707,17 +700,15 @@ class Instruction: try: # Attempt to concretize value + _bytes = util.concrete_int_to_bytes(value) - i = 0 + state.memory[mstart:mstart+len(_bytes)] = _bytes - for b in _bytes: - state.memory[mstart + i] = _bytes[i] - i += 1 - except: + except (AttributeError, TypeError): try: state.memory[mstart] = value - except: + except TypeError: logging.debug("Invalid memory access") return [global_state] @@ -729,7 +720,7 @@ class Instruction: try: offset = util.get_concrete_int(op0) - except AttributeError: + except util.ConcreteIntException: logging.debug("MSTORE to symbolic index. Not supported") return [global_state] @@ -750,7 +741,7 @@ class Instruction: index = util.get_concrete_int(index) return self._sload_helper(global_state, index) - except AttributeError: + except util.ConcreteIntException: if not keccak_function_manager.is_keccak(index): return self._sload_helper(global_state, str(index)) @@ -811,7 +802,7 @@ class Instruction: try: index = util.get_concrete_int(index) return self._sstore_helper(global_state, index, value) - except AttributeError: + except util.ConcreteIntException: is_keccak = keccak_function_manager.is_keccak(index) if not is_keccak: return self._sstore_helper(global_state, str(index), value) @@ -864,7 +855,7 @@ class Instruction: disassembly = global_state.environment.code try: jump_addr = util.get_concrete_int(state.stack.pop()) - except AttributeError: + except util.ConcreteIntException: raise InvalidJumpDestination("Invalid jump argument (symbolic address)") except IndexError: raise StackUnderflowException() @@ -894,8 +885,7 @@ class Instruction: try: jump_addr = util.get_concrete_int(op0) - # FIXME: to broad exception handler - except: + except util.ConcreteIntException: logging.debug("Skipping JUMPI to invalid destination.") global_state.mstate.pc += 1 return [global_state] @@ -975,7 +965,7 @@ class Instruction: return_data = [global_state.new_bitvec("return_data", 256)] try: return_data = state.memory[util.get_concrete_int(offset):util.get_concrete_int(offset + length)] - except AttributeError: + except util.ConcreteIntException: logging.debug("Return with symbolic length or offset. Not supported") global_state.current_transaction.end(global_state, return_data) @@ -1098,7 +1088,7 @@ class Instruction: try: memory_out_offset = util.get_concrete_int(memory_out_offset) if isinstance(memory_out_offset, ExprRef) else memory_out_offset memory_out_size = util.get_concrete_int(memory_out_size) if isinstance(memory_out_size, ExprRef) else memory_out_size - except AttributeError: + except util.ConcreteIntException: global_state.mstate.stack.append(global_state.new_bitvec("retval_" + str(instr['address']), 256)) return [global_state] @@ -1166,7 +1156,7 @@ class Instruction: try: memory_out_offset = util.get_concrete_int(memory_out_offset) if isinstance(memory_out_offset, ExprRef) else memory_out_offset memory_out_size = util.get_concrete_int(memory_out_size) if isinstance(memory_out_size, ExprRef) else memory_out_size - except AttributeError: + except util.ConcreteIntException: global_state.mstate.stack.append(global_state.new_bitvec("retval_" + str(instr['address']), 256)) return [global_state] @@ -1238,7 +1228,7 @@ class Instruction: ExprRef) else memory_out_offset memory_out_size = util.get_concrete_int(memory_out_size) if isinstance(memory_out_size, ExprRef) else memory_out_size - except AttributeError: + except util.ConcreteIntException: global_state.mstate.stack.append(global_state.new_bitvec("retval_" + str(instr['address']), 256)) return [global_state] diff --git a/mythril/laser/ethereum/util.py b/mythril/laser/ethereum/util.py index c6c8e5ce..7b9061ea 100644 --- a/mythril/laser/ethereum/util.py +++ b/mythril/laser/ethereum/util.py @@ -10,6 +10,10 @@ TT256M1 = 2 ** 256 - 1 TT255 = 2 ** 255 +class ConcreteIntException(AttributeError): + pass + + def sha3(seed): return _sha3.keccak_256(bytes(seed)).digest() @@ -80,10 +84,12 @@ def get_concrete_int(item): elif is_true(simplified): return 1 else: - raise ValueError("Symbolic boolref encountered") - - return simplify(item).as_long() + raise ConcreteIntException("Symbolic boolref encountered") + try: + return simplify(item).as_long() + except AttributeError: + raise ConcreteIntException("Got a symbolic BitVecRef") def concrete_int_from_bytes(_bytes, start_index): diff --git a/mythril/support/loader.py b/mythril/support/loader.py index 7c0855ec..16d2e683 100644 --- a/mythril/support/loader.py +++ b/mythril/support/loader.py @@ -47,7 +47,7 @@ class DynLoader: code = self.eth.eth_getCode(dependency_address) - if (code == "0x"): + if code == "0x": return None else: return Disassembly(code) From 499be11ea4e0a468f3daff9cc9d7ad484987848f Mon Sep 17 00:00:00 2001 From: Stamatis Katsaounis Date: Sun, 14 Oct 2018 23:56:50 +0300 Subject: [PATCH 2/8] Add max-transaction-count cli parameter. Signed-off-by: Stamatis Katsaounis --- mythril/analysis/symbolic.py | 5 +++-- mythril/interfaces/cli.py | 4 +++- mythril/laser/ethereum/svm.py | 7 ++++--- mythril/mythril.py | 6 ++++-- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/mythril/analysis/symbolic.py b/mythril/analysis/symbolic.py index ac03303f..c9431257 100644 --- a/mythril/analysis/symbolic.py +++ b/mythril/analysis/symbolic.py @@ -14,7 +14,7 @@ class SymExecWrapper: """ def __init__(self, contract, address, strategy, dynloader=None, max_depth=22, - execution_timeout=None, create_timeout=None): + execution_timeout=None, create_timeout=None, max_transaction_count=3): s_strategy = None if strategy == 'dfs': @@ -30,7 +30,8 @@ class SymExecWrapper: self.laser = svm.LaserEVM(self.accounts, dynamic_loader=dynloader, max_depth=max_depth, execution_timeout=execution_timeout, strategy=s_strategy, - create_timeout=create_timeout) + create_timeout=create_timeout, + max_transaction_count=max_transaction_count) if isinstance(contract, SolidityContract): self.laser.sym_exec(creation_code=contract.creation_code, contract_name=contract.name) diff --git a/mythril/interfaces/cli.py b/mythril/interfaces/cli.py index 3e398704..1784add4 100644 --- a/mythril/interfaces/cli.py +++ b/mythril/interfaces/cli.py @@ -69,6 +69,7 @@ def main(): options = parser.add_argument_group('options') options.add_argument('-m', '--modules', help='Comma-separated list of security analysis modules', metavar='MODULES') options.add_argument('--max-depth', type=int, default=22, help='Maximum recursion depth for symbolic execution') + options.add_argument('--max-transaction-count', type=int, default=3, help='Maximum number of transactions issued by laser') options.add_argument('--strategy', choices=['dfs', 'bfs'], default='dfs', help='Symbolic execution strategy') options.add_argument('--execution-timeout', type=int, default=600, help="The amount of seconds to spend on symbolic execution") options.add_argument('--create-timeout', type=int, default=10, help="The amount of seconds to spend on " @@ -218,7 +219,8 @@ def main(): modules=[m.strip() for m in args.modules.strip().split(",")] if args.modules else [], verbose_report=args.verbose_report, max_depth=args.max_depth, execution_timeout=args.execution_timeout, - create_timeout=args.create_timeout) + create_timeout=args.create_timeout, + max_transaction_count=args.max_transaction_count) outputs = { 'json': report.as_json(), 'text': report.as_text(), diff --git a/mythril/laser/ethereum/svm.py b/mythril/laser/ethereum/svm.py index 3a173515..f1bd80b5 100644 --- a/mythril/laser/ethereum/svm.py +++ b/mythril/laser/ethereum/svm.py @@ -28,7 +28,7 @@ class LaserEVM: """ def __init__(self, accounts, dynamic_loader=None, max_depth=float('inf'), execution_timeout=60, create_timeout=10, - strategy=DepthFirstSearchStrategy): + strategy=DepthFirstSearchStrategy, max_transaction_count=3): world_state = WorldState() world_state.accounts = accounts # this sets the initial world state @@ -45,6 +45,7 @@ class LaserEVM: self.work_list = [] self.strategy = strategy(self.work_list, max_depth) self.max_depth = max_depth + self.max_transaction_count = max_transaction_count self.execution_timeout = execution_timeout self.create_timeout = create_timeout @@ -60,7 +61,7 @@ class LaserEVM: def accounts(self): return self.world_state.accounts - def sym_exec(self, main_address=None, creation_code=None, contract_name=None, max_transactions=3): + def sym_exec(self, main_address=None, creation_code=None, contract_name=None): logging.debug("Starting LASER execution") self.time = datetime.now() @@ -77,7 +78,7 @@ class LaserEVM: # Reset code coverage self.coverage = {} - for i in range(max_transactions): + for i in range(self.max_transaction_count): initial_coverage = self._get_covered_instructions() self.time = datetime.now() diff --git a/mythril/mythril.py b/mythril/mythril.py index 027e1211..123ab904 100644 --- a/mythril/mythril.py +++ b/mythril/mythril.py @@ -360,14 +360,16 @@ class Mythril(object): return generate_graph(sym, physics=enable_physics, phrackify=phrackify) def fire_lasers(self, strategy, contracts=None, address=None, - modules=None, verbose_report=False, max_depth=None, execution_timeout=None, create_timeout=None): + modules=None, verbose_report=False, max_depth=None, execution_timeout=None, create_timeout=None, + max_transaction_count=None): all_issues = [] for contract in (contracts or self.contracts): sym = SymExecWrapper(contract, address, strategy, dynloader=DynLoader(self.eth) if self.dynld else None, max_depth=max_depth, execution_timeout=execution_timeout, - create_timeout=create_timeout) + create_timeout=create_timeout, + max_transaction_count=max_transaction_count) issues = fire_lasers(sym, modules) From 095e8d90b42dea21ad78a3170e135d446eaed35a Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Mon, 15 Oct 2018 21:59:59 +0530 Subject: [PATCH 3/8] Convert util exception to TypeError --- mythril/laser/ethereum/instructions.py | 32 +++++++++++++------------- mythril/laser/ethereum/util.py | 15 ++++++------ 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/mythril/laser/ethereum/instructions.py b/mythril/laser/ethereum/instructions.py index 27172f0a..881a820a 100644 --- a/mythril/laser/ethereum/instructions.py +++ b/mythril/laser/ethereum/instructions.py @@ -374,7 +374,7 @@ class Instruction: logging.debug("Final value: " + str(int.from_bytes(val, byteorder='big'))) state.stack.append(BitVecVal(int.from_bytes(val, byteorder='big'), 256)) - except (util.ConcreteIntException, AttributeError): + except (TypeError, AttributeError): state.stack.append(global_state.new_bitvec( "calldata_" + str(environment.active_account.contract_name) + "[" + str(simplify(op0)) + "]", 256)) else: @@ -402,14 +402,14 @@ class Instruction: try: mstart = util.get_concrete_int(op0) - except util.ConcreteIntException: + except TypeError: logging.debug("Unsupported symbolic memory offset in CALLDATACOPY") return [global_state] dstart_sym = False try: dstart = util.get_concrete_int(op1) - except util.ConcreteIntException: + except TypeError: logging.debug("Unsupported symbolic calldata offset in CALLDATACOPY") dstart = simplify(op1) dstart_sym = True @@ -417,7 +417,7 @@ class Instruction: size_sym = False try: size = util.get_concrete_int(op2) - except util.ConcreteIntException: + except TypeError: logging.debug("Unsupported symbolic size in CALLDATACOPY") size = simplify(op2) size_sym = True @@ -501,7 +501,7 @@ class Instruction: try: index, length = util.get_concrete_int(op0), util.get_concrete_int(op1) - except util.ConcreteIntException: + except TypeError: # Can't access symbolic memory offsets if is_expr(op0): op0 = simplify(op0) @@ -545,7 +545,7 @@ class Instruction: try: concrete_size = helper.get_concrete_int(size) global_state.mstate.mem_extend(concrete_memory_offset, concrete_size) - except (util.ConcreteIntException, TypeError): + except TypeError: # except both attribute error and Exception global_state.mstate.mem_extend(concrete_memory_offset, 1) global_state.mstate.memory[concrete_memory_offset] = \ @@ -687,7 +687,7 @@ class Instruction: try: mstart = util.get_concrete_int(op0) - except util.ConcreteIntException: + except TypeError: logging.debug("MSTORE to symbolic index. Not supported") return [global_state] @@ -720,7 +720,7 @@ class Instruction: try: offset = util.get_concrete_int(op0) - except util.ConcreteIntException: + except TypeError: logging.debug("MSTORE to symbolic index. Not supported") return [global_state] @@ -741,7 +741,7 @@ class Instruction: index = util.get_concrete_int(index) return self._sload_helper(global_state, index) - except util.ConcreteIntException: + except TypeError: if not keccak_function_manager.is_keccak(index): return self._sload_helper(global_state, str(index)) @@ -802,7 +802,7 @@ class Instruction: try: index = util.get_concrete_int(index) return self._sstore_helper(global_state, index, value) - except util.ConcreteIntException: + except TypeError: is_keccak = keccak_function_manager.is_keccak(index) if not is_keccak: return self._sstore_helper(global_state, str(index), value) @@ -855,7 +855,7 @@ class Instruction: disassembly = global_state.environment.code try: jump_addr = util.get_concrete_int(state.stack.pop()) - except util.ConcreteIntException: + except TypeError: raise InvalidJumpDestination("Invalid jump argument (symbolic address)") except IndexError: raise StackUnderflowException() @@ -885,7 +885,7 @@ class Instruction: try: jump_addr = util.get_concrete_int(op0) - except util.ConcreteIntException: + except TypeError: logging.debug("Skipping JUMPI to invalid destination.") global_state.mstate.pc += 1 return [global_state] @@ -965,7 +965,7 @@ class Instruction: return_data = [global_state.new_bitvec("return_data", 256)] try: return_data = state.memory[util.get_concrete_int(offset):util.get_concrete_int(offset + length)] - except util.ConcreteIntException: + except TypeError: logging.debug("Return with symbolic length or offset. Not supported") global_state.current_transaction.end(global_state, return_data) @@ -1088,7 +1088,7 @@ class Instruction: try: memory_out_offset = util.get_concrete_int(memory_out_offset) if isinstance(memory_out_offset, ExprRef) else memory_out_offset memory_out_size = util.get_concrete_int(memory_out_size) if isinstance(memory_out_size, ExprRef) else memory_out_size - except util.ConcreteIntException: + except TypeError: global_state.mstate.stack.append(global_state.new_bitvec("retval_" + str(instr['address']), 256)) return [global_state] @@ -1156,7 +1156,7 @@ class Instruction: try: memory_out_offset = util.get_concrete_int(memory_out_offset) if isinstance(memory_out_offset, ExprRef) else memory_out_offset memory_out_size = util.get_concrete_int(memory_out_size) if isinstance(memory_out_size, ExprRef) else memory_out_size - except util.ConcreteIntException: + except TypeError: global_state.mstate.stack.append(global_state.new_bitvec("retval_" + str(instr['address']), 256)) return [global_state] @@ -1228,7 +1228,7 @@ class Instruction: ExprRef) else memory_out_offset memory_out_size = util.get_concrete_int(memory_out_size) if isinstance(memory_out_size, ExprRef) else memory_out_size - except util.ConcreteIntException: + except TypeError: global_state.mstate.stack.append(global_state.new_bitvec("retval_" + str(instr['address']), 256)) return [global_state] diff --git a/mythril/laser/ethereum/util.py b/mythril/laser/ethereum/util.py index 7b9061ea..7b4693f5 100644 --- a/mythril/laser/ethereum/util.py +++ b/mythril/laser/ethereum/util.py @@ -10,8 +10,6 @@ TT256M1 = 2 ** 256 - 1 TT255 = 2 ** 255 -class ConcreteIntException(AttributeError): - pass def sha3(seed): @@ -20,7 +18,7 @@ def sha3(seed): def safe_decode(hex_encoded_string): - if (hex_encoded_string.startswith("0x")): + if hex_encoded_string.startswith("0x"): return bytes.fromhex(hex_encoded_string[2:]) else: return bytes.fromhex(hex_encoded_string) @@ -84,12 +82,13 @@ def get_concrete_int(item): elif is_true(simplified): return 1 else: - raise ConcreteIntException("Symbolic boolref encountered") + raise TypeError("Symbolic boolref encountered") try: return simplify(item).as_long() except AttributeError: - raise ConcreteIntException("Got a symbolic BitVecRef") + raise TypeError("Got a symbolic BitVecRef") + def concrete_int_from_bytes(_bytes, start_index): @@ -105,11 +104,11 @@ def concrete_int_to_bytes(val): # logging.debug("concrete_int_to_bytes " + str(val)) - if (type(val) == int): + try: + return (simplify(val).as_long()).to_bytes(32, byteorder='big') + except Z3Exception: return val.to_bytes(32, byteorder='big') - return (simplify(val).as_long()).to_bytes(32, byteorder='big') - def bytearray_to_int(arr): o = 0 From 9ae224069841b2f3466ce942074d3284741ea5cb Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Mon, 15 Oct 2018 22:38:55 +0530 Subject: [PATCH 4/8] Change AttributeErrors to TypeErrors --- .../modules/transaction_order_dependence.py | 2 +- mythril/analysis/ops.py | 2 +- mythril/laser/ethereum/call.py | 5 ++--- mythril/laser/ethereum/instructions.py | 20 +++++++++---------- mythril/laser/ethereum/taint_analysis.py | 8 ++++---- 5 files changed, 18 insertions(+), 19 deletions(-) diff --git a/mythril/analysis/modules/transaction_order_dependence.py b/mythril/analysis/modules/transaction_order_dependence.py index f5b45f5d..f6621293 100644 --- a/mythril/analysis/modules/transaction_order_dependence.py +++ b/mythril/analysis/modules/transaction_order_dependence.py @@ -112,7 +112,7 @@ def _get_influencing_sstores(statespace, interesting_storages): index, value = sstore_state.mstate.stack[-1], sstore_state.mstate.stack[-2] try: index = util.get_concrete_int(index) - except AttributeError: + except TypeError: index = str(index) if "storage_{}".format(index) not in interesting_storages: continue diff --git a/mythril/analysis/ops.py b/mythril/analysis/ops.py index 999bbb12..b2329294 100644 --- a/mythril/analysis/ops.py +++ b/mythril/analysis/ops.py @@ -21,7 +21,7 @@ class Variable: def get_variable(i): try: return Variable(util.get_concrete_int(i), VarType.CONCRETE) - except AttributeError: + except TypeError: return Variable(simplify(i), VarType.SYMBOLIC) diff --git a/mythril/laser/ethereum/call.py b/mythril/laser/ethereum/call.py index 36062128..583c6b2e 100644 --- a/mythril/laser/ethereum/call.py +++ b/mythril/laser/ethereum/call.py @@ -48,7 +48,7 @@ def get_callee_address(global_state:GlobalState, dynamic_loader: DynLoader, symb try: callee_address = hex(util.get_concrete_int(symbolic_to_address)) - except AttributeError: + except TypeError: logging.debug("Symbolic call encountered") match = re.search(r'storage_(\d+)', str(simplify(symbolic_to_address))) @@ -113,7 +113,6 @@ def get_callee_account(global_state, callee_address, dynamic_loader): return callee_account - def get_call_data(global_state, memory_start, memory_size, pad=True): """ Gets call_data from the global_state @@ -131,7 +130,7 @@ def get_call_data(global_state, memory_start, memory_size, pad=True): call_data += [0] * (32 - len(call_data)) call_data_type = CalldataType.CONCRETE logging.debug("Calldata: " + str(call_data)) - except AttributeError: + except TypeError: logging.info("Unsupported symbolic calldata offset") call_data_type = CalldataType.SYMBOLIC call_data = [] diff --git a/mythril/laser/ethereum/instructions.py b/mythril/laser/ethereum/instructions.py index d8275423..cea603b4 100644 --- a/mythril/laser/ethereum/instructions.py +++ b/mythril/laser/ethereum/instructions.py @@ -175,7 +175,7 @@ class Instruction: result = simplify(Concat(BitVecVal(0, 248), Extract(offset + 7, offset, op1))) else: result = 0 - except AttributeError: + except TypeError: logging.debug("BYTE: Unsupported symbolic byte offset") result = global_state.new_bitvec(str(simplify(op1)) + "[" + str(simplify(op0)) + "]", 256) @@ -265,7 +265,7 @@ class Instruction: try: s0 = util.get_concrete_int(s0) s1 = util.get_concrete_int(s1) - except ValueError: + except TypeError: return [] if s0 <= 31: @@ -355,7 +355,7 @@ class Instruction: try: offset = util.get_concrete_int(simplify(op0)) b = environment.calldata[offset] - except AttributeError: + except TypeError: logging.debug("CALLDATALOAD: Unsupported symbolic index") state.stack.append(global_state.new_bitvec( "calldata_" + str(environment.active_account.contract_name) + "[" + str(simplify(op0)) + "]", 256)) @@ -514,7 +514,7 @@ class Instruction: data = b''.join([util.get_concrete_int(i).to_bytes(1, byteorder='big') for i in state.memory[index: index + length]]) - except util.ConcreteIntException: + except TypeError: argument = str(state.memory[index]).replace(" ", "_") result = BitVec("KECCAC[{}]".format(argument), 256) @@ -539,7 +539,7 @@ class Instruction: try: concrete_memory_offset = helper.get_concrete_int(memory_offset) - except AttributeError: + except TypeError: logging.debug("Unsupported symbolic memory offset in CODECOPY") return [global_state] @@ -555,7 +555,7 @@ class Instruction: try: concrete_code_offset = helper.get_concrete_int(code_offset) - except AttributeError: + except TypeError: logging.debug("Unsupported symbolic code offset in CODECOPY") global_state.mstate.mem_extend(concrete_memory_offset, concrete_size) for i in range(concrete_size): @@ -589,7 +589,7 @@ class Instruction: environment = global_state.environment try: addr = hex(helper.get_concrete_int(addr)) - except AttributeError: + except TypeError: logging.info("unsupported symbolic address for EXTCODESIZE") state.stack.append(global_state.new_bitvec("extcodesize_" + str(addr), 256)) return [global_state] @@ -663,7 +663,7 @@ class Instruction: try: offset = util.get_concrete_int(op0) - except AttributeError: + except TypeError: logging.debug("Can't MLOAD from symbolic index") data = global_state.new_bitvec("mem[" + str(simplify(op0)) + "]", 256) state.stack.append(data) @@ -998,7 +998,7 @@ class Instruction: return_data = [global_state.new_bitvec("return_data", 256)] try: return_data = state.memory[util.get_concrete_int(offset):util.get_concrete_int(offset + length)] - except AttributeError: + except TypeError: logging.debug("Return with symbolic length or offset. Not supported") global_state.current_transaction.end(global_state, return_data=return_data, revert=True) @@ -1042,7 +1042,7 @@ class Instruction: try: mem_out_start = helper.get_concrete_int(memory_out_offset) mem_out_sz = memory_out_size.as_long() - except AttributeError: + except TypeError: logging.debug("CALL with symbolic start or offset not supported") return [global_state] diff --git a/mythril/laser/ethereum/taint_analysis.py b/mythril/laser/ethereum/taint_analysis.py index 2144d864..061ab088 100644 --- a/mythril/laser/ethereum/taint_analysis.py +++ b/mythril/laser/ethereum/taint_analysis.py @@ -213,7 +213,7 @@ class TaintRunner: _ = record.stack.pop() try: index = helper.get_concrete_int(op0) - except AttributeError: + except TypeError: logging.debug("Can't MLOAD taint track symbolically") record.stack.append(False) return @@ -225,7 +225,7 @@ class TaintRunner: _, value_taint = record.stack.pop(), record.stack.pop() try: index = helper.get_concrete_int(op0) - except AttributeError: + except TypeError: logging.debug("Can't mstore taint track symbolically") return @@ -236,7 +236,7 @@ class TaintRunner: _ = record.stack.pop() try: index = helper.get_concrete_int(op0) - except AttributeError: + except TypeError: logging.debug("Can't MLOAD taint track symbolically") record.stack.append(False) return @@ -248,7 +248,7 @@ class TaintRunner: _, value_taint = record.stack.pop(), record.stack.pop() try: index = helper.get_concrete_int(op0) - except AttributeError: + except TypeError: logging.debug("Can't mstore taint track symbolically") return From 58395cb6c6c33d4e634ca8e95e23b23ecb83d349 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Mon, 15 Oct 2018 23:16:40 +0530 Subject: [PATCH 5/8] Pad 0s for the compressed hashes --- mythril/disassembler/disassembly.py | 8 +++++++- mythril/ether/asm.py | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/mythril/disassembler/disassembly.py b/mythril/disassembler/disassembly.py index 394f22b1..e31a6ba3 100644 --- a/mythril/disassembler/disassembly.py +++ b/mythril/disassembler/disassembly.py @@ -20,10 +20,16 @@ class Disassembly(object): # Parse jump table & resolve function names - jmptable_indices = asm.find_opcode_sequence(["PUSH4", "EQ"], self.instruction_list) + # Take from PUSH1 to PUSH4 because solc seems to remove excess 0s at the beginning for optimizing + jmptable_indices = asm.find_opcode_sequence([("PUSH1", "PUSH2", "PUSH3", "PUSH4"), ("EQ",)], + self.instruction_list) for i in jmptable_indices: func_hash = self.instruction_list[i]['argument'] + + # Append with missing 0s at the beginning + func_hash = "0x" + func_hash[2:].rjust(8, "0") + self.func_hashes.append(func_hash) try: # tries local cache, file and optional online lookup diff --git a/mythril/ether/asm.py b/mythril/ether/asm.py index 5e2267ea..985b2f07 100644 --- a/mythril/ether/asm.py +++ b/mythril/ether/asm.py @@ -70,13 +70,13 @@ def find_opcode_sequence(pattern, instruction_list): for i in range(0, len(instruction_list) - pattern_length + 1): - if instruction_list[i]['opcode'] == pattern[0]: + if instruction_list[i]['opcode'] in pattern[0]: matched = True for j in range(1, len(pattern)): - if not (instruction_list[i + j]['opcode'] == pattern[j]): + if not (instruction_list[i + j]['opcode'] in pattern[j]): matched = False break From 59020c6e4ae4a5ba449562108846600957ce55fc Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Mon, 15 Oct 2018 23:19:26 +0530 Subject: [PATCH 6/8] Comment correction --- mythril/disassembler/disassembly.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mythril/disassembler/disassembly.py b/mythril/disassembler/disassembly.py index e31a6ba3..93ec0c27 100644 --- a/mythril/disassembler/disassembly.py +++ b/mythril/disassembler/disassembly.py @@ -20,7 +20,7 @@ class Disassembly(object): # Parse jump table & resolve function names - # Take from PUSH1 to PUSH4 because solc seems to remove excess 0s at the beginning for optimizing + # Need to take from PUSH1 to PUSH4 because solc seems to remove excess 0s at the beginning for optimizing jmptable_indices = asm.find_opcode_sequence([("PUSH1", "PUSH2", "PUSH3", "PUSH4"), ("EQ",)], self.instruction_list) From a4eea864f8fef2484318343f64093ad10ca0b31f Mon Sep 17 00:00:00 2001 From: Yurii Rashkovskii Date: Tue, 16 Oct 2018 11:58:23 -0700 Subject: [PATCH 7/8] Problem: truffle project analysis ignores --solc-args This prevents passing custom `solc` arguments which is important in some cases (for example, `--allow-paths` is a very useful option) Solution: pass solc_args through --- mythril/support/truffle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mythril/support/truffle.py b/mythril/support/truffle.py index aced1086..6dd13b2e 100644 --- a/mythril/support/truffle.py +++ b/mythril/support/truffle.py @@ -40,7 +40,7 @@ def analyze_truffle_project(sigs, args): if len(bytecode) < 4: continue - sigs.import_from_solidity_source(contractdata['sourcePath']) + sigs.import_from_solidity_source(contractdata['sourcePath'], solc_args=args.solc_args) sigs.write() ethcontract = ETHContract(bytecode, name=name) From 0619cf2612ac9ae5048520ee0c078eef7e96808b Mon Sep 17 00:00:00 2001 From: Yurii Rashkovskii Date: Tue, 16 Oct 2018 12:01:30 -0700 Subject: [PATCH 8/8] Problem: docker build times Currenlty, every time a docker container is rebuilt for updated source code of the package, it'll start from scratch. This means it will not reuse layers with existing Python, solc, etc. installation. Solution: install Python first, copy source code after This allows to reuse the base layer. --- Dockerfile | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 3e3cb592..59c9123d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,5 @@ FROM ubuntu:bionic -COPY . /opt/mythril - RUN apt-get update \ && apt-get install -y \ build-essential \ @@ -18,8 +16,11 @@ RUN apt-get update \ python3-dev \ pandoc \ git \ - && ln -s /usr/bin/python3 /usr/local/bin/python \ - && cd /opt/mythril \ + && ln -s /usr/bin/python3 /usr/local/bin/python + +COPY . /opt/mythril + +RUN cd /opt/mythril \ && pip3 install -r requirements.txt \ && python setup.py install