@ -1,9 +1,7 @@
from z3 import *
import re
from mythril . analysis . ops import *
from mythril . analysis . report import Issue
from laser . ethereum . svm import NodeFlags
import logging
from laser . ethereum import helper
import re
'''
@ -30,55 +28,67 @@ def execute(statespace):
logging . debug ( " Executing module: UNCHECKED_RETVAL " )
issues = [ ]
visited = [ ]
for call in statespace . calls :
for k in statespace . nodes :
node = statespace . nodes [ k ]
if NodeFlags . CALL_RETURN in node . flags :
retval_checked = False
for state in node . states :
state = call . state
address = state . get_current_instruction ( ) [ ' address ' ]
instr = state . get_current_instruction ( )
# Only needs to be checked once per call instructions (it's essentially just static analysis)
if ( instr [ ' opcode ' ] == ' ISZERO ' and re . search ( r ' retval ' , str ( state . mstate . stack [ - 1 ] ) ) ) :
retval_checked = True
break
if not retval_checked :
address = state . get_current_instruction ( ) [ ' address ' ]
issue = Issue ( node . contract_name , node . function_name , address , " Unchecked CALL return value " )
issue . description = \
" The return value of an external call is not checked. Note that the contract will continue if the call fails. "
issues . append ( issue )
if call . state . mstate . pc in visited :
continue
else :
visited . append ( call . state . mstate . pc )
retval_checked = False
nStates = len ( node . states )
for idx in range ( 0 , nStates ) :
# ISZERO retval is expected to be found within the next few instructions.
state = node . states [ idx ]
instr = state . get_current_instruction ( )
for i in range ( 0 , 10 ) :
if ( instr [ ' opcode ' ] == ' CALL ' ) :
_state = call . node . states [ call . state_index + i ]
retval_checked = False
try :
instr = _state . get_current_instruction ( )
except IndexError :
break
for _idx in range ( idx , idx + 10 ) :
if ( instr [ ' opcode ' ] == ' ISZERO ' and re . search ( r ' retval ' , str ( _state . mstate . stack [ - 1 ] ) ) ) :
retval_checked = True
break
try :
_state = node . states [ _idx ]
_instr = _state . get_current_instruction ( )
if not retval_checked :
if ( _instr [ ' opcode ' ] == ' ISZERO ' and re . search ( r ' retval ' , str ( _state . mstate . stack [ - 1 ] ) ) ) :
retval_checked = True
break
issue = Issue ( call . node . contract_name , call . node . function_name , address , " Unchecked CALL return value " )
except IndexError :
break
if ( call . to . type == VarType . CONCRETE ) :
receiver = hex ( call . to . val )
elif ( re . search ( r " caller " , str ( call . to ) ) ) :
receiver = " msg.sender "
elif ( re . search ( r " storage " , str ( call . to ) ) ) :
receiver = " an address obtained from storage "
else :
receiver = str ( call . to )
if not retval_checked :
address = instr [ ' address ' ]
issue = Issue ( node . contract_name , node . function_name , address , " Unchecked CALL return value " )
issue . description = \
" The function " + call . node . function_name + " contains a call to " + receiver + " . \n " \
" The return value of this call is not checked. Note that the function will continue to execute with a return value of ' 0 ' if the called contract throws. "
issue . description = \
" The return value of an external call is not checked. Note that execution continue even if the called contract throws. "
issues . append ( issue )
issues . append ( issue )
return issues