diff --git a/mythril/analysis/modules/dependence_on_predictable_vars.py b/mythril/analysis/modules/dependence_on_predictable_vars.py index 34e1826c..5cb26202 100644 --- a/mythril/analysis/modules/dependence_on_predictable_vars.py +++ b/mythril/analysis/modules/dependence_on_predictable_vars.py @@ -15,10 +15,13 @@ import traceback log = logging.getLogger(__name__) predictable_ops = ["COINBASE", "GASLIMIT", "TIMESTAMP", "NUMBER"] -critical_ops = ["CALL", "SUICIDE"] +final_ops = ["CALL", "SUICIDE", "STOP"] +# One of Bernhard's trademark hacks! + def is_prehook(): + """Check if we are in prehook.""" return "pre_hook" in traceback.format_stack()[-4] @@ -32,7 +35,7 @@ class PredictableValueAnnotation: class PredictablePathAnnotation(StateAnnotation): """State annotation used when a path is chosen based on a predictable variable.""" - def __init__(self, operation, location) -> None: + def __init__(self, operation: str, location: int) -> None: self.operation = operation self.location = location @@ -58,7 +61,7 @@ class PredictableDependenceModule(DetectionModule): "block.gaslimit, block.timestamp or block.number." ), entrypoint="callback", - pre_hooks=["BLOCKHASH", "JUMPI"] + critical_ops, + pre_hooks=["BLOCKHASH", "JUMPI"] + final_ops, post_hooks=["BLOCKHASH"] + predictable_ops, ) @@ -88,7 +91,7 @@ def _analyze_states(state: GlobalState) -> list: opcode = state.get_current_instruction()["opcode"] - if opcode in critical_ops: + if opcode in final_ops: for annotation in state.annotations: @@ -105,14 +108,28 @@ def _analyze_states(state: GlobalState) -> list: "generation or to make critical control flow decisions." ) + ''' + Usually report low severity except in cases where thje hash of a previous block is used to + determine control flow. + ''' + + severity = "Medium" if "hash" in annotation.operation else "Low" + + ''' + Note: We report the location of the JUMPI that lead to this path. Usually this maps to an if or + require statement. + ''' + + swc_id = TIMESTAMP_DEPENDENCE if "timestamp" in annotation.operation else WEAK_RANDOMNESS + issue = Issue( contract=state.environment.active_account.contract_name, function_name=state.environment.active_function_name, - address=state.get_current_instruction()["address"], - swc_id=TIMESTAMP_DEPENDENCE, + address=annotation.location, + swc_id=swc_id, bytecode=state.environment.code.bytecode, title="Dependence on predictable environment variable", - severity="Low", + severity=severity, description_head="A control flow decision is made based on a predictable variable.", description_tail=description, gas_used=(state.mstate.min_gas_used, state.mstate.max_gas_used), @@ -124,6 +141,7 @@ def _analyze_states(state: GlobalState) -> list: # Look for predictable state variables in jump condition for annotation in state.mstate.stack[-2].annotations: + if isinstance(annotation, PredictableValueAnnotation): state.annotate( PredictablePathAnnotation(