from mythril . analysis . modules . delegatecall import execute , _concrete_call , _symbolic_call
from mythril . analysis . ops import Call , Variable , VarType
from mythril . analysis . symbolic import SymExecWrapper
from mythril . disassembler . disassembly import Disassembly
from mythril . laser . ethereum . cfg import Node
from mythril . laser . ethereum . state import GlobalState , Environment , Account
import pytest
from unittest . mock import MagicMock , patch
import pytest_mock
def test_concrete_call ( ) :
# arrange
address = " 0x10 "
active_account = Account ( address )
active_account . code = Disassembly ( " 00 " )
environment = Environment ( active_account , None , None , None , None , None )
state = GlobalState ( None , environment , None )
state . mstate . memory = [ " placeholder " , " calldata_bling_0 " ]
node = Node ( " example " )
node . contract_name = " the contract name "
node . function_name = " the function name "
to = Variable ( 1 , VarType . CONCRETE )
meminstart = Variable ( 1 , VarType . CONCRETE )
call = Call ( node , state , None , None , to , None )
# act
issues = _concrete_call ( call , state , address , meminstart )
# assert
issue = issues [ 0 ]
assert issue . address == address
assert issue . contract == node . contract_name
assert issue . function == node . function_name
assert issue . title == " Call data forwarded with delegatecall() "
assert issue . type == ' Informational '
assert issue . description == " This contract forwards its call data via DELEGATECALL in its fallback function. " \
" This means that any function in the called contract can be executed. " \
" Note that the callee contract will have access to the storage of the " \
" calling contract. \n DELEGATECALL target: 0x1 "
def test_concrete_call_symbolic_to ( ) :
# arrange
address = " 0x10 "
active_account = Account ( address )
active_account . code = Disassembly ( " 00 " )
environment = Environment ( active_account , None , None , None , None , None )
state = GlobalState ( None , environment , None )
state . mstate . memory = [ " placeholder " , " calldata_bling_0 " ]
node = Node ( " example " )
node . contract_name = " the contract name "
node . function_name = " the function name "
to = Variable ( " calldata_3 " , VarType . SYMBOLIC )
meminstart = Variable ( 1 , VarType . CONCRETE )
call = Call ( node , state , None , None , to , None )
# act
issues = _concrete_call ( call , state , address , meminstart )
# assert
issue = issues [ 0 ]
assert issue . address == address
assert issue . contract == node . contract_name
assert issue . function == node . function_name
assert issue . title == " Call data forwarded with delegatecall() "
assert issue . type == ' Informational '
assert issue . description == " This contract forwards its call data via DELEGATECALL in its fallback function. " \
" This means that any function in the called contract can be executed. " \
" Note that the callee contract will have access to the storage of the " \
" calling contract. \n DELEGATECALL target: calldata_3 "
def test_concrete_call_not_calldata ( ) :
# arrange
state = GlobalState ( None , None , None )
state . mstate . memory = [ " placeholder " , " not_calldata " ]
meminstart = Variable ( 1 , VarType . CONCRETE )
# act
issues = _concrete_call ( None , state , None , meminstart )
# assert
assert issues == [ ]
def test_symbolic_call_storage_to ( mocker ) :
# arrange
address = " 0x10 "
active_account = Account ( address )
active_account . code = Disassembly ( " 00 " )
environment = Environment ( active_account , None , None , None , None , None )
state = GlobalState ( None , environment , None )
state . mstate . memory = [ " placeholder " , " calldata_bling_0 " ]
node = Node ( " example " )
node . contract_name = " the contract name "
node . function_name = " the function name "
to = Variable ( " storage_1 " , VarType . SYMBOLIC )
call = Call ( node , state , None , " Type: " , to , None )
mocker . patch . object ( SymExecWrapper , " __init__ " , lambda x , y : None )
statespace = SymExecWrapper ( 1 )
mocker . patch . object ( statespace , ' find_storage_write ' )
statespace . find_storage_write . return_value = " Function name "
# act
issues = _symbolic_call ( call , state , address , statespace )
# assert
issue = issues [ 0 ]
assert issue . address == address
assert issue . contract == node . contract_name
assert issue . function == node . function_name
assert issue . title == ' Type: to a user-supplied address '
assert issue . type == ' Informational '
assert issue . description == ' This contract delegates execution to a contract address in storage slot 1. ' \
' This storage slot can be written to by calling the function `Function name`. ' \
' Be aware that the called contract gets unrestricted access to this contract \' s state. '
def test_symbolic_call_calldata_to ( mocker ) :
# arrange
address = " 0x10 "
active_account = Account ( address )
active_account . code = Disassembly ( " 00 " )
environment = Environment ( active_account , None , None , None , None , None )
state = GlobalState ( None , environment , None )
state . mstate . memory = [ " placeholder " , " calldata_bling_0 " ]
node = Node ( " example " )
node . contract_name = " the contract name "
node . function_name = " the function name "
to = Variable ( " calldata " , VarType . SYMBOLIC )
call = Call ( node , state , None , " Type: " , to , None )
mocker . patch . object ( SymExecWrapper , " __init__ " , lambda x , y : None )
statespace = SymExecWrapper ( 1 )
mocker . patch . object ( statespace , ' find_storage_write ' )
statespace . find_storage_write . return_value = " Function name "
# act
issues = _symbolic_call ( call , state , address , statespace )
# assert
issue = issues [ 0 ]
assert issue . address == address
assert issue . contract == node . contract_name
assert issue . function == node . function_name
assert issue . title == ' Type: to a user-supplied address '
assert issue . type == ' Informational '
assert issue . description == ' This contract delegates execution to a contract address obtained from calldata. ' \
' Be aware that the called contract gets unrestricted access to this contract \' s state. '
@patch ( ' mythril.laser.ethereum.state.GlobalState.get_current_instruction ' )
@patch ( ' mythril.analysis.modules.delegatecall._concrete_call ' )
@patch ( ' mythril.analysis.modules.delegatecall._symbolic_call ' )
def test_delegate_call ( sym_mock , concrete_mock , curr_instruction ) :
# arrange
# sym_mock = mocker.patch.object(delegatecall, "_symbolic_call")
# concrete_mock = mocker.patch.object(delegatecall, "_concrete_call")
sym_mock . return_value = [ ]
concrete_mock . return_value = [ ]
curr_instruction . return_value = { ' address ' : ' 0x10 ' }
active_account = Account ( ' 0x10 ' )
active_account . code = Disassembly ( " 00 " )
environment = Environment ( active_account , None , None , None , None , None )
state = GlobalState ( None , environment , Node )
state . mstate . memory = [ " placeholder " , " calldata_bling_0 " ]
state . mstate . stack = [ 1 , 2 , 3 ]
assert state . get_current_instruction ( ) == { ' address ' : ' 0x10 ' }
node = Node ( " example " )
node . contract_name = " the contract name "
node . function_name = " fallback "
to = Variable ( " storage_1 " , VarType . SYMBOLIC )
call = Call ( node , state , None , " DELEGATECALL " , to , None )
statespace = MagicMock ( )
statespace . calls = [ call ]
# act
execute ( statespace )
# assert
assert concrete_mock . call_count == 1
assert sym_mock . call_count == 1
@patch ( ' mythril.analysis.modules.delegatecall._concrete_call ' )
@patch ( ' mythril.analysis.modules.delegatecall._symbolic_call ' )
def test_delegate_call_not_delegate ( sym_mock , concrete_mock ) :
# arrange
# sym_mock = mocker.patch.object(delegatecall, "_symbolic_call")
# concrete_mock = mocker.patch.object(delegatecall, "_concrete_call")
sym_mock . return_value = [ ]
concrete_mock . return_value = [ ]
node = Node ( " example " )
node . function_name = " fallback "
to = Variable ( " storage_1 " , VarType . SYMBOLIC )
call = Call ( node , None , None , " NOT_DELEGATECALL " , to , None )
statespace = MagicMock ( )
statespace . calls = [ call ]
# act
issues = execute ( statespace )
# assert
assert issues == [ ]
assert concrete_mock . call_count == 0
assert sym_mock . call_count == 0
@patch ( ' mythril.analysis.modules.delegatecall._concrete_call ' )
@patch ( ' mythril.analysis.modules.delegatecall._symbolic_call ' )
def test_delegate_call_not_fallback ( sym_mock , concrete_mock ) :
# arrange
# sym_mock = mocker.patch.object(delegatecall, "_symbolic_call")
# concrete_mock = mocker.patch.object(delegatecall, "_concrete_call")
sym_mock . return_value = [ ]
concrete_mock . return_value = [ ]
node = Node ( " example " )
node . function_name = " not_fallback "
to = Variable ( " storage_1 " , VarType . SYMBOLIC )
call = Call ( node , None , None , " DELEGATECALL " , to , None )
statespace = MagicMock ( )
statespace . calls = [ call ]
# act
issues = execute ( statespace )
# assert
assert issues == [ ]
assert concrete_mock . call_count == 0
assert sym_mock . call_count == 0