mirror of https://github.com/ConsenSys/mythril
parent
aaa8e132db
commit
a4118e14b8
@ -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…
Reference in new issue