@ -6,7 +6,6 @@ import logging
from copy import copy , deepcopy
from copy import copy , deepcopy
from typing import cast , Callable , List , Set , Union , Tuple , Any
from typing import cast , Callable , List , Set , Union , Tuple , Any
from datetime import datetime
from datetime import datetime
from math import ceil
from ethereum import utils
from ethereum import utils
from mythril . laser . smt import (
from mythril . laser . smt import (
@ -25,18 +24,24 @@ from mythril.laser.smt import (
Bool ,
Bool ,
Not ,
Not ,
LShR ,
LShR ,
UGE ,
)
)
from mythril . laser . smt import symbol_factory
from mythril . laser . smt import symbol_factory
from mythril . disassembler . disassembly import Disassembly
from mythril . laser . ethereum . state . calldata import ConcreteCalldata , SymbolicCalldata
import mythril . laser . ethereum . util as helper
import mythril . laser . ethereum . util as helper
from mythril . laser . ethereum import util
from mythril . laser . ethereum import util
from mythril . laser . ethereum . call import get_call_parameters , native_call
from mythril . laser . ethereum . call import get_call_parameters , native_call , get_call_data
from mythril . laser . ethereum . evm_exceptions import (
from mythril . laser . ethereum . evm_exceptions import (
VmException ,
VmException ,
StackUnderflowException ,
StackUnderflowException ,
InvalidJumpDestination ,
InvalidJumpDestination ,
InvalidInstruction ,
InvalidInstruction ,
OutOfGasException ,
OutOfGasException ,
WriteProtection ,
)
)
from mythril . laser . ethereum . gas import OPCODE_GAS
from mythril . laser . ethereum . gas import OPCODE_GAS
from mythril . laser . ethereum . state . global_state import GlobalState
from mythril . laser . ethereum . state . global_state import GlobalState
@ -44,8 +49,11 @@ from mythril.laser.ethereum.transaction import (
MessageCallTransaction ,
MessageCallTransaction ,
TransactionStartSignal ,
TransactionStartSignal ,
ContractCreationTransaction ,
ContractCreationTransaction ,
get_next_transaction_id ,
)
)
from mythril . support . support_utils import get_code_hash
from mythril . support . loader import DynLoader
from mythril . support . loader import DynLoader
log = logging . getLogger ( __name__ )
log = logging . getLogger ( __name__ )
@ -54,6 +62,30 @@ TT256 = 2 ** 256
TT256M1 = 2 * * 256 - 1
TT256M1 = 2 * * 256 - 1
def transfer_ether (
global_state : GlobalState ,
sender : BitVec ,
receiver : BitVec ,
value : Union [ int , BitVec ] ,
) :
"""
Perform an Ether transfer between two accounts
: param global_state : The global state in which the Ether transfer occurs
: param sender : The sender of the Ether
: param receiver : The recipient of the Ether
: param value : The amount of Ether to send
: return :
"""
value = value if isinstance ( value , BitVec ) else symbol_factory . BitVecVal ( value , 256 )
global_state . mstate . constraints . append (
UGE ( global_state . world_state . balances [ sender ] , value )
)
global_state . world_state . balances [ receiver ] + = value
global_state . world_state . balances [ sender ] - = value
class StateTransition ( object ) :
class StateTransition ( object ) :
""" Decorator that handles global state copy and original return.
""" Decorator that handles global state copy and original return.
@ -63,14 +95,18 @@ class StateTransition(object):
if ` increment_pc = True ` .
if ` increment_pc = True ` .
"""
"""
def __init__ ( self , increment_pc = True , enable_gas = True ) :
def __init__ (
self , increment_pc = True , enable_gas = True , is_state_mutation_instruction = False
) :
"""
"""
: param increment_pc :
: param increment_pc :
: param enable_gas :
: param enable_gas :
: param is_state_mutation_instruction : The function mutates state
"""
"""
self . increment_pc = increment_pc
self . increment_pc = increment_pc
self . enable_gas = enable_gas
self . enable_gas = enable_gas
self . is_state_mutation_instruction = is_state_mutation_instruction
@staticmethod
@staticmethod
def call_on_state_copy ( func : Callable , func_obj : " Instruction " , state : GlobalState ) :
def call_on_state_copy ( func : Callable , func_obj : " Instruction " , state : GlobalState ) :
@ -140,6 +176,13 @@ class StateTransition(object):
: param global_state :
: param global_state :
: return :
: return :
"""
"""
if self . is_state_mutation_instruction and global_state . environment . static :
raise WriteProtection (
" The function {} cannot be executed in a static call " . format (
func . __name__ [ : - 1 ]
)
)
new_global_states = self . call_on_state_copy ( func , func_obj , global_state )
new_global_states = self . call_on_state_copy ( func , func_obj , global_state )
new_global_states = [
new_global_states = [
self . accumulate_gas ( state ) for state in new_global_states
self . accumulate_gas ( state ) for state in new_global_states
@ -173,6 +216,7 @@ class Instruction:
"""
"""
# Generalize some ops
# Generalize some ops
log . debug ( " Evaluating %s at %i " , self . op_code , global_state . mstate . pc )
log . debug ( " Evaluating %s at %i " , self . op_code , global_state . mstate . pc )
op = self . op_code . lower ( )
op = self . op_code . lower ( )
if self . op_code . startswith ( " PUSH " ) :
if self . op_code . startswith ( " PUSH " ) :
op = " push "
op = " push "
@ -575,23 +619,29 @@ class Instruction:
: param global_state :
: param global_state :
: return :
: return :
"""
"""
state = global_state . mstate
m state = global_state . mstate
s0 , s1 = state . stack . pop ( ) , state . stack . pop ( )
s0 , s1 = m state. stack . pop ( ) , m state. stack . pop ( )
try :
try :
s0 = util . get_concrete_int ( s0 )
s0 = util . get_concrete_int ( s0 )
s1 = util . get_concrete_int ( s1 )
s1 = util . get_concrete_int ( s1 )
except TypeError :
except TypeError :
return [ ]
log . debug ( " Unsupported symbolic argument for SIGNEXTEND " )
mstate . stack . append (
global_state . new_bitvec (
" SIGNEXTEND( {} , {} ) " . format ( hash ( s0 ) , hash ( s1 ) ) , 256
)
)
return [ global_state ]
if s0 < = 31 :
if s0 < = 31 :
testbit = s0 * 8 + 7
testbit = s0 * 8 + 7
if s1 & ( 1 << testbit ) :
if s1 & ( 1 << testbit ) :
state . stack . append ( s1 | ( TT256 - ( 1 << testbit ) ) )
m state. stack . append ( s1 | ( TT256 - ( 1 << testbit ) ) )
else :
else :
state . stack . append ( s1 & ( ( 1 << testbit ) - 1 ) )
m state. stack . append ( s1 & ( ( 1 << testbit ) - 1 ) )
else :
else :
state . stack . append ( s1 )
m state. stack . append ( s1 )
return [ global_state ]
return [ global_state ]
@ -732,51 +782,45 @@ class Instruction:
"""
"""
state = global_state . mstate
state = global_state . mstate
environment = global_state . environment
environment = global_state . environment
if isinstance ( global_state . current_transaction , ContractCreationTransaction ) :
log . debug ( " Attempt to use CALLDATASIZE in creation transaction " )
state . stack . append ( 0 )
else :
state . stack . append ( environment . calldata . calldatasize )
state . stack . append ( environment . calldata . calldatasize )
return [ global_state ]
@StateTransition ( )
return [ global_state ]
def calldatacopy_ ( self , global_state : GlobalState ) - > List [ GlobalState ] :
"""
: param global_state :
@staticmethod
: return :
def _calldata_copy_helper ( global_state , mstate , mstart , dstart , size ) :
"""
state = global_state . mstate
environment = global_state . environment
environment = global_state . environment
op0 , op1 , op2 = state . stack . pop ( ) , state . stack . pop ( ) , state . stack . pop ( )
try :
try :
mstart = util . get_concrete_int ( op0 )
mstart = util . get_concrete_int ( mstart )
except TypeError :
except TypeError :
log . debug ( " Unsupported symbolic memory offset in CALLDATACOPY " )
log . debug ( " Unsupported symbolic memory offset in CALLDATACOPY " )
return [ global_state ]
return [ global_state ]
try :
try :
dstart = util . get_concrete_int ( op1 ) # type: Union[int, BitVec]
dstart = util . get_concrete_int ( dstart ) # type: Union[int, BitVec]
except TypeError :
except TypeError :
log . debug ( " Unsupported symbolic calldata offset in CALLDATACOPY " )
log . debug ( " Unsupported symbolic calldata offset in CALLDATACOPY " )
dstart = simplify ( op1 )
dstart = simplify ( dstart )
size_sym = False
try :
try :
size = util . get_concrete_int ( op2 ) # type: Union[int, BitVec]
size = util . get_concrete_int ( size ) # type: Union[int, BitVec]
except TypeError :
except TypeError :
log . debug ( " Unsupported symbolic size in CALLDATACOPY " )
log . debug ( " Unsupported symbolic size in CALLDATACOPY " )
size = simplify ( op2 )
size_sym = True
if size_sym :
size = 320 # The excess size will get overwritten
size = 320 # The excess size will get overwritten
size = cast ( int , size )
size = cast ( int , size )
if size > 0 :
if size > 0 :
try :
try :
state . mem_extend ( mstart , size )
m state. mem_extend ( mstart , size )
except TypeError as e :
except TypeError as e :
log . debug ( " Memory allocation error: {} " . format ( e ) )
log . debug ( " Memory allocation error: {} " . format ( e ) )
state . mem_extend ( mstart , 1 )
m state. mem_extend ( mstart , 1 )
state . memory [ mstart ] = global_state . new_bitvec (
m state. memory [ mstart ] = global_state . new_bitvec (
" calldata_ "
" calldata_ "
+ str ( environment . active_account . contract_name )
+ str ( environment . active_account . contract_name )
+ " [ "
+ " [ "
@ -801,12 +845,12 @@ class Instruction:
else simplify ( cast ( BitVec , i_data ) + 1 )
else simplify ( cast ( BitVec , i_data ) + 1 )
)
)
for i in range ( len ( new_memory ) ) :
for i in range ( len ( new_memory ) ) :
state . memory [ i + mstart ] = new_memory [ i ]
m state. memory [ i + mstart ] = new_memory [ i ]
except IndexError :
except IndexError :
log . debug ( " Exception copying calldata to memory " )
log . debug ( " Exception copying calldata to memory " )
state . memory [ mstart ] = global_state . new_bitvec (
m state. memory [ mstart ] = global_state . new_bitvec (
" calldata_ "
" calldata_ "
+ str ( environment . active_account . contract_name )
+ str ( environment . active_account . contract_name )
+ " [ "
+ " [ "
@ -818,6 +862,22 @@ class Instruction:
)
)
return [ global_state ]
return [ global_state ]
@StateTransition ( )
def calldatacopy_ ( self , global_state : GlobalState ) - > List [ GlobalState ] :
"""
: param global_state :
: return :
"""
state = global_state . mstate
op0 , op1 , op2 = state . stack . pop ( ) , state . stack . pop ( ) , state . stack . pop ( )
if isinstance ( global_state . current_transaction , ContractCreationTransaction ) :
log . debug ( " Attempt to use CALLDATACOPY in creation transaction " )
return [ global_state ]
return self . _calldata_copy_helper ( global_state , state , op0 , op1 , op2 )
# Environment
# Environment
@StateTransition ( )
@StateTransition ( )
def address_ ( self , global_state : GlobalState ) - > List [ GlobalState ] :
def address_ ( self , global_state : GlobalState ) - > List [ GlobalState ] :
@ -840,7 +900,11 @@ class Instruction:
"""
"""
state = global_state . mstate
state = global_state . mstate
address = state . stack . pop ( )
address = state . stack . pop ( )
state . stack . append ( global_state . new_bitvec ( " balance_at_ " + str ( address ) , 256 ) )
balance = global_state . world_state . balances [
global_state . environment . active_account . address
]
state . stack . append ( balance )
return [ global_state ]
return [ global_state ]
@StateTransition ( )
@StateTransition ( )
@ -877,9 +941,31 @@ class Instruction:
state = global_state . mstate
state = global_state . mstate
environment = global_state . environment
environment = global_state . environment
disassembly = environment . code
disassembly = environment . code
state . stack . append ( len ( disassembly . bytecode ) / / 2 )
calldata = global_state . environment . calldata
if isinstance ( global_state . current_transaction , ContractCreationTransaction ) :
# Hacky way to ensure constructor arguments work - Pick some reasonably large size.
no_of_bytes = len ( disassembly . bytecode ) / / 2
if isinstance ( calldata , ConcreteCalldata ) :
no_of_bytes + = calldata . size
else :
no_of_bytes + = 0x200 # space for 16 32-byte arguments
global_state . mstate . constraints . append (
global_state . environment . calldata . size == no_of_bytes
)
else :
no_of_bytes = len ( disassembly . bytecode ) / / 2
state . stack . append ( no_of_bytes )
return [ global_state ]
return [ global_state ]
@staticmethod
def _sha3_gas_helper ( global_state , length ) :
min_gas , max_gas = cast ( Callable , OPCODE_GAS [ " SHA3_FUNC " ] ) ( length )
global_state . mstate . min_gas_used + = min_gas
global_state . mstate . max_gas_used + = max_gas
StateTransition . check_gas_usage_limit ( global_state )
return global_state
@StateTransition ( enable_gas = False )
@StateTransition ( enable_gas = False )
def sha3_ ( self , global_state : GlobalState ) - > List [ GlobalState ] :
def sha3_ ( self , global_state : GlobalState ) - > List [ GlobalState ] :
"""
"""
@ -905,10 +991,7 @@ class Instruction:
state . max_gas_used + = gas_tuple [ 1 ]
state . max_gas_used + = gas_tuple [ 1 ]
return [ global_state ]
return [ global_state ]
min_gas , max_gas = cast ( Callable , OPCODE_GAS [ " SHA3_FUNC " ] ) ( length )
Instruction . _sha3_gas_helper ( global_state , length )
state . min_gas_used + = min_gas
state . max_gas_used + = max_gas
StateTransition . check_gas_usage_limit ( global_state )
state . mem_extend ( index , length )
state . mem_extend ( index , length )
data_list = [
data_list = [
@ -961,34 +1044,6 @@ class Instruction:
global_state . mstate . stack . append ( global_state . environment . gasprice )
global_state . mstate . stack . append ( global_state . environment . gasprice )
return [ global_state ]
return [ global_state ]
@staticmethod
def _handle_symbolic_args (
global_state : GlobalState , concrete_memory_offset : int
) - > None :
"""
In contract creation transaction with dynamic arguments ( like arrays , maps ) solidity will try to
execute CODECOPY with code size as len ( with_args ) - len ( without_args ) which in our case
would be 0 , hence we are writing 10 symbol words onto the memory on the assumption that
no one would use 10 array / map arguments for constructor .
: param global_state : The global state
: param concrete_memory_offset : The memory offset on which symbols should be written
"""
no_of_words = ceil (
min ( len ( global_state . environment . code . bytecode ) / 2 , 320 ) / 32
)
global_state . mstate . mem_extend ( concrete_memory_offset , 32 * no_of_words )
for i in range ( no_of_words ) :
global_state . mstate . memory . write_word_at (
concrete_memory_offset + i * 32 ,
global_state . new_bitvec (
" code_ {} ( {} ) " . format (
concrete_memory_offset + i * 32 ,
global_state . environment . active_account . contract_name ,
) ,
256 ,
) ,
)
@StateTransition ( )
@StateTransition ( )
def codecopy_ ( self , global_state : GlobalState ) - > List [ GlobalState ] :
def codecopy_ ( self , global_state : GlobalState ) - > List [ GlobalState ] :
"""
"""
@ -1001,6 +1056,63 @@ class Instruction:
global_state . mstate . stack . pop ( ) ,
global_state . mstate . stack . pop ( ) ,
global_state . mstate . stack . pop ( ) ,
global_state . mstate . stack . pop ( ) ,
)
)
code = global_state . environment . code . bytecode
if code [ 0 : 2 ] == " 0x " :
code = code [ 2 : ]
code_size = len ( code ) / / 2
if isinstance ( global_state . current_transaction , ContractCreationTransaction ) :
# Treat creation code after the expected disassembly as calldata.
# This is a slightly hacky way to ensure that symbolic constructor
# arguments work correctly.
mstate = global_state . mstate
offset = code_offset - code_size
log . debug ( " Copying from code offset: {} with size: {} " . format ( offset , size ) )
if isinstance ( global_state . environment . calldata , SymbolicCalldata ) :
if code_offset > = code_size :
return self . _calldata_copy_helper (
global_state , mstate , memory_offset , offset , size
)
else :
# Copy from both code and calldata appropriately.
concrete_code_offset = helper . get_concrete_int ( code_offset )
concrete_size = helper . get_concrete_int ( size )
code_copy_offset = concrete_code_offset
code_copy_size = (
concrete_size
if concrete_code_offset + concrete_size < = code_size
else code_size - concrete_code_offset
)
code_copy_size = code_copy_size if code_copy_size > = 0 else 0
calldata_copy_offset = (
concrete_code_offset - code_size
if concrete_code_offset - code_size > 0
else 0
)
calldata_copy_size = concrete_code_offset + concrete_size - code_size
calldata_copy_size = (
calldata_copy_size if calldata_copy_size > = 0 else 0
)
[ global_state ] = self . _code_copy_helper (
code = global_state . environment . code . bytecode ,
memory_offset = memory_offset ,
code_offset = code_copy_offset ,
size = code_copy_size ,
op = " CODECOPY " ,
global_state = global_state ,
)
return self . _calldata_copy_helper (
global_state = global_state ,
mstate = mstate ,
mstart = memory_offset + code_copy_size ,
dstart = calldata_copy_offset ,
size = calldata_copy_size ,
)
return self . _code_copy_helper (
return self . _code_copy_helper (
code = global_state . environment . code . bytecode ,
code = global_state . environment . code . bytecode ,
memory_offset = memory_offset ,
memory_offset = memory_offset ,
@ -1041,9 +1153,9 @@ class Instruction:
@staticmethod
@staticmethod
def _code_copy_helper (
def _code_copy_helper (
code : str ,
code : str ,
memory_offset : BitVec ,
memory_offset : Union [ int , BitVec ] ,
code_offset : BitVec ,
code_offset : Union [ int , BitVec ] ,
size : BitVec ,
size : Union [ int , BitVec ] ,
op : str ,
op : str ,
global_state : GlobalState ,
global_state : GlobalState ,
) - > List [ GlobalState ] :
) - > List [ GlobalState ] :
@ -1089,13 +1201,6 @@ class Instruction:
if code [ 0 : 2 ] == " 0x " :
if code [ 0 : 2 ] == " 0x " :
code = code [ 2 : ]
code = code [ 2 : ]
if concrete_size == 0 and isinstance (
global_state . current_transaction , ContractCreationTransaction
) :
if concrete_code_offset > = len ( code ) / / 2 :
Instruction . _handle_symbolic_args ( global_state , concrete_memory_offset )
return [ global_state ]
for i in range ( concrete_size ) :
for i in range ( concrete_size ) :
if 2 * ( concrete_code_offset + i + 1 ) < = len ( code ) :
if 2 * ( concrete_code_offset + i + 1 ) < = len ( code ) :
global_state . mstate . memory [ concrete_memory_offset + i ] = int (
global_state . mstate . memory [ concrete_memory_offset + i ] = int (
@ -1155,18 +1260,26 @@ class Instruction:
global_state = global_state ,
global_state = global_state ,
)
)
@StateTransition
@StateTransition ( )
def extcodehash_ ( self , global_state : GlobalState ) - > List [ GlobalState ] :
def extcodehash_ ( self , global_state : GlobalState ) - > List [ GlobalState ] :
"""
"""
: param global_state :
: param global_state :
: return : List of global states possible , list of size 1 in this case
: return : List of global states possible , list of size 1 in this case
"""
"""
# TODO: To be implemented
world_state = global_state . world_state
address = global_state . mstate . stack . pop ( )
stack = global_state . mstate . stack
global_state . mstate . stack . append (
address = Extract ( 159 , 0 , stack . pop ( ) )
global_state . new_bitvec ( " extcodehash_ {} " . format ( str ( address ) ) , 256 )
)
if address . symbolic :
code_hash = symbol_factory . BitVecVal ( int ( get_code_hash ( " " ) , 16 ) , 256 )
elif address . value not in world_state . accounts :
code_hash = symbol_factory . BitVecVal ( 0 , 256 )
else :
addr = " 0 " * ( 40 - len ( hex ( address . value ) [ 2 : ] ) ) + hex ( address . value ) [ 2 : ]
code = world_state . accounts_exist_or_load ( addr , self . dynamic_loader )
code_hash = symbol_factory . BitVecVal ( int ( get_code_hash ( code ) , 16 ) , 256 )
stack . append ( code_hash )
return [ global_state ]
return [ global_state ]
@StateTransition ( )
@StateTransition ( )
@ -1368,11 +1481,10 @@ class Instruction:
state = global_state . mstate
state = global_state . mstate
index = state . stack . pop ( )
index = state . stack . pop ( )
state . stack . append ( global_state . environment . active_account . storage [ index ] )
state . stack . append ( global_state . environment . active_account . storage [ index ] )
return [ global_state ]
return [ global_state ]
@StateTransition ( )
@StateTransition ( is_state_mutation_instruction = True )
def sstore_ ( self , global_state : GlobalState ) - > List [ GlobalState ] :
def sstore_ ( self , global_state : GlobalState ) - > List [ GlobalState ] :
"""
"""
@ -1381,7 +1493,6 @@ class Instruction:
"""
"""
state = global_state . mstate
state = global_state . mstate
index , value = state . stack . pop ( ) , state . stack . pop ( )
index , value = state . stack . pop ( ) , state . stack . pop ( )
global_state . environment . active_account . storage [ index ] = value
global_state . environment . active_account . storage [ index ] = value
return [ global_state ]
return [ global_state ]
@ -1540,7 +1651,7 @@ class Instruction:
global_state . mstate . stack . append ( global_state . new_bitvec ( " gas " , 256 ) )
global_state . mstate . stack . append ( global_state . new_bitvec ( " gas " , 256 ) )
return [ global_state ]
return [ global_state ]
@StateTransition ( )
@StateTransition ( is_state_mutation_instruction = True )
def log_ ( self , global_state : GlobalState ) - > List [ GlobalState ] :
def log_ ( self , global_state : GlobalState ) - > List [ GlobalState ] :
"""
"""
@ -1555,37 +1666,112 @@ class Instruction:
# Not supported
# Not supported
return [ global_state ]
return [ global_state ]
@StateTransition ( )
def _create_transaction_helper (
self , global_state , call_value , mem_offset , mem_size , create2_salt = None
) :
mstate = global_state . mstate
environment = global_state . environment
world_state = global_state . world_state
call_data = get_call_data ( global_state , mem_offset , mem_offset + mem_size )
code_raw = [ ]
code_end = call_data . size
for i in range ( call_data . size ) :
if call_data [ i ] . symbolic :
code_end = i
break
code_raw . append ( call_data [ i ] . value )
code_str = bytes . hex ( bytes ( code_raw ) )
next_transaction_id = get_next_transaction_id ( )
constructor_arguments = ConcreteCalldata (
next_transaction_id , call_data [ code_end : ]
)
code = Disassembly ( code_str )
caller = environment . active_account . address
gas_price = environment . gasprice
origin = environment . origin
contract_address = None
if create2_salt :
salt = hex ( create2_salt ) [ 2 : ]
salt = " 0 " * ( 64 - len ( salt ) ) + salt
addr = hex ( caller . value ) [ 2 : ]
addr = " 0 " * ( 40 - len ( addr ) ) + addr
Instruction . _sha3_gas_helper ( global_state , len ( code_str [ 2 : ] / / 2 ) )
contract_address = int (
get_code_hash ( " 0xff " + addr + salt + get_code_hash ( code_str ) [ 2 : ] ) [ 26 : ] ,
16 ,
)
transaction = ContractCreationTransaction (
world_state = world_state ,
caller = caller ,
code = code ,
call_data = constructor_arguments ,
gas_price = gas_price ,
gas_limit = mstate . gas_limit ,
origin = origin ,
call_value = call_value ,
contract_address = contract_address ,
)
raise TransactionStartSignal ( transaction , self . op_code , global_state )
@StateTransition ( is_state_mutation_instruction = True )
def create_ ( self , global_state : GlobalState ) - > List [ GlobalState ] :
def create_ ( self , global_state : GlobalState ) - > List [ GlobalState ] :
"""
"""
: param global_state :
: param global_state :
: return :
: return :
"""
"""
# TODO: implement me
call_value , mem_offset , mem_size = global_state . mstate . pop ( 3 )
state = global_state . mstate
state . stack . pop ( ) , state . stack . pop ( ) , state . stack . pop ( )
return self . _create_transaction_helper (
# Not supported
global_state , call_value , mem_offset , mem_size
state . stack . append ( 0 )
)
return [ global_state ]
@StateTransition ( )
@StateTransition ( )
def create_post ( self , global_state : GlobalState ) - > List [ GlobalState ] :
call_value , mem_offset , mem_size = global_state . mstate . pop ( 3 )
call_data = get_call_data ( global_state , mem_offset , mem_offset + mem_size )
if global_state . last_return_data :
return_val = symbol_factory . BitVecVal (
int ( global_state . last_return_data , 16 ) , 256
)
else :
return_val = symbol_factory . BitVecVal ( 0 , 256 )
global_state . mstate . stack . append ( return_val )
return [ global_state ]
@StateTransition ( is_state_mutation_instruction = True )
def create2_ ( self , global_state : GlobalState ) - > List [ GlobalState ] :
def create2_ ( self , global_state : GlobalState ) - > List [ GlobalState ] :
"""
"""
: param global_state :
: param global_state :
: return :
: return :
"""
"""
# TODO: implement me
call_value , mem_offset , mem_size , salt = global_state . mstate . pop ( 4 )
state = global_state . mstate
endowment , memory_start , memory_length , salt = (
return self . _create_transaction_helper (
state . stack . pop ( ) ,
global_state , call_value , mem_offset , mem_size , salt
state . stack . pop ( ) ,
state . stack . pop ( ) ,
state . stack . pop ( ) ,
)
)
# Not supported
state . stack . append ( 0 )
@StateTransition ( )
def create2_post ( self , global_state : GlobalState ) - > List [ GlobalState ] :
call_value , mem_offset , mem_size , salt = global_state . mstate . pop ( 4 )
call_data = get_call_data ( global_state , mem_offset , mem_offset + mem_size )
if global_state . last_return_data :
return_val = symbol_factory . BitVecVal (
int ( global_state . last_return_data ) , 256
)
else :
return_val = symbol_factory . BitVecVal ( 0 , 256 )
global_state . mstate . stack . append ( return_val )
return [ global_state ]
return [ global_state ]
@StateTransition ( )
@StateTransition ( )
@ -1605,7 +1791,7 @@ class Instruction:
return_data = state . memory [ offset : offset + length ]
return_data = state . memory [ offset : offset + length ]
global_state . current_transaction . end ( global_state , return_data )
global_state . current_transaction . end ( global_state , return_data )
@StateTransition ( )
@StateTransition ( is_state_mutation_instruction = True )
def suicide_ ( self , global_state : GlobalState ) :
def suicide_ ( self , global_state : GlobalState ) :
"""
"""
@ -1615,7 +1801,7 @@ class Instruction:
transfer_amount = global_state . environment . active_account . balance ( )
transfer_amount = global_state . environment . active_account . balance ( )
# Often the target of the suicide instruction will be symbolic
# Often the target of the suicide instruction will be symbolic
# If it isn't then we'll transfer the balance to the indicated contract
# If it isn't then we'll transfer the balance to the indicated contract
global_state . world_state [ target ] . add_balance ( transfer_amount )
global_state . world_state . balances [ target ] + = transfer_amount
global_state . environment . active_account = deepcopy (
global_state . environment . active_account = deepcopy (
global_state . environment . active_account
global_state . environment . active_account
@ -1689,6 +1875,10 @@ class Instruction:
if callee_account is not None and callee_account . code . bytecode == " " :
if callee_account is not None and callee_account . code . bytecode == " " :
log . debug ( " The call is related to ether transfer between accounts " )
log . debug ( " The call is related to ether transfer between accounts " )
sender = environment . active_account . address
receiver = callee_account . address
transfer_ether ( global_state , sender , receiver , value )
global_state . mstate . stack . append (
global_state . mstate . stack . append (
global_state . new_bitvec ( " retval_ " + str ( instr [ " address " ] ) , 256 )
global_state . new_bitvec ( " retval_ " + str ( instr [ " address " ] ) , 256 )
)
)
@ -1706,6 +1896,21 @@ class Instruction:
)
)
return [ global_state ]
return [ global_state ]
if environment . static :
if isinstance ( value , int ) and value > 0 :
raise WriteProtection (
" Cannot call with non zero value in a static call "
)
if isinstance ( value , BitVec ) :
if value . symbolic :
global_state . mstate . constraints . append (
value == symbol_factory . BitVecVal ( 0 , 256 )
)
elif value . value > 0 :
raise WriteProtection (
" Cannot call with non zero value in a static call "
)
native_result = native_call (
native_result = native_call (
global_state , callee_address , call_data , memory_out_offset , memory_out_size
global_state , callee_address , call_data , memory_out_offset , memory_out_size
)
)
@ -1720,20 +1925,81 @@ class Instruction:
callee_account = callee_account ,
callee_account = callee_account ,
call_data = call_data ,
call_data = call_data ,
call_value = value ,
call_value = value ,
static = environment . static ,
)
)
raise TransactionStartSignal ( transaction , self . op_code )
raise TransactionStartSignal ( transaction , self . op_code , global_state )
@StateTransition ( )
@StateTransition ( )
def call_post ( self , global_state : GlobalState ) - > List [ GlobalState ] :
def call_post ( self , global_state : GlobalState ) - > List [ GlobalState ] :
"""
"""
: param global_state :
: return :
"""
return self . post_handler ( global_state , function_name = " call " )
@StateTransition ( )
def callcode_ ( self , global_state : GlobalState ) - > List [ GlobalState ] :
"""
: param global_state :
: param global_state :
: return :
: return :
"""
"""
instr = global_state . get_current_instruction ( )
instr = global_state . get_current_instruction ( )
environment = global_state . environment
try :
try :
callee_address , callee_account , call_data , value , gas , memory_out_offset , memory_out_size = get_call_parameters (
callee_address , callee_account , call_data , value , gas , _ , _ = get_call_parameters (
global_state , self . dynamic_loader , True
)
if callee_account is not None and callee_account . code . bytecode == " " :
log . debug ( " The call is related to ether transfer between accounts " )
sender = global_state . environment . active_account . address
receiver = callee_account . address
transfer_ether ( global_state , sender , receiver , value )
global_state . mstate . stack . append (
global_state . new_bitvec ( " retval_ " + str ( instr [ " address " ] ) , 256 )
)
return [ global_state ]
except ValueError as e :
log . debug (
" Could not determine required parameters for call, putting fresh symbol on the stack. \n {} " . format (
e
)
)
global_state . mstate . stack . append (
global_state . new_bitvec ( " retval_ " + str ( instr [ " address " ] ) , 256 )
)
return [ global_state ]
transaction = MessageCallTransaction (
world_state = global_state . world_state ,
gas_price = environment . gasprice ,
gas_limit = gas ,
origin = environment . origin ,
code = callee_account . code ,
caller = environment . address ,
callee_account = environment . active_account ,
call_data = call_data ,
call_value = value ,
static = environment . static ,
)
raise TransactionStartSignal ( transaction , self . op_code , global_state )
@StateTransition ( )
def callcode_post ( self , global_state : GlobalState ) - > List [ GlobalState ] :
"""
: param global_state :
: return :
"""
instr = global_state . get_current_instruction ( )
try :
callee_address , _ , _ , value , _ , memory_out_offset , memory_out_size = get_call_parameters (
global_state , self . dynamic_loader , True
global_state , self . dynamic_loader , True
)
)
except ValueError as e :
except ValueError as e :
@ -1754,7 +2020,6 @@ class Instruction:
)
)
global_state . mstate . stack . append ( return_value )
global_state . mstate . stack . append ( return_value )
global_state . mstate . constraints . append ( return_value == 0 )
global_state . mstate . constraints . append ( return_value == 0 )
return [ global_state ]
return [ global_state ]
try :
try :
@ -1787,11 +2052,10 @@ class Instruction:
return_value = global_state . new_bitvec ( " retval_ " + str ( instr [ " address " ] ) , 256 )
return_value = global_state . new_bitvec ( " retval_ " + str ( instr [ " address " ] ) , 256 )
global_state . mstate . stack . append ( return_value )
global_state . mstate . stack . append ( return_value )
global_state . mstate . constraints . append ( return_value == 1 )
global_state . mstate . constraints . append ( return_value == 1 )
return [ global_state ]
return [ global_state ]
@StateTransition ( )
@StateTransition ( )
def callcode _ ( self , global_state : GlobalState ) - > List [ GlobalState ] :
def delegate call_( self , global_state : GlobalState ) - > List [ GlobalState ] :
"""
"""
: param global_state :
: param global_state :
@ -1802,8 +2066,20 @@ class Instruction:
try :
try :
callee_address , callee_account , call_data , value , gas , _ , _ = get_call_parameters (
callee_address , callee_account , call_data , value , gas , _ , _ = get_call_parameters (
global_state , self . dynamic_loader , True
global_state , self . dynamic_loader
)
)
if callee_account is not None and callee_account . code . bytecode == " " :
log . debug ( " The call is related to ether transfer between accounts " )
sender = global_state . environment . active_account . address
receiver = callee_account . address
transfer_ether ( global_state , sender , receiver , value )
global_state . mstate . stack . append (
global_state . new_bitvec ( " retval_ " + str ( instr [ " address " ] ) , 256 )
)
return [ global_state ]
except ValueError as e :
except ValueError as e :
log . debug (
log . debug (
" Could not determine required parameters for call, putting fresh symbol on the stack. \n {} " . format (
" Could not determine required parameters for call, putting fresh symbol on the stack. \n {} " . format (
@ -1821,15 +2097,16 @@ class Instruction:
gas_limit = gas ,
gas_limit = gas ,
origin = environment . origin ,
origin = environment . origin ,
code = callee_account . code ,
code = callee_account . code ,
caller = environment . address ,
caller = environment . sender ,
callee_account = environment . active_account ,
callee_account = environment . active_account ,
call_data = call_data ,
call_data = call_data ,
call_value = value ,
call_value = environment . callvalue ,
static = environment . static ,
)
)
raise TransactionStartSignal ( transaction , self . op_code )
raise TransactionStartSignal ( transaction , self . op_code , global_state )
@StateTransition ( )
@StateTransition ( )
def callcode _post ( self , global_state : GlobalState ) - > List [ GlobalState ] :
def delegate call_post( self , global_state : GlobalState ) - > List [ GlobalState ] :
"""
"""
: param global_state :
: param global_state :
@ -1839,7 +2116,7 @@ class Instruction:
try :
try :
callee_address , _ , _ , value , _ , memory_out_offset , memory_out_size = get_call_parameters (
callee_address , _ , _ , value , _ , memory_out_offset , memory_out_size = get_call_parameters (
global_state , self . dynamic_loader , True
global_state , self . dynamic_loader
)
)
except ValueError as e :
except ValueError as e :
log . debug (
log . debug (
@ -1894,7 +2171,7 @@ class Instruction:
return [ global_state ]
return [ global_state ]
@StateTransition ( )
@StateTransition ( )
def delegate call_( self , global_state : GlobalState ) - > List [ GlobalState ] :
def static call_( self , global_state : GlobalState ) - > List [ GlobalState ] :
"""
"""
: param global_state :
: param global_state :
@ -1902,11 +2179,22 @@ class Instruction:
"""
"""
instr = global_state . get_current_instruction ( )
instr = global_state . get_current_instruction ( )
environment = global_state . environment
environment = global_state . environment
try :
try :
callee_address , callee_account , call_data , value , gas , _ , _ = get_call_parameters (
callee_address , callee_account , call_data , value , gas , memory _out_offset , memory _out_size = get_call_parameters (
global_state , self . dynamic_loader
global_state , self . dynamic_loader
)
)
if callee_account is not None and callee_account . code . bytecode == " " :
log . debug ( " The call is related to ether transfer between accounts " )
sender = environment . active_account . address
receiver = callee_account . address
transfer_ether ( global_state , sender , receiver , value )
global_state . mstate . stack . append (
global_state . new_bitvec ( " retval_ " + str ( instr [ " address " ] ) , 256 )
)
return [ global_state ]
except ValueError as e :
except ValueError as e :
log . debug (
log . debug (
" Could not determine required parameters for call, putting fresh symbol on the stack. \n {} " . format (
" Could not determine required parameters for call, putting fresh symbol on the stack. \n {} " . format (
@ -1918,36 +2206,43 @@ class Instruction:
)
)
return [ global_state ]
return [ global_state ]
native_result = native_call (
global_state , callee_address , call_data , memory_out_offset , memory_out_size
)
if native_result :
return native_result
transaction = MessageCallTransaction (
transaction = MessageCallTransaction (
world_state = global_state . world_state ,
world_state = global_state . world_state ,
gas_price = environment . gasprice ,
gas_price = environment . gasprice ,
gas_limit = gas ,
gas_limit = gas ,
origin = environment . origin ,
origin = environment . origin ,
code = callee_account . code ,
code = callee_account . code ,
caller = environment . sender ,
caller = environment . address ,
callee_account = environment . activ e_account,
callee_account = calle e_account,
call_data = call_data ,
call_data = call_data ,
call_value = environment . callvalue ,
call_value = value ,
static = True ,
)
)
raise TransactionStartSignal ( transaction , self . op_code )
raise TransactionStartSignal ( transaction , self . op_code , global_state )
@StateTransition ( )
@StateTransition ( )
def delegate call_post( self , global_state : GlobalState ) - > List [ GlobalState ] :
def static call_post( self , global_state : GlobalState ) - > List [ GlobalState ] :
"""
return self . post_handler ( global_state , function_name = " staticcall " )
: param global_state :
def post_handler ( self , global_state , function_name : str ) :
: return :
"""
instr = global_state . get_current_instruction ( )
instr = global_state . get_current_instruction ( )
try :
try :
callee_address , _ , _ , value , _ , memory_out_offset , memory_out_size = get_call_parameters (
with_value = function_name is not " staticcall "
global_state , self . dynamic_loader
callee_address , callee_account , call_data , value , gas , memory_out_offset , memory_out_size = get_call_parameters (
global_state , self . dynamic_loader , with_value
)
)
except ValueError as e :
except ValueError as e :
log . debug (
log . debug (
" Could not determine required parameters for call , putting fresh symbol on the stack. \n {} " . format (
" Could not determine required parameters for {} , putting fresh symbol on the stack. \n {} " . format (
e
function_name , e
)
)
)
)
global_state . mstate . stack . append (
global_state . mstate . stack . append (
@ -1993,39 +2288,5 @@ class Instruction:
return_value = global_state . new_bitvec ( " retval_ " + str ( instr [ " address " ] ) , 256 )
return_value = global_state . new_bitvec ( " retval_ " + str ( instr [ " address " ] ) , 256 )
global_state . mstate . stack . append ( return_value )
global_state . mstate . stack . append ( return_value )
global_state . mstate . constraints . append ( return_value == 1 )
global_state . mstate . constraints . append ( return_value == 1 )
return [ global_state ]
@StateTransition ( )
def staticcall_ ( self , global_state : GlobalState ) - > List [ GlobalState ] :
"""
: param global_state :
: return :
"""
# TODO: implement me
instr = global_state . get_current_instruction ( )
try :
callee_address , callee_account , call_data , value , gas , memory_out_offset , memory_out_size = get_call_parameters (
global_state , self . dynamic_loader
)
except ValueError as e :
log . debug (
" Could not determine required parameters for call, putting fresh symbol on the stack. \n {} " . format (
e
)
)
global_state . mstate . stack . append (
global_state . new_bitvec ( " retval_ " + str ( instr [ " address " ] ) , 256 )
)
return [ global_state ]
native_result = native_call (
global_state , callee_address , call_data , memory_out_offset , memory_out_size
)
if native_result :
return native_result
global_state . mstate . stack . append (
global_state . new_bitvec ( " retval_ " + str ( instr [ " address " ] ) , 256 )
)
return [ global_state ]
return [ global_state ]