|
|
@ -1,13 +1,14 @@ |
|
|
|
"""This module contains the detection code for reachable exceptions.""" |
|
|
|
"""This module contains the detection code for reachable exceptions.""" |
|
|
|
import logging |
|
|
|
import logging |
|
|
|
|
|
|
|
|
|
|
|
from typing import List |
|
|
|
from typing import List, cast |
|
|
|
from mythril.analysis import solver |
|
|
|
from mythril.analysis import solver |
|
|
|
from mythril.analysis.module.base import DetectionModule, EntryPoint |
|
|
|
from mythril.analysis.module.base import DetectionModule, EntryPoint |
|
|
|
from mythril.analysis.report import Issue |
|
|
|
from mythril.analysis.report import Issue |
|
|
|
from mythril.analysis.swc_data import ASSERT_VIOLATION |
|
|
|
from mythril.analysis.swc_data import ASSERT_VIOLATION |
|
|
|
from mythril.exceptions import UnsatError |
|
|
|
from mythril.exceptions import UnsatError |
|
|
|
from mythril.laser.ethereum.state.global_state import GlobalState |
|
|
|
from mythril.laser.ethereum.state.global_state import GlobalState |
|
|
|
|
|
|
|
from mythril.laser.ethereum.state.annotation import StateAnnotation |
|
|
|
from mythril.laser.ethereum import util |
|
|
|
from mythril.laser.ethereum import util |
|
|
|
|
|
|
|
|
|
|
|
log = logging.getLogger(__name__) |
|
|
|
log = logging.getLogger(__name__) |
|
|
@ -16,6 +17,17 @@ log = logging.getLogger(__name__) |
|
|
|
PANIC_SIGNATURE = [78, 72, 123, 113] |
|
|
|
PANIC_SIGNATURE = [78, 72, 123, 113] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class LastJumpAnnotation(StateAnnotation): |
|
|
|
|
|
|
|
""" State Annotation used if an overflow is both possible and used in the annotated path""" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, last_jump: int = None) -> None: |
|
|
|
|
|
|
|
self.last_jump: int = last_jump |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def __copy__(self): |
|
|
|
|
|
|
|
new_annotation = LastJumpAnnotation(self.last_jump) |
|
|
|
|
|
|
|
return new_annotation |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Exceptions(DetectionModule): |
|
|
|
class Exceptions(DetectionModule): |
|
|
|
"""""" |
|
|
|
"""""" |
|
|
|
|
|
|
|
|
|
|
@ -23,7 +35,7 @@ class Exceptions(DetectionModule): |
|
|
|
swc_id = ASSERT_VIOLATION |
|
|
|
swc_id = ASSERT_VIOLATION |
|
|
|
description = "Checks whether any exception states are reachable." |
|
|
|
description = "Checks whether any exception states are reachable." |
|
|
|
entry_point = EntryPoint.CALLBACK |
|
|
|
entry_point = EntryPoint.CALLBACK |
|
|
|
pre_hooks = ["ASSERT_FAIL", "REVERT"] |
|
|
|
pre_hooks = ["ASSERT_FAIL", "JUMP", "REVERT"] |
|
|
|
|
|
|
|
|
|
|
|
def _execute(self, state: GlobalState) -> None: |
|
|
|
def _execute(self, state: GlobalState) -> None: |
|
|
|
""" |
|
|
|
""" |
|
|
@ -49,6 +61,23 @@ class Exceptions(DetectionModule): |
|
|
|
:return: |
|
|
|
:return: |
|
|
|
""" |
|
|
|
""" |
|
|
|
opcode = state.get_current_instruction()["opcode"] |
|
|
|
opcode = state.get_current_instruction()["opcode"] |
|
|
|
|
|
|
|
address = state.get_current_instruction()["address"] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
annotations = cast( |
|
|
|
|
|
|
|
List[LastJumpAnnotation], |
|
|
|
|
|
|
|
[a for a in state.get_annotations(LastJumpAnnotation)], |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
if len(annotations) == 0: |
|
|
|
|
|
|
|
state.annotate(LastJumpAnnotation()) |
|
|
|
|
|
|
|
annotations = cast( |
|
|
|
|
|
|
|
List[LastJumpAnnotation], |
|
|
|
|
|
|
|
[a for a in state.get_annotations(LastJumpAnnotation)], |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if opcode == "JUMP": |
|
|
|
|
|
|
|
annotations[0].last_jump = address |
|
|
|
|
|
|
|
return [] |
|
|
|
|
|
|
|
|
|
|
|
if opcode == "REVERT" and not is_assertion_failure(state): |
|
|
|
if opcode == "REVERT" and not is_assertion_failure(state): |
|
|
|
return [] |
|
|
|
return [] |
|
|
|
|
|
|
|
|
|
|
@ -57,7 +86,6 @@ class Exceptions(DetectionModule): |
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
try: |
|
|
|
try: |
|
|
|
address = state.get_current_instruction()["address"] |
|
|
|
|
|
|
|
description_tail = ( |
|
|
|
description_tail = ( |
|
|
|
"It is possible to trigger an assertion violation. Note that Solidity assert() statements should " |
|
|
|
"It is possible to trigger an assertion violation. Note that Solidity assert() statements should " |
|
|
|
"only be used to check invariants. Review the transaction trace generated for this issue and " |
|
|
|
"only be used to check invariants. Review the transaction trace generated for this issue and " |
|
|
@ -68,6 +96,7 @@ class Exceptions(DetectionModule): |
|
|
|
transaction_sequence = solver.get_transaction_sequence( |
|
|
|
transaction_sequence = solver.get_transaction_sequence( |
|
|
|
state, state.world_state.constraints |
|
|
|
state, state.world_state.constraints |
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
issue = Issue( |
|
|
|
issue = Issue( |
|
|
|
contract=state.environment.active_account.contract_name, |
|
|
|
contract=state.environment.active_account.contract_name, |
|
|
|
function_name=state.environment.active_function_name, |
|
|
|
function_name=state.environment.active_function_name, |
|
|
@ -80,6 +109,7 @@ class Exceptions(DetectionModule): |
|
|
|
bytecode=state.environment.code.bytecode, |
|
|
|
bytecode=state.environment.code.bytecode, |
|
|
|
transaction_sequence=transaction_sequence, |
|
|
|
transaction_sequence=transaction_sequence, |
|
|
|
gas_used=(state.mstate.min_gas_used, state.mstate.max_gas_used), |
|
|
|
gas_used=(state.mstate.min_gas_used, state.mstate.max_gas_used), |
|
|
|
|
|
|
|
source_location=annotations[0].last_jump, |
|
|
|
) |
|
|
|
) |
|
|
|
return [issue] |
|
|
|
return [issue] |
|
|
|
|
|
|
|
|
|
|
|