mirror of https://github.com/ConsenSys/mythril
parent
f3c45c153e
commit
df886eb81c
@ -0,0 +1,159 @@ |
||||
"""This module contains the detection code for transaction order dependence |
||||
calls.""" |
||||
|
||||
from mythril.analysis import solver |
||||
from mythril.analysis.potential_issues import ( |
||||
PotentialIssue, |
||||
get_potential_issues_annotation, |
||||
) |
||||
from mythril.analysis.swc_data import TX_ORDER_DEPENDENCE |
||||
from mythril.laser.ethereum.state.constraints import Constraints |
||||
from mythril.laser.ethereum.transaction.symbolic import ACTORS |
||||
from mythril.laser.ethereum.transaction.transaction_models import ( |
||||
ContractCreationTransaction, |
||||
) |
||||
from mythril.analysis.modules.base import DetectionModule |
||||
from mythril.laser.smt import UGT, symbol_factory, Or, BitVec |
||||
from mythril.laser.ethereum.natives import PRECOMPILE_COUNT |
||||
from mythril.laser.ethereum.state.global_state import GlobalState |
||||
from mythril.exceptions import UnsatError |
||||
from copy import copy |
||||
import logging |
||||
|
||||
log = logging.getLogger(__name__) |
||||
|
||||
DESCRIPTION = """ |
||||
|
||||
Search for low level calls (e.g. call.value()) that forward all gas to the callee. |
||||
Report a warning if the callee address can be set by the sender, otherwise create |
||||
an informational issue. |
||||
|
||||
""" |
||||
|
||||
|
||||
def _is_precompile_call(global_state: GlobalState): |
||||
to = global_state.mstate.stack[-2] # type: BitVec |
||||
constraints = copy(global_state.world_state.constraints) |
||||
constraints += [ |
||||
Or( |
||||
to < symbol_factory.BitVecVal(1, 256), |
||||
to > symbol_factory.BitVecVal(PRECOMPILE_COUNT, 256), |
||||
) |
||||
] |
||||
|
||||
try: |
||||
solver.get_model(constraints) |
||||
return False |
||||
except UnsatError: |
||||
return True |
||||
|
||||
|
||||
class TransactionOrderDependence(DetectionModule): |
||||
"""This module searches for transaction order dependence.""" |
||||
|
||||
def __init__(self): |
||||
"""""" |
||||
super().__init__( |
||||
name="Transaction Order Dependence", |
||||
swc_id=TX_ORDER_DEPENDENCE, |
||||
description=DESCRIPTION, |
||||
entrypoint="callback", |
||||
pre_hooks=["CALL"], |
||||
) |
||||
|
||||
def _execute(self, state: GlobalState) -> None: |
||||
""" |
||||
|
||||
:param state: |
||||
:return: |
||||
""" |
||||
potential_issues = self._analyze_state(state) |
||||
|
||||
annotation = get_potential_issues_annotation(state) |
||||
annotation.potential_issues.extend(potential_issues) |
||||
|
||||
def _analyze_state(self, state: GlobalState): |
||||
""" |
||||
|
||||
:param state: |
||||
:return: |
||||
""" |
||||
gas = state.mstate.stack[-1] |
||||
to = state.mstate.stack[-2] |
||||
|
||||
address = state.get_current_instruction()["address"] |
||||
|
||||
try: |
||||
constraints = Constraints([UGT(gas, symbol_factory.BitVecVal(2300, 256))]) |
||||
|
||||
solver.get_transaction_sequence( |
||||
state, constraints + state.world_state.constraints |
||||
) |
||||
|
||||
# Check whether we can also set the callee address |
||||
|
||||
try: |
||||
new_constraints = constraints + [to == ACTORS.attacker] |
||||
|
||||
solver.get_transaction_sequence( |
||||
state, new_constraints + state.world_state.constraints |
||||
) |
||||
|
||||
description_head = "A call to a user-supplied address is executed." |
||||
description_tail = ( |
||||
"The callee address of an external message call can be set by " |
||||
"the caller. Note that the callee can contain arbitrary code and may re-enter any function " |
||||
"in this contract. Review the business logic carefully to prevent averse effects on the " |
||||
"contract state." |
||||
) |
||||
|
||||
issue = PotentialIssue( |
||||
contract=state.environment.active_account.contract_name, |
||||
function_name=state.environment.active_function_name, |
||||
address=address, |
||||
swc_id=TransactionOrderDependence, |
||||
title="External Call To User-Supplied Address", |
||||
bytecode=state.environment.code.bytecode, |
||||
severity="Medium", |
||||
description_head=description_head, |
||||
description_tail=description_tail, |
||||
constraints=new_constraints, |
||||
detector=self, |
||||
) |
||||
|
||||
except UnsatError: |
||||
if _is_precompile_call(state): |
||||
return [] |
||||
|
||||
log.debug( |
||||
"[EXTERNAL_CALLS] Callee address cannot be modified. Reporting informational issue." |
||||
) |
||||
|
||||
description_head = "The contract executes an external message call." |
||||
description_tail = ( |
||||
"An external function call to a fixed contract address is executed. Make sure " |
||||
"that the callee contract has been reviewed carefully." |
||||
) |
||||
|
||||
issue = PotentialIssue( |
||||
contract=state.environment.active_account.contract_name, |
||||
function_name=state.environment.active_function_name, |
||||
address=address, |
||||
swc_id=TX_ORDER_DEPENDENCE, |
||||
title="Transaction Order Dependence", |
||||
bytecode=state.environment.code.bytecode, |
||||
severity="Low", |
||||
description_head=description_head, |
||||
description_tail=description_tail, |
||||
constraints=constraints, |
||||
detector=self, |
||||
) |
||||
|
||||
except UnsatError: |
||||
log.debug("[EXTERNAL_CALLS] No model found.") |
||||
return [] |
||||
|
||||
return [issue] |
||||
|
||||
|
||||
detector = ExternalCalls() |
Loading…
Reference in new issue