Merge branch 'develop' into arb_jump

pull/1269/head
JoranHonig 5 years ago committed by GitHub
commit ea935cf04f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 137
      mythril/analysis/modules/dos.py

@ -1,137 +0,0 @@
"""This module contains the detection code SWC-128 - DOS with block gas limit."""
import logging
from typing import Dict, cast, List
from mythril.analysis.swc_data import DOS_WITH_BLOCK_GAS_LIMIT
from mythril.analysis.report import Issue
from mythril.analysis.modules.base import DetectionModule
from mythril.analysis.solver import get_transaction_sequence, UnsatError
from mythril.analysis.analysis_args import analysis_args
from mythril.laser.ethereum.state.global_state import GlobalState
from mythril.laser.ethereum.state.annotation import StateAnnotation
from mythril.laser.ethereum import util
from copy import copy
log = logging.getLogger(__name__)
class VisitsAnnotation(StateAnnotation):
"""State annotation that stores the addresses of state-modifying operations"""
def __init__(self) -> None:
self.loop_start = None # type: int
self.jump_targets = {} # type: Dict[int, int]
def __copy__(self):
result = VisitsAnnotation()
result.loop_start = self.loop_start
result.jump_targets = copy(self.jump_targets)
return result
class DosModule(DetectionModule):
"""This module consists of a makeshift loop detector that annotates the state with
a list of byte ranges likely to be loops. If a CALL or SSTORE detection is found in
one of the ranges it creates a low-severity issue. This is not super precise but
good enough to identify places that warrant a closer look. Checking the loop condition
would be a possible improvement.
"""
def __init__(self) -> None:
""""""
super().__init__(
name="DOS",
swc_id=DOS_WITH_BLOCK_GAS_LIMIT,
description="Check for DOS",
entrypoint="callback",
pre_hooks=["JUMP", "JUMPI", "CALL", "SSTORE"],
)
def _execute(self, state: GlobalState) -> None:
"""
:param state:
:return:
"""
issues = self._analyze_state(state)
self.issues.extend(issues)
def _analyze_state(self, state: GlobalState) -> List[Issue]:
"""
:param state: the current state
:return: returns the issues for that corresponding state
"""
opcode = state.get_current_instruction()["opcode"]
address = state.get_current_instruction()["address"]
annotations = cast(
List[VisitsAnnotation], list(state.get_annotations(VisitsAnnotation))
)
if len(annotations) == 0:
annotation = VisitsAnnotation()
state.annotate(annotation)
else:
annotation = annotations[0]
if opcode in ["JUMP", "JUMPI"]:
if annotation.loop_start is not None:
return []
try:
target = util.get_concrete_int(state.mstate.stack[-1])
except TypeError:
log.debug("Symbolic target encountered in dos module")
return []
if target in annotation.jump_targets:
annotation.jump_targets[target] += 1
else:
annotation.jump_targets[target] = 1
if annotation.jump_targets[target] > min(2, analysis_args.loop_bound - 1):
annotation.loop_start = address
elif annotation.loop_start is not None:
if opcode == "CALL":
operation = "A message call"
else:
operation = "A storage modification"
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
)
try:
transaction_sequence = get_transaction_sequence(
state, state.mstate.constraints
)
except UnsatError:
return []
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),
transaction_sequence=transaction_sequence,
)
return [issue]
return []
detector = DosModule()
Loading…
Cancel
Save