diff --git a/mythril/analysis/symbolic.py b/mythril/analysis/symbolic.py index ce98b0ce..cc33e43c 100644 --- a/mythril/analysis/symbolic.py +++ b/mythril/analysis/symbolic.py @@ -4,6 +4,7 @@ purposes.""" from mythril.analysis.security import get_detection_module_hooks, get_detection_modules from mythril.laser.ethereum import svm +from mythril.laser.ethereum.iprof import InstructionProfiler from mythril.laser.ethereum.state.account import Account from mythril.laser.ethereum.state.world_state import WorldState from mythril.laser.ethereum.strategy.basic import ( @@ -28,7 +29,7 @@ from mythril.laser.ethereum.strategy.extensions.bounded_loops import ( BoundedLoopsStrategy, ) from mythril.laser.smt import symbol_factory, BitVec -from typing import Union, List, Type +from typing import Union, List, Type, Optional from mythril.solidity.soliditycontract import EVMContract, SolidityContract from .ops import Call, VarType, get_variable @@ -44,32 +45,38 @@ class SymExecWrapper: self, contract, address: Union[int, str, BitVec], - strategy, + strategy: str, dynloader=None, - max_depth=22, - execution_timeout=None, - loop_bound=3, - create_timeout=None, - transaction_count=2, + max_depth: int = 22, + execution_timeout: Optional[int] = None, + loop_bound: int = 3, + create_timeout: Optional[int] = None, + transaction_count: int = 2, modules=(), - compulsory_statespace=True, - enable_iprof=False, - disable_dependency_pruning=False, - run_analysis_modules=True, - enable_coverage_strategy=False, - custom_modules_directory="", + compulsory_statespace: bool = True, + iprof: Optional[InstructionProfiler] = None, + disable_dependency_pruning: bool = False, + run_analysis_modules: bool = True, + enable_coverage_strategy: bool = False, + custom_modules_directory: str = "", ): """ - :param contract: - :param address: - :param strategy: - :param dynloader: - :param max_depth: - :param execution_timeout: - :param create_timeout: - :param transaction_count: - :param modules: + :param contract: Contract to symbolically execute + :param address: Address of the contract to symbolically execute + :param strategy: Execution strategy to use (bfs, dfs, etc) + :param dynloader: Dynamic Loader + :param max_depth: Max analysis depth + :param execution_timeout: Timeout for the entire analysis + :param create_timeout: Timeout for the creation transaction + :param transaction_count: Number of transactions to symbolically execute + :param modules: Analysis modules to run during analysis + :param compulsory_statespace: Boolean indicating whether or not the statespace should be saved + :param iprof: Instruction Profiler + :param disable_dependency_pruning: Boolean indicating whether dependency pruning should be disabled + :param run_analysis_modules: Boolean indicating whether analysis modules should be executed + :param enable_coverage_strategy: Boolean indicating whether the coverage strategy should be enabled + :param custom_modules_directory: The directory to read custom analysis modules from """ if isinstance(address, str): address = symbol_factory.BitVecVal(int(address, 16), 256) @@ -116,7 +123,7 @@ class SymExecWrapper: create_timeout=create_timeout, transaction_count=transaction_count, requires_statespace=requires_statespace, - enable_iprof=enable_iprof, + iprof=iprof, enable_coverage_strategy=enable_coverage_strategy, instruction_laser_plugin=instruction_laser_plugin, ) diff --git a/mythril/laser/ethereum/svm.py b/mythril/laser/ethereum/svm.py index af52c12b..1c9c86ae 100644 --- a/mythril/laser/ethereum/svm.py +++ b/mythril/laser/ethereum/svm.py @@ -10,7 +10,6 @@ from mythril.laser.ethereum.cfg import NodeFlags, Node, Edge, JumpType from mythril.laser.ethereum.evm_exceptions import StackUnderflowException from mythril.laser.ethereum.evm_exceptions import VmException from mythril.laser.ethereum.instructions import Instruction -from mythril.laser.ethereum.iprof import InstructionProfiler from mythril.laser.ethereum.plugins.signals import PluginSkipWorldState, PluginSkipState from mythril.laser.ethereum.plugins.implementations.plugin_annotations import ( MutationAnnotation, @@ -58,7 +57,7 @@ class LaserEVM: strategy=DepthFirstSearchStrategy, transaction_count=2, requires_statespace=True, - enable_iprof=False, + iprof=None, enable_coverage_strategy=False, instruction_laser_plugin=None, ) -> None: @@ -72,7 +71,7 @@ class LaserEVM: :param strategy: Execution search strategy :param transaction_count: The amount of transactions to execute :param requires_statespace: Variable indicating whether the statespace should be recorded - :param enable_iprof: Variable indicating whether instruction profiling should be turned on + :param iprof: Instruction Profiler """ self.open_states = [] # type: List[WorldState] @@ -107,7 +106,7 @@ class LaserEVM: self._start_sym_exec_hooks = [] # type: List[Callable] self._stop_sym_exec_hooks = [] # type: List[Callable] - self.iprof = InstructionProfiler() if enable_iprof else None + self.iprof = iprof if enable_coverage_strategy: from mythril.laser.ethereum.plugins.implementations.coverage.coverage_strategy import ( diff --git a/mythril/mythril/mythril_analyzer.py b/mythril/mythril/mythril_analyzer.py index d5e87369..a7920d57 100644 --- a/mythril/mythril/mythril_analyzer.py +++ b/mythril/mythril/mythril_analyzer.py @@ -5,6 +5,7 @@ import logging import traceback from typing import Optional, List +from mythril.laser.ethereum.iprof import InstructionProfiler from . import MythrilDisassembler from mythril.support.source_support import Source from mythril.support.loader import DynLoader @@ -61,7 +62,7 @@ class MythrilAnalyzer: self.execution_timeout = execution_timeout self.loop_bound = loop_bound self.create_timeout = create_timeout - self.enable_iprof = enable_iprof + self.iprof = InstructionProfiler() if enable_iprof else None self.disable_dependency_pruning = disable_dependency_pruning self.enable_coverage_strategy = enable_coverage_strategy self.custom_modules_directory = custom_modules_directory @@ -87,7 +88,7 @@ class MythrilAnalyzer: max_depth=self.max_depth, execution_timeout=self.execution_timeout, create_timeout=self.create_timeout, - enable_iprof=self.enable_iprof, + iprof=self.iprof, disable_dependency_pruning=self.disable_dependency_pruning, run_analysis_modules=False, enable_coverage_strategy=self.enable_coverage_strategy, @@ -111,6 +112,7 @@ class MythrilAnalyzer: :param transaction_count: The amount of transactions to be executed :return: The generated graph in html format """ + sym = SymExecWrapper( contract or self.contracts[0], self.address, @@ -124,7 +126,7 @@ class MythrilAnalyzer: execution_timeout=self.execution_timeout, transaction_count=transaction_count, create_timeout=self.create_timeout, - enable_iprof=self.enable_iprof, + iprof=self.iprof, disable_dependency_pruning=self.disable_dependency_pruning, run_analysis_modules=False, enable_coverage_strategy=self.enable_coverage_strategy, @@ -164,7 +166,7 @@ class MythrilAnalyzer: transaction_count=transaction_count, modules=modules, compulsory_statespace=False, - enable_iprof=self.enable_iprof, + iprof=self.iprof, disable_dependency_pruning=self.disable_dependency_pruning, enable_coverage_strategy=self.enable_coverage_strategy, custom_modules_directory=self.custom_modules_directory, @@ -172,6 +174,8 @@ class MythrilAnalyzer: issues = fire_lasers(sym, modules, self.custom_modules_directory) except KeyboardInterrupt: log.critical("Keyboard Interrupt") + if self.iprof is not None: + log.info("Instruction Statistics:\n{}".format(self.iprof)) issues = retrieve_callback_issues( modules, self.custom_modules_directory ) @@ -184,6 +188,8 @@ class MythrilAnalyzer: modules, self.custom_modules_directory ) exceptions.append(traceback.format_exc()) + if self.iprof is not None: + log.info("Instruction Statistics:\n{}".format(self.iprof)) for issue in issues: issue.add_code_info(contract)