Reduce default loop bound to 2 and simplify DOS module

pull/1110/head
Bernhard Mueller 5 years ago
parent b24dc32f82
commit 667ca755ba
  1. 109
      mythril/analysis/modules/dos.py
  2. 2
      mythril/analysis/symbolic.py

@ -8,18 +8,23 @@ from mythril.analysis.report import Issue
from mythril.analysis.modules.base import DetectionModule from mythril.analysis.modules.base import DetectionModule
from mythril.laser.ethereum.state.global_state import GlobalState from mythril.laser.ethereum.state.global_state import GlobalState
from mythril.laser.ethereum.state.annotation import StateAnnotation from mythril.laser.ethereum.state.annotation import StateAnnotation
from mythril.laser.ethereum import util from copy import copy
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class LoopAnnotation(StateAnnotation): class VisitsAnnotation(StateAnnotation):
def __init__(self, loop_start: int, loop_end: int) -> None: """State annotation that stores the addresses of state-modifying operations"""
self.loop_start = loop_start
self.loop_end = loop_end
def contains(self, address: int) -> bool: def __init__(self) -> None:
return self.loop_start < address < self.loop_end self.last_jumpdest = 0
self._visited = {} # type: Dict[int, str]
def __copy__(self):
result = VisitsAnnotation()
result.last_jumpdest = self.last_jumpdest
result._visited = copy(self._visited)
return result
class DOS(DetectionModule): class DOS(DetectionModule):
@ -37,12 +42,9 @@ class DOS(DetectionModule):
swc_id=DOS_WITH_BLOCK_GAS_LIMIT, swc_id=DOS_WITH_BLOCK_GAS_LIMIT,
description="Check for DOS", description="Check for DOS",
entrypoint="callback", entrypoint="callback",
pre_hooks=["JUMPI", "CALL", "SSTORE"], pre_hooks=["JUMPDEST", "CALL", "SSTORE"],
) )
"""Keeps track of how often jump destinations are reached."""
self._jumpdest_count = {} # type: Dict[object, dict]
def _execute(self, state: GlobalState) -> None: def _execute(self, state: GlobalState) -> None:
""" """
@ -61,65 +63,50 @@ class DOS(DetectionModule):
opcode = state.get_current_instruction()["opcode"] opcode = state.get_current_instruction()["opcode"]
address = state.get_current_instruction()["address"] address = state.get_current_instruction()["address"]
if opcode == "JUMPI": annotations = cast(
List[VisitsAnnotation], list(state.get_annotations(VisitsAnnotation))
target = util.get_concrete_int(state.mstate.stack[-1]) )
transaction = state.current_transaction
if state.current_transaction in self._jumpdest_count:
try: if len(annotations) == 0:
self._jumpdest_count[transaction][target] += 1 annotation = VisitsAnnotation()
if self._jumpdest_count[transaction][target] == 3: state.annotate(annotation)
else:
annotation = annotations[0]
annotation = ( if opcode == "JUMPDEST":
LoopAnnotation(address, target) annotation.last_jumpdest == address
if target > address
else LoopAnnotation(target, address)
)
state.annotate(annotation) if address in annotation._visited:
except KeyError:
self._jumpdest_count[transaction][target] = 0
if annotation._visited[address] == "CALL":
operation = "A message call"
else: else:
self._jumpdest_count[transaction] = {} operation = "A storage modification"
self._jumpdest_count[transaction][target] = 0
else: description_head = (
"Potential denial-of-service if block gas limit is reached."
)
description_tail = "{} is executed in a loop. Be aware that the transaction may fail to execute if the loop is unbounded and the necessary gas exceeds the block gas limit. ".format(
operation
)
annotations = cast( issue = Issue(
List[LoopAnnotation], list(state.get_annotations(LoopAnnotation)) contract=state.environment.active_account.contract_name,
function_name=state.environment.active_function_name,
address=annotation.last_jumpdest,
swc_id=DOS_WITH_BLOCK_GAS_LIMIT,
bytecode=state.environment.code.bytecode,
title="Potential denial-of-service if block gas limit is reached",
severity="Low",
description_head=description_head,
description_tail=description_tail,
gas_used=(state.mstate.min_gas_used, state.mstate.max_gas_used),
) )
for annotation in annotations: return [issue]
if annotation.contains(address): else:
annotation._visited[address] = opcode
operation = (
"A storage modification"
if opcode == "SSTORE"
else "An external call"
)
description_head = (
"Potential denial-of-service if block gas limit is reached."
)
description_tail = "{} is executed in a loop.".format(operation)
issue = Issue(
contract=state.environment.active_account.contract_name,
function_name=state.environment.active_function_name,
address=annotation.loop_start,
swc_id=DOS_WITH_BLOCK_GAS_LIMIT,
bytecode=state.environment.code.bytecode,
title="Potential denial-of-service if block gas limit is reached",
severity="Low",
description_head=description_head,
description_tail=description_tail,
gas_used=(state.mstate.min_gas_used, state.mstate.max_gas_used),
)
return [issue]
return [] return []

@ -50,7 +50,7 @@ class SymExecWrapper:
dynloader=None, dynloader=None,
max_depth=22, max_depth=22,
execution_timeout=None, execution_timeout=None,
loop_bound=4, loop_bound=2,
create_timeout=None, create_timeout=None,
transaction_count=2, transaction_count=2,
modules=(), modules=(),

Loading…
Cancel
Save