"""This module contains the detection code for predictable variable
dependence."""
import logging
from copy import copy
from mythril.analysis.issue_annotation import IssueAnnotation
from mythril.analysis.module.base import DetectionModule, EntryPoint
from mythril.analysis.report import Issue
from mythril.exceptions import UnsatError
from mythril.analysis import solver
from mythril.analysis.swc_data import TX_ORIGIN_USAGE
from mythril.laser.ethereum.state.global_state import GlobalState
from mythril.laser.smt import And
from typing import List
log = logging.getLogger(__name__)
[docs]class TxOriginAnnotation:
"""Symbol annotation added to a variable that is initialized with a call to the ORIGIN instruction."""
def __init__(self) -> None:
pass
[docs]class TxOrigin(DetectionModule):
"""This module detects whether control flow decisions are made based on the transaction origin."""
name = "Control flow depends on tx.origin"
swc_id = TX_ORIGIN_USAGE
description = "Check whether control flow decisions are influenced by tx.origin"
entry_point = EntryPoint.CALLBACK
pre_hooks = ["JUMPI"]
post_hooks = ["ORIGIN"]
def _execute(self, state: GlobalState) -> List[Issue]:
"""
:param state:
:return:
"""
return self._analyze_state(state)
def _analyze_state(self, state: GlobalState) -> List[Issue]:
"""
:param state:
:return:
"""
issues = []
if state.get_current_instruction()["opcode"] == "JUMPI":
# We're in JUMPI prehook
for annotation in state.mstate.stack[-2].annotations:
if isinstance(annotation, TxOriginAnnotation):
constraints = copy(state.world_state.constraints)
try:
transaction_sequence = solver.get_transaction_sequence(
state, constraints
)
except UnsatError:
continue
description = (
"The tx.origin environment variable has been found to influence a control flow decision. "
"Note that using tx.origin as a security control might cause a situation where a user "
"inadvertently authorizes a smart contract to perform an action on their behalf. It is "
"recommended to use msg.sender instead."
)
severity = "Low"
"""
Note: We report the location of the JUMPI instruction. Usually this maps to an if or
require statement.
"""
issue = Issue(
contract=state.environment.active_account.contract_name,
function_name=state.environment.active_function_name,
address=state.get_current_instruction()["address"],
swc_id=TX_ORIGIN_USAGE,
bytecode=state.environment.code.bytecode,
title="Dependence on tx.origin",
severity=severity,
description_head="Use of tx.origin as a part of authorization control.",
description_tail=description,
gas_used=(state.mstate.min_gas_used, state.mstate.max_gas_used),
transaction_sequence=transaction_sequence,
)
state.annotate(
IssueAnnotation(
conditions=[And(*constraints)], issue=issue, detector=self
)
)
issues.append(issue)
else:
# In ORIGIN posthook
state.mstate.stack[-1].annotate(TxOriginAnnotation())
return issues
detector = TxOrigin()