Source code for mythril.analysis.module.modules.multiple_sends

"""This module contains the detection code to find multiple sends occurring in
a single transaction."""
from copy import copy
from typing import cast, List
from mythril.analysis.issue_annotation import IssueAnnotation
from mythril.analysis.report import Issue
from mythril.analysis.solver import get_transaction_sequence, UnsatError
from mythril.analysis.swc_data import MULTIPLE_SENDS
from mythril.analysis.module.base import DetectionModule, EntryPoint
from mythril.laser.ethereum.state.annotation import StateAnnotation
from mythril.laser.ethereum.state.global_state import GlobalState
from mythril.laser.smt import And
import logging

log = logging.getLogger(__name__)


[docs]class MultipleSendsAnnotation(StateAnnotation): def __init__(self) -> None: self.call_offsets = [] # type: List[int] def __copy__(self): result = MultipleSendsAnnotation() result.call_offsets = copy(self.call_offsets) return result
[docs]class MultipleSends(DetectionModule): """This module checks for multiple sends in a single transaction.""" name = "Multiple external calls in the same transaction" swc_id = MULTIPLE_SENDS description = "Check for multiple sends in a single transaction" entry_point = EntryPoint.CALLBACK pre_hooks = ["CALL", "DELEGATECALL", "STATICCALL", "CALLCODE", "RETURN", "STOP"] def _execute(self, state: GlobalState) -> None: return self._analyze_state(state) def _analyze_state(self, state: GlobalState): """ :param state: the current state :return: returns the issues for that corresponding state """ instruction = state.get_current_instruction() annotations = cast( List[MultipleSendsAnnotation], list(state.get_annotations(MultipleSendsAnnotation)), ) if len(annotations) == 0: state.annotate(MultipleSendsAnnotation()) annotations = cast( List[MultipleSendsAnnotation], list(state.get_annotations(MultipleSendsAnnotation)), ) call_offsets = annotations[0].call_offsets if instruction["opcode"] in ["CALL", "DELEGATECALL", "STATICCALL", "CALLCODE"]: call_offsets.append(state.get_current_instruction()["address"]) else: # RETURN or STOP for offset in call_offsets[1:]: try: transaction_sequence = get_transaction_sequence( state, state.world_state.constraints ) except UnsatError: continue description_tail = ( "This call is executed following another call within the same transaction. It is possible " "that the call never gets executed if a prior call fails permanently. This might be caused " "intentionally by a malicious callee. If possible, refactor the code such that each transaction " "only executes one external call or " "make sure that all callees can be trusted (i.e. they’re part of your own codebase)." ) issue = Issue( contract=state.environment.active_account.contract_name, function_name=state.environment.active_function_name, address=offset, swc_id=MULTIPLE_SENDS, bytecode=state.environment.code.bytecode, title="Multiple Calls in a Single Transaction", severity="Low", description_head="Multiple calls are executed in the same transaction.", description_tail=description_tail, gas_used=(state.mstate.min_gas_used, state.mstate.max_gas_used), transaction_sequence=transaction_sequence, ) state.annotate( IssueAnnotation( conditions=[And(*state.world_state.constraints)], issue=issue, detector=self, ) ) return [issue] return []
detector = MultipleSends()