Merge pull request #986 from ConsenSys/maintenance/use_instruction_coverage_plugin

Use instruction coverage plugin
pull/997/head
JoranHonig 6 years ago committed by GitHub
commit 71f1b95ee1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      mythril/analysis/symbolic.py
  2. 1
      mythril/laser/ethereum/plugins/implementations/benchmark.py
  3. 40
      mythril/laser/ethereum/plugins/implementations/coverage.py
  4. 59
      mythril/laser/ethereum/svm.py

@ -89,6 +89,7 @@ class SymExecWrapper:
plugin_loader = LaserPluginLoader(self.laser)
plugin_loader.load(PluginFactory.build_mutation_pruner_plugin())
plugin_loader.load(PluginFactory.build_instruction_coverage_plugin())
self.laser.register_hooks(
hook_type="pre",

@ -7,6 +7,7 @@ import logging
log = logging.getLogger(__name__)
# TODO: introduce dependency on coverage plugin
class BenchmarkPlugin(LaserPlugin):
"""Benchmark Plugin

@ -21,6 +21,11 @@ class InstructionCoveragePlugin(LaserPlugin):
"""
def __init__(self):
self.coverage = {} # type: Dict[str, Tuple[int, List[bool]]]
self.initial_coverage = 0
self.tx_id = 0
def initialize(self, symbolic_vm: LaserEVM):
"""Initializes the instruction coverage plugin
@ -28,12 +33,14 @@ class InstructionCoveragePlugin(LaserPlugin):
:param symbolic_vm:
:return:
"""
coverage = {} # type: Dict[str, Tuple[int, List[bool]]]
self.coverage = {}
self.initial_coverage = 0
self.tx_id = 0
@symbolic_vm.laser_hook("stop_sym_exec")
def stop_sym_exec_hook():
# Print results
for code, code_cov in coverage.items():
for code, code_cov in self.coverage.items():
cov_percentage = sum(code_cov[1]) / float(code_cov[0]) * 100
log.info(
@ -47,13 +54,36 @@ class InstructionCoveragePlugin(LaserPlugin):
# Record coverage
code = global_state.environment.code.bytecode
if code not in coverage.keys():
if code not in self.coverage.keys():
number_of_instructions = len(
global_state.environment.code.instruction_list
)
coverage[code] = (
self.coverage[code] = (
number_of_instructions,
[False] * number_of_instructions,
)
coverage[code][1][global_state.mstate.pc] = True
self.coverage[code][1][global_state.mstate.pc] = True
@symbolic_vm.laser_hook("start_sym_trans")
def execute_start_sym_trans_hook():
self.initial_coverage = self._get_covered_instructions()
@symbolic_vm.laser_hook("stop_sym_trans")
def execute_stop_sym_trans_hook():
end_coverage = self._get_covered_instructions()
log.info(
"Number of new instructions covered in tx %d: %d"
% (self.tx_id, end_coverage - self.initial_coverage)
)
self.tx_id += 1
def _get_covered_instructions(self) -> int:
"""Gets the total number of covered instructions for all accounts in
the svm.
:return:
"""
total_covered_instructions = 0
for _, cv in self.coverage.items():
total_covered_instructions += sum(cv[1])
return total_covered_instructions

@ -72,8 +72,6 @@ class LaserEVM:
self.world_state = world_state
self.open_states = [world_state]
self.coverage = {} # type: Dict[str, Tuple[int, List[bool]]]
self.total_states = 0
self.dynamic_loader = dynamic_loader
@ -97,6 +95,10 @@ class LaserEVM:
self._add_world_state_hooks = [] # type: List[Callable]
self._execute_state_hooks = [] # type: List[Callable]
self._start_sym_trans_hooks = [] # type: List[Callable]
self._stop_sym_trans_hooks = [] # type: List[Callable]
self._start_sym_exec_hooks = [] # type: List[Callable]
self._stop_sym_exec_hooks = [] # type: List[Callable]
@ -158,10 +160,6 @@ class LaserEVM:
len(self.edges),
self.total_states,
)
for code, coverage in self.coverage.items():
cov = sum(coverage[1]) / float(coverage[0]) * 100
log.info("Achieved {:.2f}% coverage for code: {}".format(cov, code))
if self.iprof is not None:
log.info("Instruction Statistics:\n{}".format(self.iprof))
@ -170,42 +168,25 @@ class LaserEVM:
hook()
def _execute_transactions(self, address):
"""This function executes multiple transactions on the address based on
the coverage.
"""This function executes multiple transactions on the address
:param address: Address of the contract
:return:
"""
self.coverage = {}
for i in range(self.transaction_count):
initial_coverage = self._get_covered_instructions()
self.time = datetime.now()
log.info(
"Starting message call transaction, iteration: {}, {} initial states".format(
i, len(self.open_states)
)
)
for hook in self._start_sym_trans_hooks:
hook()
execute_message_call(self, address)
end_coverage = self._get_covered_instructions()
log.info(
"Number of new instructions covered in tx %d: %d"
% (i, end_coverage - initial_coverage)
)
def _get_covered_instructions(self) -> int:
"""Gets the total number of covered instructions for all accounts in
the svm.
:return:
"""
total_covered_instructions = 0
for _, cv in self.coverage.items():
total_covered_instructions += sum(cv[1])
return total_covered_instructions
for hook in self._stop_sym_trans_hooks:
hook()
def exec(self, create=False, track_gas=False) -> Union[List[GlobalState], None]:
"""
@ -284,7 +265,6 @@ class LaserEVM:
self._execute_pre_hook(op_code, global_state)
try:
self._measure_coverage(global_state)
new_global_states = Instruction(
op_code, self.dynamic_loader, self.iprof
).evaluate(global_state)
@ -389,23 +369,6 @@ class LaserEVM:
return new_global_states
def _measure_coverage(self, global_state: GlobalState) -> None:
"""
:param global_state:
"""
code = global_state.environment.code.bytecode
number_of_instructions = len(global_state.environment.code.instruction_list)
instruction_index = global_state.mstate.pc
if code not in self.coverage.keys():
self.coverage[code] = (
number_of_instructions,
[False] * number_of_instructions,
)
self.coverage[code][1][instruction_index] = True
def manage_cfg(self, opcode: str, new_states: List[GlobalState]) -> None:
"""
@ -527,6 +490,10 @@ class LaserEVM:
self._start_sym_exec_hooks.append(hook)
elif hook_type == "stop_sym_exec":
self._stop_sym_exec_hooks.append(hook)
elif hook_type == "start_sym_trans":
self._start_sym_trans_hooks.append(hook)
elif hook_type == "stop_sym_trans":
self._stop_sym_trans_hooks.append(hook)
else:
raise ValueError(
"Invalid hook type %s. Must be one of {add_world_state}", hook_type

Loading…
Cancel
Save