EVM refactor and simple UI (#629)
* Fixes symbolic reentrancy example * Fix coverage Issue# 527 * Remove debug unused code * New solidity biased API and reporting * Updated examples to new api WIP * simple_mapping FIXED. new api * Simple transaction example added. msg.value can be symbolic now * Reentrancy symbolic now updated to new API + bugfixes * Doc and cleanups in evm assembler * EVMInstruction -> Instruction * cleanups * typo * deepcopy in Constant * Better EVM-asm api and doc * some docs * More evm asm docs * Initial seth in place refactor * Fix import * * typo * newline between text and param * similar phrasing to all the other flags * typo * typo * fix function name in comment * sphinx newline * documentation fixes * documentation fixes * refactors * EVMAssembler to EVMAsm * Fix evm @hook signature * EVMAsm * WIP seth doc * WIP move seth * seth moved to manticore module * Fixed DUP and typo * Slightly better evm reporting * review * review * Removed unfinished refactor * Various refactors. Auxiliar for calculating % coverage * Change report in examples * Detailed transactions and reporting accessible to the user2 * Fix on Expression Array * Some documentation * Get full ABI from solc compiler * evm/examples -> bugfixes * Clarify try/except blocks * Code review * Code review
This commit is contained in:
@@ -30,7 +30,7 @@ seth.transaction( caller=user_account,
|
||||
print "[+] There are %d reverted states now"% len(seth.final_state_ids)
|
||||
print "[+] There are %d alive states now"% len(seth.running_state_ids)
|
||||
for state_id in seth.running_state_ids:
|
||||
seth.report(state_id)
|
||||
print seth.report(state_id)
|
||||
|
||||
print "[+] Global coverage: %x"% contract_account
|
||||
print seth.coverage(contract_account)
|
||||
|
||||
@@ -27,11 +27,11 @@ contract_account.add(seth.SValue)
|
||||
|
||||
print "[+] There are %d reverted states now"% len(seth.final_state_ids)
|
||||
for state_id in seth.final_state_ids:
|
||||
seth.report(state_id)
|
||||
print seth.report(state_id)
|
||||
|
||||
print "[+] There are %d alive states now"% len(seth.running_state_ids)
|
||||
for state_id in seth.running_state_ids:
|
||||
seth.report(state_id)
|
||||
print seth.report(state_id)
|
||||
|
||||
print "[+] Global coverage: %x"% contract_account
|
||||
print seth.coverage(contract_account)
|
||||
|
||||
@@ -2,7 +2,6 @@ from manticore.seth import ManticoreEVM
|
||||
################ Script #######################
|
||||
|
||||
seth = ManticoreEVM()
|
||||
seth.verbosity(0)
|
||||
#And now make the contract account to analyze
|
||||
# cat | solc --bin
|
||||
source_code = '''
|
||||
@@ -42,7 +41,7 @@ print "[+] There are %d reverted states now"% len(seth.final_state_ids)
|
||||
|
||||
print "[+] There are %d alive states now"% len(seth.running_state_ids)
|
||||
for state_id in seth.running_state_ids:
|
||||
seth.report(state_id)
|
||||
print seth.report(state_id)
|
||||
|
||||
print "[+] Global coverage:"
|
||||
print seth.coverage(contract_account)
|
||||
|
||||
@@ -127,11 +127,11 @@ print " contract_account %x balance: %d"% (contract_account, seth.get_balance(c
|
||||
|
||||
print "[+] There are %d reverted states now"% len(seth.final_state_ids)
|
||||
for state_id in seth.final_state_ids:
|
||||
seth.report(state_id)
|
||||
print seth.report(state_id)
|
||||
|
||||
print "[+] There are %d alive states now"% (len(seth.running_state_ids))
|
||||
for state_id in seth.running_state_ids:
|
||||
seth.report(state_id)
|
||||
print seth.report(state_id)
|
||||
|
||||
print "[+] Global coverage:"
|
||||
print seth.coverage(contract_account)
|
||||
|
||||
@@ -119,7 +119,7 @@ exploit_account.get_money()
|
||||
|
||||
print "[+] There are %d alive states now"% (len(seth.running_state_ids))
|
||||
for state_id in seth.running_state_ids:
|
||||
seth.report(state_id)
|
||||
world = seth.get_world(state_id)
|
||||
|
||||
print "[+] Global coverage:"
|
||||
print seth.coverage(contract_account, ty='SUICIDE')
|
||||
|
||||
@@ -35,11 +35,11 @@ seth.transaction( caller=user_account,
|
||||
|
||||
print "[+] There are %d reverted states now"% len(seth.final_state_ids)
|
||||
for state_id in seth.final_state_ids:
|
||||
seth.report(state_id)
|
||||
print seth.report(state_id)
|
||||
|
||||
print "[+] There are %d alive states now"% len(seth.running_state_ids)
|
||||
for state_id in seth.running_state_ids:
|
||||
seth.report(state_id)
|
||||
print seth.report(state_id)
|
||||
|
||||
print "[+] Global coverage:"
|
||||
print seth.coverage(contract_account)
|
||||
|
||||
@@ -45,11 +45,11 @@ seth.transaction( caller=user_account,
|
||||
|
||||
print "[+] There are %d reverted states now"% len(seth.final_state_ids)
|
||||
for state_id in seth.final_state_ids:
|
||||
seth.report(state_id)
|
||||
print seth.report(state_id)
|
||||
|
||||
print "[+] There are %d alive states now"% len(seth.running_state_ids)
|
||||
for state_id in seth.running_state_ids:
|
||||
seth.report(state_id)
|
||||
print seth.report(state_id)
|
||||
|
||||
print "[+] Global coverage:"
|
||||
print seth.coverage(contract_account)
|
||||
|
||||
@@ -31,11 +31,11 @@ contract_account.target(value=seth.SValue)
|
||||
|
||||
print "[+] There are %d reverted states now"% len(seth.final_state_ids)
|
||||
for state_id in seth.final_state_ids:
|
||||
seth.report(state_id)
|
||||
print seth.report(state_id)
|
||||
|
||||
print "[+] There are %d alive states now"% len(seth.running_state_ids)
|
||||
for state_id in seth.running_state_ids:
|
||||
seth.report(state_id)
|
||||
print seth.report(state_id)
|
||||
|
||||
print "[+] Global coverage:"
|
||||
print seth.coverage(contract_account)
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
from manticore.seth import ManticoreEVM, calculate_coverage, ABI
|
||||
import sys
|
||||
################ Script #######################
|
||||
seth = ManticoreEVM(procs=8)
|
||||
seth.verbosity(0)
|
||||
#And now make the contract account to analyze
|
||||
# cat | solc --bin
|
||||
source_code = file(sys.argv[1],'rb').read()
|
||||
|
||||
user_account = seth.create_account(balance=1000)
|
||||
print "[+] Creating a user account", user_account
|
||||
|
||||
|
||||
contract_account = seth.solidity_create_contract(source_code, owner=user_account)
|
||||
print "[+] Creating a contract account", contract_account
|
||||
|
||||
attacker_account = seth.create_account(balance=1000)
|
||||
print "[+] Creating a attacker account", attacker_account
|
||||
|
||||
|
||||
last_coverage = None
|
||||
new_coverage = 0
|
||||
tx_count = 0
|
||||
while new_coverage != last_coverage and new_coverage < 100:
|
||||
|
||||
symbolic_data = seth.make_symbolic_buffer(320)
|
||||
symbolic_value = seth.make_symbolic_value()
|
||||
|
||||
seth.transaction(caller=attacker_account,
|
||||
address=contract_account,
|
||||
data=symbolic_data,
|
||||
value=symbolic_value )
|
||||
|
||||
tx_count += 1
|
||||
last_coverage = new_coverage
|
||||
new_coverage = seth.global_coverage(contract_account)
|
||||
|
||||
print "[+] Coverage after %d transactions: %d%%"%(tx_count, new_coverage)
|
||||
print "[+] There are %d reverted states now"% len(seth.terminated_state_ids)
|
||||
print "[+] There are %d alive states now"% len(seth.running_state_ids)
|
||||
|
||||
for state in seth.all_states:
|
||||
blockchain = state.platform
|
||||
for tx in blockchain.transactions: #external transactions
|
||||
print "Transaction:"
|
||||
print "\tsort %s" % tx.sort #Last instruction or type? TBD
|
||||
print "\tcaller 0x%x" % state.solve_one(tx.caller) #The caller as by the yellow paper
|
||||
print "\taddress 0x%x" % state.solve_one(tx.address) #The address as by the yellow paper
|
||||
print "\tvalue: %d" % state.solve_one(tx.value) #The value as by the yellow paper
|
||||
print "\tcalldata: %r" % state.solve_one(tx.data)
|
||||
print "\tresult: %s" % tx.result #The result if any RETURN or REVERT
|
||||
print "\treturn_data: %r" % state.solve_one(tx.return_data) #The returned data if RETURN or REVERT
|
||||
|
||||
if tx.sort == 'Call':
|
||||
metadata = seth.get_metadata(tx.address)
|
||||
if metadata is not None:
|
||||
function_id = tx.data[:4] #hope there is enough data
|
||||
function_id = state.solve_one(function_id).encode('hex')
|
||||
signature = metadata.get_func_signature(function_id)
|
||||
print "\tparsed calldata", ABI.parse(signature, tx.data) #func_id, *function arguments
|
||||
if tx.result == 'RETURN':
|
||||
ret_types = metadata.get_func_return_types(function_id)
|
||||
print '\tparsed return_data', ABI.parse(ret_types, tx.return_data) #function return
|
||||
|
||||
#the logs
|
||||
for log_item in blockchain.logs:
|
||||
print "log address", log_item.address
|
||||
print "memlog", log_item.memlog
|
||||
for topic in log_item.topics:
|
||||
print "topic", topic
|
||||
|
||||
for address in blockchain.deleted_addresses:
|
||||
print "deleted address", address #selfdestructed address
|
||||
|
||||
#accounts alive in this state
|
||||
for address in blockchain.contract_accounts:
|
||||
code = blockchain.get_code(address)
|
||||
balance = blockchain.get_balance(address)
|
||||
trace = set(( offset for address_i, offset in state.context['seth.trace'] if address == address_i))
|
||||
print calculate_coverage(code, trace) #coverage % for address in this state
|
||||
|
||||
# All accounts ever created by the script
|
||||
# (may not all be alife in all states)
|
||||
# (accounts created by contract code are not in this list )
|
||||
print "[+] Global coverage:"
|
||||
for address in seth.contract_accounts:
|
||||
print address, seth.global_coverage(address) #coverage % for address in this state
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+21
-18
@@ -427,23 +427,26 @@ class Executor(Eventful):
|
||||
|
||||
|
||||
while not self.is_shutdown():
|
||||
try:
|
||||
#select a suitable state to analyze
|
||||
if current_state is None:
|
||||
with self._lock:
|
||||
#notify siblings we are about to stop this run
|
||||
self._notify_stop_run()
|
||||
#Select a single state_id
|
||||
current_state_id = self.get()
|
||||
#load selected state from secondary storage
|
||||
if current_state_id is not None:
|
||||
self._publish('will_load_state', current_state_id)
|
||||
current_state = self._workspace.load_state(current_state_id)
|
||||
self.forward_events_from(current_state, True)
|
||||
self._publish('did_load_state', current_state, current_state_id)
|
||||
logger.info("load state %r", current_state_id)
|
||||
#notify siblings we have a state to play with
|
||||
self._notify_start_run()
|
||||
try: # handle fatal errors: exceptions in Manticore
|
||||
try: # handle external (e.g. solver) errors, and executor control exceptions
|
||||
#select a suitable state to analyze
|
||||
if current_state is None:
|
||||
with self._lock:
|
||||
#notify siblings we are about to stop this run
|
||||
self._notify_stop_run()
|
||||
try:
|
||||
#Select a single state_id
|
||||
current_state_id = self.get()
|
||||
#load selected state from secondary storage
|
||||
if current_state_id is not None:
|
||||
self._publish('will_load_state', current_state_id)
|
||||
current_state = self._workspace.load_state(current_state_id)
|
||||
self.forward_events_from(current_state, True)
|
||||
self._publish('did_load_state', current_state, current_state_id)
|
||||
logger.info("load state %r", current_state_id)
|
||||
#notify siblings we have a state to play with
|
||||
finally:
|
||||
self._notify_start_run()
|
||||
|
||||
#If current_state is still None. We are done.
|
||||
if current_state is None:
|
||||
@@ -453,7 +456,7 @@ class Executor(Eventful):
|
||||
assert current_state is not None
|
||||
assert current_state.constraints is current_state.platform.constraints
|
||||
|
||||
try:
|
||||
|
||||
|
||||
# Allows to terminate manticore worker on user request
|
||||
while not self.is_shutdown():
|
||||
|
||||
@@ -10,6 +10,8 @@ class Expression(object):
|
||||
super(Expression, self).__init__()
|
||||
self._taint = frozenset(taint)
|
||||
|
||||
def __repr__(self):
|
||||
return "<%s at %x>" % (type(self).__name__, id(self))
|
||||
@property
|
||||
def is_tainted(self):
|
||||
return len(self._taint) != 0
|
||||
@@ -536,7 +538,7 @@ class Array(Expression):
|
||||
def __init__(self, index_bits, index_max, *operands, **kwargs):
|
||||
assert index_bits in (32, 64, 256)
|
||||
assert index_max is None or isinstance(index_max, (int, long))
|
||||
assert index_max is None or index_max > 0 and index_max < 2 ** index_bits
|
||||
assert index_max is None or index_max >= 0 and index_max < 2 ** index_bits
|
||||
self._index_bits = index_bits
|
||||
self._index_max = index_max
|
||||
super(Array, self).__init__(*operands, **kwargs)
|
||||
@@ -619,10 +621,22 @@ class ArrayProxy(Array):
|
||||
self._array = array
|
||||
self.name = array.name
|
||||
|
||||
|
||||
def __len__(self):
|
||||
return len(self._array)
|
||||
|
||||
@property
|
||||
def operands(self):
|
||||
return self._array.operands
|
||||
|
||||
@property
|
||||
def index_bits(self):
|
||||
return self._array.index_bits
|
||||
|
||||
@property
|
||||
def index_max(self):
|
||||
return self._array.index_max
|
||||
|
||||
@property
|
||||
def taint(self):
|
||||
return self._array.taint
|
||||
@@ -635,8 +649,17 @@ class ArrayProxy(Array):
|
||||
self._array = auxiliar
|
||||
return auxiliar
|
||||
|
||||
def _fix_index(self, index):
|
||||
stop, start = index.stop, index.start
|
||||
if start is None:
|
||||
start = 0
|
||||
if stop is None:
|
||||
stop = len(self)
|
||||
return start, stop
|
||||
|
||||
def _get_size(self, index):
|
||||
size = index.stop - index.start
|
||||
start, stop = self._fix_index(index)
|
||||
size = stop - start
|
||||
if isinstance(size, BitVec):
|
||||
import visitors
|
||||
from manticore.core.smtlib.visitors import arithmetic_simplifier
|
||||
@@ -648,23 +671,30 @@ class ArrayProxy(Array):
|
||||
|
||||
def __getitem__(self, index):
|
||||
if isinstance(index, slice):
|
||||
start, stop = self._fix_index(index)
|
||||
size = self._get_size(index)
|
||||
result = []
|
||||
new_array = ArrayVariable(self.index_bits, size, name='%s_b%d_e%d'%(self.name, start, stop), taint=self.taint)
|
||||
new_array = ArrayProxy(new_array)
|
||||
for i in xrange(size):
|
||||
if self.index_max is not None and not isinstance(i+index.start, Expression) and i+index.start >= self.index_max:
|
||||
result.append(0)
|
||||
if self.index_max is not None and not isinstance(i+start, Expression) and i+start >= self.index_max:
|
||||
new_array[i] = 0
|
||||
else:
|
||||
result.append(self._array.select(index.start+i))
|
||||
return result
|
||||
new_array[i] = self._array.select(start+i)
|
||||
return new_array
|
||||
else:
|
||||
if self.index_max is not None :
|
||||
if not isinstance(index, Expression) and index >= self.index_max:
|
||||
raise IndexError
|
||||
return self._array.select(index)
|
||||
|
||||
def __setitem__(self, index, value):
|
||||
|
||||
if isinstance(index, slice):
|
||||
start, stop = self._fix_index(index)
|
||||
size = self._get_size(index)
|
||||
assert len(value) == size
|
||||
for i in xrange(size):
|
||||
self.store(index.start+i, value[i])
|
||||
self.store(start+i, value[i])
|
||||
else:
|
||||
self.store(index, value)
|
||||
|
||||
|
||||
@@ -447,9 +447,16 @@ class Z3Solver(Solver):
|
||||
The current set of assertions must be sat.
|
||||
:param val: an expression or symbol '''
|
||||
if not issymbolic(expression):
|
||||
if expression is None:
|
||||
return
|
||||
if isinstance(expression, str):
|
||||
expression = ord(expression)
|
||||
if isinstance(expression, list):
|
||||
if len(expression) == 1:
|
||||
expression = ord(expression)
|
||||
else:
|
||||
expression = map(ord, expression)
|
||||
if isinstance(expression, (list, tuple)):
|
||||
if len(expression) == 0:
|
||||
return expression
|
||||
arr = constraints.new_array(index_max=len(expression))
|
||||
for i in range(len(expression)):
|
||||
arr[i] = expression[i]
|
||||
|
||||
@@ -394,7 +394,7 @@ class Manticore(Eventful):
|
||||
|
||||
@property
|
||||
def running(self):
|
||||
return self._executor._running.value
|
||||
return self._executor.running
|
||||
|
||||
def enqueue(self, state):
|
||||
''' Dynamically enqueue states. Users should typically not need to do this '''
|
||||
|
||||
+128
-29
@@ -27,6 +27,26 @@ def ceil32(x):
|
||||
def to_signed(i):
|
||||
return Operators.ITEBV(256, i<TT255, i, i-TT256)
|
||||
|
||||
class Transaction(object):
|
||||
__slots__= 'sort', 'address', 'origin', 'price', 'data', 'caller', 'value', 'return_data', 'result'
|
||||
def __init__(self, sort, address, origin, price, data, caller, value, return_data, result):
|
||||
self.sort = sort
|
||||
self.address = address
|
||||
self.origin = origin
|
||||
self.price = price
|
||||
self.data = data
|
||||
self.caller = caller
|
||||
self.value = value
|
||||
self.return_data = return_data
|
||||
self.result = result
|
||||
|
||||
def __reduce__(self):
|
||||
''' Implements serialization/pickle '''
|
||||
return (self.__class__, (self.sort, self.address, self.origin, self.price, self.data, self.caller, self.value, self.return_data, self.result))
|
||||
|
||||
|
||||
|
||||
|
||||
class EVMMemory(object):
|
||||
def __init__(self, constraints, address_size=256, value_size=8, *args, **kwargs):
|
||||
'''
|
||||
@@ -1041,7 +1061,7 @@ class EVM(Eventful):
|
||||
self.value = value
|
||||
self.depth = depth
|
||||
self.bytecode = code
|
||||
self.suicide = set()
|
||||
self.suicides = set()
|
||||
self.logs = []
|
||||
self.gas=gas
|
||||
#FIXME parse decode and mark invalid instructions
|
||||
@@ -1087,7 +1107,7 @@ class EVM(Eventful):
|
||||
state['stack'] = self.stack
|
||||
state['gas'] = self.gas
|
||||
state['allocated'] = self.allocated
|
||||
state['suicide'] = self.suicide
|
||||
state['suicides'] = self.suicides
|
||||
state['logs'] = self.logs
|
||||
|
||||
return state
|
||||
@@ -1112,7 +1132,7 @@ class EVM(Eventful):
|
||||
self.stack = state['stack']
|
||||
self.gas = state['gas']
|
||||
self.allocated = state['allocated']
|
||||
self.suicide = state['suicide']
|
||||
self.suicides = state['suicides']
|
||||
super(EVM, self).__setstate__(state)
|
||||
|
||||
#Memory related
|
||||
@@ -1822,6 +1842,7 @@ class EVMWorld(Platform):
|
||||
self._logs = list()
|
||||
self._sha3 = {}
|
||||
self._pending_transaction = None
|
||||
self._transactions = list()
|
||||
|
||||
def __getstate__(self):
|
||||
state = super(EVMWorld, self).__getstate__()
|
||||
@@ -1832,6 +1853,8 @@ class EVMWorld(Platform):
|
||||
state['constraints'] = self._constraints
|
||||
state['callstack'] = self._callstack
|
||||
state['deleted_address'] = self._deleted_address
|
||||
state['transactions'] = self._transactions
|
||||
|
||||
return state
|
||||
|
||||
def __setstate__(self, state):
|
||||
@@ -1843,6 +1866,7 @@ class EVMWorld(Platform):
|
||||
self._constraints = state['constraints']
|
||||
self._callstack = state['callstack']
|
||||
self._deleted_address = state['deleted_address']
|
||||
self._transactions = state['transactions']
|
||||
self._do_events()
|
||||
|
||||
def _do_events(self):
|
||||
@@ -1871,6 +1895,14 @@ class EVMWorld(Platform):
|
||||
def constraints(self):
|
||||
return self._constraints
|
||||
|
||||
@property
|
||||
def transactions(self):
|
||||
return self._transactions
|
||||
|
||||
@property
|
||||
def last_return_data(self):
|
||||
return self.transactions[-1].return_data
|
||||
|
||||
@constraints.setter
|
||||
def constraints(self, constraints):
|
||||
self._constraints = constraints
|
||||
@@ -1889,11 +1921,28 @@ class EVMWorld(Platform):
|
||||
return None
|
||||
|
||||
@property
|
||||
def suicide(self):
|
||||
if self.depth:
|
||||
return self.current.suicide
|
||||
else:
|
||||
return self._suicide
|
||||
def accounts(self):
|
||||
return self.storage.keys()
|
||||
|
||||
@property
|
||||
def normal_accounts(self):
|
||||
accs = []
|
||||
for address in self.accounts:
|
||||
if len(self.get_code(address)) == 0:
|
||||
accs.append(address)
|
||||
return accs
|
||||
|
||||
@property
|
||||
def contract_accounts(self):
|
||||
accs = []
|
||||
for address in self.accounts:
|
||||
if len(self.get_code(address)) > 0:
|
||||
accs.append(address)
|
||||
return accs
|
||||
|
||||
@property
|
||||
def deleted_addresses(self):
|
||||
return self._deleted_address
|
||||
|
||||
@property
|
||||
def storage(self):
|
||||
@@ -1948,6 +1997,41 @@ class EVMWorld(Platform):
|
||||
self.storage[address]['code'] = data
|
||||
|
||||
|
||||
def log(self, address, topic, data):
|
||||
self.logs.append((address, data, topics))
|
||||
logger.info('LOG %r %r', memlog, topics)
|
||||
|
||||
def log_storage(self, addr):
|
||||
pass
|
||||
|
||||
def add_refund(self, value):
|
||||
pass
|
||||
|
||||
def block_prevhash(self):
|
||||
return 0
|
||||
|
||||
def block_coinbase(self):
|
||||
return 0
|
||||
|
||||
def block_timestamp(self):
|
||||
return 0
|
||||
|
||||
def block_number(self):
|
||||
return 0
|
||||
|
||||
def block_difficulty(self):
|
||||
return 0
|
||||
|
||||
def block_gas_limit(self):
|
||||
return 0
|
||||
|
||||
def tx_origin(self):
|
||||
return self.current_vm.origin
|
||||
|
||||
def tx_gasprice(self):
|
||||
return 0
|
||||
|
||||
#CALLSTACK
|
||||
def _push_vm(self, vm):
|
||||
#Storage address -> account(value, local_storage)
|
||||
vm.global_storage = self.storage
|
||||
@@ -1970,15 +2054,14 @@ class EVMWorld(Platform):
|
||||
assert self.constraints == vm.constraints
|
||||
if self.current:
|
||||
self.current.constraints = vm.constraints
|
||||
|
||||
if not rollback:
|
||||
if self.depth:
|
||||
self.current.global_storage = vm.global_storage
|
||||
else:
|
||||
self._global_storage = vm.global_storage
|
||||
|
||||
self._deleted_address = self._deleted_address.union(vm.suicide)
|
||||
self._logs += vm.logs
|
||||
if not self.depth:
|
||||
self._deleted_address = self._deleted_address.union(vm.suicides)
|
||||
self._logs += vm.logs
|
||||
for address in self._deleted_address:
|
||||
del self.storage[address]
|
||||
return vm
|
||||
@@ -2027,11 +2110,6 @@ class EVMWorld(Platform):
|
||||
except TerminateState as e:
|
||||
if self.depth == 0 and e.message == 'RETURN':
|
||||
return self.last_return
|
||||
'''import traceback
|
||||
print "Exception in user code:"
|
||||
print '-'*60
|
||||
traceback.print_exc(file=sys.stdout)
|
||||
print '-'*60'''
|
||||
raise e
|
||||
|
||||
def create_account(self, address=None, balance=0, code='', storage=None):
|
||||
@@ -2108,11 +2186,15 @@ class EVMWorld(Platform):
|
||||
if origin is None and caller is not None:
|
||||
origin = caller
|
||||
|
||||
if address not in self.accounts or\
|
||||
caller not in self.accounts or \
|
||||
origin != caller and origin not in self.accounts:
|
||||
raise TerminateState('Account does not exist %x'%address, testcase=True)
|
||||
|
||||
if header is None:
|
||||
header = {'timestamp':1}
|
||||
if any([ isinstance(data[i], Expression) for i in range(len(data))]):
|
||||
data_symb = self._constraints.new_array(index_bits=256, index_max=len(data))
|
||||
#data_symb = EVMMemory(self.constraints, 256, 8)
|
||||
for i in range(len(data)):
|
||||
data_symb[i] = Operators.ORD(data[i])
|
||||
data = data_symb
|
||||
@@ -2143,12 +2225,17 @@ class EVMWorld(Platform):
|
||||
new_vm = EVM(self._constraints, address, origin, price, data, caller, value, bytecode, header, global_storage=self.storage)
|
||||
self._push_vm(new_vm)
|
||||
if self.depth == 1:
|
||||
|
||||
#handle human transactions
|
||||
if ty == 'Create':
|
||||
self.current.last_exception = Create(None, None, None)
|
||||
data = bytecode
|
||||
elif ty == 'Call':
|
||||
self.current.last_exception = Call(None, None, None, None)
|
||||
|
||||
tx = Transaction(ty, address, origin, price, data, caller, value, None, None)
|
||||
self._transactions.append(tx)
|
||||
|
||||
|
||||
def CALL(self, gas, to, value, data):
|
||||
address = to
|
||||
@@ -2165,8 +2252,10 @@ class EVMWorld(Platform):
|
||||
def RETURN(self, data):
|
||||
prev_vm = self._pop_vm() #current VM changed!
|
||||
if self.depth == 0:
|
||||
self.last_return=data
|
||||
self.last_pc = prev_vm.pc
|
||||
tx = self._transactions[-1]
|
||||
tx.return_data=data
|
||||
#tx.last_pc = prev_vm.pc
|
||||
tx.result = 'RETURN'
|
||||
raise TerminateState("RETURN", testcase=True)
|
||||
|
||||
|
||||
@@ -2187,7 +2276,10 @@ class EVMWorld(Platform):
|
||||
def STOP(self):
|
||||
prev_vm = self._pop_vm(rollback=False)
|
||||
if self.depth == 0:
|
||||
self.last_pc = prev_vm.pc
|
||||
tx = self._transactions[-1]
|
||||
tx.return_data=None
|
||||
#tx.last_pc = prev_vm.pc
|
||||
tx.result = 'STOP'
|
||||
raise TerminateState("STOP", testcase=True)
|
||||
self.current.last_exception = None
|
||||
self.current._push(1)
|
||||
@@ -2202,7 +2294,10 @@ class EVMWorld(Platform):
|
||||
self.storage[prev_vm.address]['balance'] -= prev_vm.value
|
||||
|
||||
if self.depth == 0:
|
||||
self.last_pc = prev_vm.pc
|
||||
tx = self._transactions[-1]
|
||||
#tx.last_pc = prev_vm.pc
|
||||
tx.return_data=None
|
||||
tx.result = 'THROW'
|
||||
raise TerminateState("THROW", testcase=True)
|
||||
|
||||
self.current.last_exception = None
|
||||
@@ -2217,8 +2312,10 @@ class EVMWorld(Platform):
|
||||
self.storage[prev_vm.address]['balance'] -= prev_vm.value
|
||||
|
||||
if self.depth == 0:
|
||||
self.last_return=data
|
||||
self.last_pc = prev_vm.pc
|
||||
tx = self._transactions[-1]
|
||||
tx.return_data=data
|
||||
#tx.last_pc = prev_vm.pc
|
||||
tx.result = 'REVERT'
|
||||
raise TerminateState("REVERT", testcase=True)
|
||||
|
||||
self.current.last_exception = None
|
||||
@@ -2233,12 +2330,14 @@ class EVMWorld(Platform):
|
||||
self.create_account(address=recipient, balance=0, code='', storage=None)
|
||||
self.storage[recipient]['balance'] += self.storage[address]['balance']
|
||||
self.storage[address]['balance'] = 0
|
||||
self.suicide.add(address)
|
||||
self.current.suicides.add(address)
|
||||
prev_vm = self._pop_vm(rollback=False)
|
||||
if self.depth == 0:
|
||||
self.last_pc = prev_vm.pc
|
||||
raise TerminateState("SELFDESTRUCT", testcase=True)
|
||||
|
||||
if self.depth == 0:
|
||||
tx = self._transactions[-1]
|
||||
tx.result = 'SELFDESTRUCT'
|
||||
raise TerminateState("SELFDESTRUCT", testcase=True)
|
||||
|
||||
def HASH(self, data):
|
||||
|
||||
def compare_buffers(a, b):
|
||||
@@ -2255,7 +2354,7 @@ class EVMWorld(Platform):
|
||||
logger.info("SHA3 Searching over %d known hashes", len(self._sha3))
|
||||
logger.info("SHA3 TODO save this state for future explorations with more known hashes")
|
||||
#Broadcast the signal
|
||||
self._publish( 'on_symbolic_sha3', data, self._sha3.items())
|
||||
self._publish('on_symbolic_sha3', data, self._sha3.items())
|
||||
|
||||
results = []
|
||||
known_hashes = False
|
||||
|
||||
+292
-64
@@ -9,6 +9,54 @@ import sha3
|
||||
import json
|
||||
import StringIO
|
||||
|
||||
def calculate_coverage(code, seen):
|
||||
''' '''
|
||||
runtime_bytecode = code
|
||||
end = None
|
||||
if ''.join(runtime_bytecode[-44: -34]) =='\x00\xa1\x65\x62\x7a\x7a\x72\x30\x58\x20' \
|
||||
and ''.join(runtime_bytecode[-2:]) =='\x00\x29':
|
||||
end = -9-33-2 #Size of metadata at the end of most contracts
|
||||
|
||||
count, total = 0, 0
|
||||
for i in evm.EVMAsm.disassemble_all(runtime_bytecode[:end]) :
|
||||
if i.offset in seen:
|
||||
count += 1
|
||||
total += 1
|
||||
|
||||
return count*100.0/total
|
||||
|
||||
class SolidityMetadata(object):
|
||||
def __init__(self, name, source_code, init_bytecode, metadata, metadata_runtime, hashes, abi):
|
||||
self.name = name
|
||||
self.source_code = source_code
|
||||
self.init_bytecode = init_bytecode
|
||||
self.metadata = metadata
|
||||
self.metadata_runtime = metadata_runtime
|
||||
self.hashes = hashes
|
||||
self.abi = dict( [(item.get('name', '{fallback}'), item) for item in abi ])
|
||||
|
||||
@property
|
||||
def signatures(self):
|
||||
return dict(( (b,a) for (a,b) in self.hashes.items() ))
|
||||
|
||||
def get_abi(self, hsh):
|
||||
func_name = self.get_func_name(hsh)
|
||||
return self.abi[func_name]
|
||||
|
||||
def get_func_argument_types(self, hsh):
|
||||
abi = self.get_abi(hsh)
|
||||
return '('+','.join(x['type'] for x in abi['inputs']) +')'
|
||||
|
||||
def get_func_return_types(self, hsh):
|
||||
abi = self.get_abi(hsh)
|
||||
return '('+','.join(x['type'] for x in abi['outputs']) +')'
|
||||
|
||||
def get_func_name(self, hsh):
|
||||
signature = self.signatures.get(hsh,'{fallback}()')
|
||||
return signature.split('(')[0]
|
||||
|
||||
def get_func_signature(self, hsh):
|
||||
return self.signatures.get(hsh, '{fallback}()')
|
||||
|
||||
class ABI(object):
|
||||
'''
|
||||
@@ -114,6 +162,66 @@ class ABI(object):
|
||||
return reduce(lambda x,y: x+y, result)
|
||||
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _consume_type(ty, data, offset):
|
||||
def get_uint(size, offset):
|
||||
byte_size = size/8
|
||||
padding = 32 - byte_size # for 160
|
||||
return Operators.CONCAT(size, *map(Operators.ORD, data[offset+padding:offset+padding+byte_size]))
|
||||
if ty == u'uint256':
|
||||
return get_uint(256, offset), offset+32
|
||||
if ty == u'bool':
|
||||
return get_uint(8, offset), offset+32
|
||||
elif ty == u'address':
|
||||
return get_uint(160, offset), offset+32
|
||||
elif ty == u'int256':
|
||||
value = get_uint(256, offset)
|
||||
mask = 2**(256 - 1)
|
||||
value = -(value & mask) + (value & ~mask)
|
||||
return value, offset+32
|
||||
elif ty == u'':
|
||||
return '', offset
|
||||
elif ty in (u'bytes', u'string'):
|
||||
dyn_offset = 4 + get_uint(256,offset)
|
||||
size = get_uint(256, dyn_offset)
|
||||
return data[dyn_offset+32:dyn_offset+32+size], offset+4
|
||||
else:
|
||||
print "<",ty,">"
|
||||
raise NotImplemented(ty)
|
||||
|
||||
@staticmethod
|
||||
def parse(signature, data):
|
||||
is_multiple = '(' in signature
|
||||
|
||||
if is_multiple:
|
||||
func_name = signature.split('(')[0]
|
||||
types = signature.split('(')[1][:-1].split(',')
|
||||
if len(func_name) > 0:
|
||||
off = 4
|
||||
else:
|
||||
func_name = None
|
||||
off = 0
|
||||
else:
|
||||
func_name = None
|
||||
types = (signature,)
|
||||
off = 0
|
||||
|
||||
arguments = []
|
||||
for ty in types:
|
||||
val, off = ABI._consume_type(ty, data, off)
|
||||
arguments.append(val)
|
||||
|
||||
if is_multiple:
|
||||
if func_name is not None:
|
||||
return func_name, tuple(arguments)
|
||||
else:
|
||||
return tuple(arguments)
|
||||
else:
|
||||
return arguments[0]
|
||||
|
||||
|
||||
|
||||
class EVMAccount(object):
|
||||
''' An EVM account '''
|
||||
def __init__(self, address, seth=None, default_caller=None):
|
||||
@@ -131,14 +239,18 @@ class EVMAccount(object):
|
||||
self._hashes = {}
|
||||
|
||||
if self._seth:
|
||||
name, source_code, init_bytecode, metadata, metadata_runtime, hashes = self._seth.context['seth']['metadata'][address]
|
||||
for signature in hashes.keys():
|
||||
func_name = str(signature.split('(')[0])
|
||||
self._hashes[func_name] = signature, hashes[signature]
|
||||
md = self._seth.get_metadata(address)
|
||||
if md is not None:
|
||||
for signature, func_id in md.hashes.items():
|
||||
func_name = str(signature.split('(')[0])
|
||||
self._hashes[func_name] = signature, func_id
|
||||
|
||||
def __int__(self):
|
||||
return self._address
|
||||
|
||||
def __str__(self):
|
||||
return str(self._address)
|
||||
|
||||
def __getattribute__(self, name):
|
||||
''' If this is a contract account of which we know the functions hashes
|
||||
this will build the transaction for the function call.
|
||||
@@ -199,9 +311,42 @@ class ManticoreEVM(Manticore):
|
||||
SByte=ABI.SByte
|
||||
SValue=ABI.SValue
|
||||
|
||||
def make_symbolic_buffer(self, size):
|
||||
''' Creates a symbolic buffer of size bytes to be used in transactions.
|
||||
You can not operate on it. It is intended as a place holder for the
|
||||
real expression.
|
||||
|
||||
Example use::
|
||||
|
||||
symbolic_data = seth.make_symbolic_buffer(320)
|
||||
seth.transaction(caller=attacker_account,
|
||||
address=contract_account,
|
||||
data=symbolic_data,
|
||||
value=100000 )
|
||||
|
||||
|
||||
'''
|
||||
return ABI.SByte(size)
|
||||
|
||||
def make_symbolic_value(self):
|
||||
''' Creates a symbolic value, normally a uint256, to be used in transactions.
|
||||
You can not operate on it. It is intended as a place holder for the
|
||||
real expression.
|
||||
|
||||
Example use::
|
||||
|
||||
symbolic_value = seth.make_symbolic_value()
|
||||
seth.transaction(caller=attacker_account,
|
||||
address=contract_account,
|
||||
data=data,
|
||||
value=symbolic_data )
|
||||
|
||||
'''
|
||||
return ABI.SValue
|
||||
|
||||
@staticmethod
|
||||
def compile(source_code):
|
||||
''' Get initialization bytecode from a solidity source code '''
|
||||
name, source_code, bytecode, srcmap, srcmap_runtime, hashes = ManticoreEVM._compile(source_code)
|
||||
return bytecode
|
||||
|
||||
@@ -216,7 +361,7 @@ class ManticoreEVM(Manticore):
|
||||
with tempfile.NamedTemporaryFile() as temp:
|
||||
temp.write(source_code)
|
||||
temp.flush()
|
||||
p = Popen([solc, '--combined-json', 'srcmap,srcmap-runtime,bin,hashes', temp.name], stdout=PIPE)
|
||||
p = Popen([solc, '--combined-json', 'abi,srcmap,srcmap-runtime,bin,hashes', temp.name], stdout=PIPE)
|
||||
outp = json.loads(p.stdout.read())
|
||||
assert len(outp['contracts']), "Only one contract by file supported"
|
||||
name, outp = outp['contracts'].items()[0]
|
||||
@@ -225,9 +370,16 @@ class ManticoreEVM(Manticore):
|
||||
srcmap = outp['srcmap'].split(';')
|
||||
srcmap_runtime = outp['srcmap-runtime'].split(';')
|
||||
hashes = outp['hashes']
|
||||
return name, source_code, bytecode, srcmap, srcmap_runtime, hashes
|
||||
abi = json.loads(outp['abi'])
|
||||
return name, source_code, bytecode, srcmap, srcmap_runtime, hashes, abi
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, procs=1):
|
||||
''' A manticere EVM manager
|
||||
:param procs: number of workers to use in the exploration
|
||||
'''
|
||||
self.normal_accounts = set()
|
||||
self.contract_accounts = set()
|
||||
self._config_procs=procs
|
||||
#Make the constraint store
|
||||
constraints = ConstraintSet()
|
||||
#make the ethereum world state
|
||||
@@ -236,11 +388,9 @@ class ManticoreEVM(Manticore):
|
||||
initial_state.context['tx'] = []
|
||||
super(ManticoreEVM, self).__init__(initial_state)
|
||||
|
||||
|
||||
self.metadata = {}
|
||||
#The following should go to manticore.context so we can use multiprocessing
|
||||
self.context['seth'] = {}
|
||||
self.context['seth']['trace'] = []
|
||||
self.context['seth']['metadata'] = {}
|
||||
self.context['seth']['_pending_transaction'] = None
|
||||
self.context['seth']['_saved_states'] = []
|
||||
self.context['seth']['_final_states'] = []
|
||||
@@ -267,12 +417,41 @@ class ManticoreEVM(Manticore):
|
||||
else:
|
||||
return context['_saved_states']
|
||||
|
||||
|
||||
@property
|
||||
def final_state_ids(self):
|
||||
def all_state_ids(self):
|
||||
''' IDs of the all states '''
|
||||
return self.running_state_ids + self.terminated_state_ids
|
||||
|
||||
@property
|
||||
def terminated_state_ids(self):
|
||||
''' IDs of the terminated states '''
|
||||
with self.locked_context('seth') as context:
|
||||
return context['_final_states']
|
||||
|
||||
@property
|
||||
def running_states(self):
|
||||
''' Iterates over the running states'''
|
||||
for state_id in self.running_state_ids:
|
||||
state = self.load(state_id)
|
||||
yield state
|
||||
|
||||
@property
|
||||
def terminated_states(self):
|
||||
''' Iterates over the terminated states'''
|
||||
for state_id in self.terminated_state_ids:
|
||||
state = self.load(state_id)
|
||||
yield state
|
||||
|
||||
@property
|
||||
def all_states(self):
|
||||
''' Iterates over the all states (terminated and alive)'''
|
||||
for state_id in self.terminated_state_ids + self.running_state_ids:
|
||||
state = self.load(state_id)
|
||||
yield state
|
||||
|
||||
|
||||
#deprecate this 5 in favor of for sta in seth.all_states: do stuff?
|
||||
def get_world(self, state_id=None):
|
||||
''' Returns the evm world of `state_id` state. '''
|
||||
state = self.load(state_id)
|
||||
@@ -294,9 +473,14 @@ class ManticoreEVM(Manticore):
|
||||
return self.get_world(state_id).storage[address]['storage'].get(offset)
|
||||
|
||||
def last_return(self, state_id=None):
|
||||
''' last returned buffer for state `state_id` '''
|
||||
''' Last returned buffer for state `state_id` '''
|
||||
state = self.load(state_id)
|
||||
return state.world.last_return
|
||||
return state.platform.last_return
|
||||
|
||||
def transactions(self, state_id=None):
|
||||
''' Transactions list for state `state_id` '''
|
||||
state = self.load(state_id)
|
||||
return state.platform.transactions
|
||||
|
||||
def solidity_create_contract(self, source_code, owner, balance=0, address=None, args=()):
|
||||
''' Creates a solidity contract
|
||||
@@ -314,9 +498,9 @@ class ManticoreEVM(Manticore):
|
||||
:return: an EVMAccount
|
||||
'''
|
||||
|
||||
name, source_code, init_bytecode, metadata, metadata_runtime, hashes = self._compile(source_code)
|
||||
name, source_code, init_bytecode, metadata, metadata_runtime, hashes, abi = self._compile(source_code)
|
||||
address = self.create_contract(owner=owner, address=address, balance=balance, init=tuple(init_bytecode)+tuple(ABI.make_function_arguments(*args)))
|
||||
self.context['seth']['metadata'][address] = name, source_code, init_bytecode, metadata, metadata_runtime, hashes
|
||||
self.metadata[address] = SolidityMetadata(name, source_code, init_bytecode, metadata, metadata_runtime, hashes, abi)
|
||||
return EVMAccount(address, self, default_caller=owner)
|
||||
|
||||
def create_contract(self, owner, balance=0, init=None, address=None):
|
||||
@@ -329,9 +513,10 @@ class ManticoreEVM(Manticore):
|
||||
:param balance: balance to be transfered on creation
|
||||
:type balance: int or SValue
|
||||
:param address: the address for the new contract (optional)
|
||||
:type address: int or EVMAccount
|
||||
:return: an EVMAccount address (long)
|
||||
:type address: int
|
||||
:return: an EVMAccount
|
||||
'''
|
||||
assert len(self.running_state_ids) == 1, "No forking yet"
|
||||
with self.locked_context('seth') as context:
|
||||
assert context['_pending_transaction'] is None
|
||||
assert init is not None
|
||||
@@ -339,8 +524,9 @@ class ManticoreEVM(Manticore):
|
||||
address = self.world._new_address()
|
||||
self.context['seth']['_pending_transaction'] = ('CREATE_CONTRACT', owner, address, balance, init)
|
||||
|
||||
self.run()
|
||||
self.run(procs=self._config_procs)
|
||||
|
||||
self.contract_accounts.add(address)
|
||||
return address
|
||||
|
||||
def create_account(self, balance=0, address=None, code=''):
|
||||
@@ -349,12 +535,15 @@ class ManticoreEVM(Manticore):
|
||||
:param balance: balance to be transfered on creation
|
||||
:type balance: int or SValue
|
||||
:param address: the address for the new contract (optional)
|
||||
:type address: int or EVMAccount
|
||||
:return: an EVMAccount address (long)
|
||||
:type address: int
|
||||
:return: an EVMAccount
|
||||
'''
|
||||
assert len(self.running_state_ids) == 1, "No forking yet"
|
||||
with self.locked_context('seth') as context:
|
||||
assert context['_pending_transaction'] is None
|
||||
return self.world.create_account( address, balance, code=code, storage=None)
|
||||
address = self.world.create_account( address, balance, code=code, storage=None)
|
||||
self.normal_accounts.add(address)
|
||||
return address
|
||||
|
||||
def transaction(self, caller, address, value, data):
|
||||
''' Issue a transaction
|
||||
@@ -371,13 +560,12 @@ class ManticoreEVM(Manticore):
|
||||
address = int(address)
|
||||
if isinstance(caller, EVMAccount):
|
||||
caller = int(caller)
|
||||
|
||||
|
||||
|
||||
if isinstance(data, self.SByte):
|
||||
data = (None,)*data.size
|
||||
with self.locked_context('seth') as context:
|
||||
context['_pending_transaction'] = ('CALL', caller, address, value, data)
|
||||
return self.run(procs=10)
|
||||
return self.run(procs=self._config_procs)
|
||||
|
||||
def run(self, **kwargs):
|
||||
''' Run any pending transaction on any running state '''
|
||||
@@ -387,13 +575,11 @@ class ManticoreEVM(Manticore):
|
||||
assert context['_pending_transaction'] is not None
|
||||
#there is at least one states in seth saved states
|
||||
assert context['_saved_states'] or self.initial_state
|
||||
|
||||
#there is no states added to the executor queue
|
||||
assert len(self._executor.list()) == 0
|
||||
|
||||
for state_id in context['_saved_states']:
|
||||
self._executor.put(state_id)
|
||||
|
||||
context['_saved_states'] = []
|
||||
|
||||
#A callback will use _pending_transaction and issue the transaction
|
||||
@@ -404,6 +590,7 @@ class ManticoreEVM(Manticore):
|
||||
if len(context['_saved_states'])==1:
|
||||
self._initial_state = self._executor._workspace.load_state(context['_saved_states'][0], delete=True)
|
||||
context['_saved_states'] = []
|
||||
assert self.running_state_ids == [-1]
|
||||
|
||||
#clear pending transcations. We are done.
|
||||
context['_pending_transaction'] = None
|
||||
@@ -441,12 +628,9 @@ class ManticoreEVM(Manticore):
|
||||
if len(self.running_state_ids) == 1:
|
||||
#Get the ID of the single running state
|
||||
state_id = self.running_state_ids[0]
|
||||
if state_id != -1:
|
||||
#if there is a single running state with id != 1. We consider it is a new initial_state
|
||||
assert self.initial_state is None
|
||||
state = self.initial_state = self._executor._workspace.load_state(state_id, delete=True)
|
||||
else:
|
||||
raise Exception("More than one state running. Do not know which to choose.")
|
||||
raise Exception("More than one state running. Do not know which one to choose.")
|
||||
|
||||
if state_id == -1:
|
||||
state = self.initial_state
|
||||
else:
|
||||
@@ -455,6 +639,17 @@ class ManticoreEVM(Manticore):
|
||||
return state
|
||||
|
||||
#Callbacks
|
||||
def _symbolic_sha3(self, state, data, known_hashes):
|
||||
''' INTERNAL USE '''
|
||||
|
||||
with self.locked_context('known_sha3', set) as known_sha3:
|
||||
state.platform._sha3.update(known_sha3)
|
||||
|
||||
def _concrete_sha3(self, state, buf, value):
|
||||
''' INTERNAL USE '''
|
||||
with self.locked_context('known_sha3', set) as known_sha3:
|
||||
known_sha3.add((buf,value))
|
||||
|
||||
def _terminate_state_callback(self, state, state_id, e):
|
||||
''' INTERNAL USE
|
||||
Every time a state finishes executing last transaction we save it in
|
||||
@@ -469,7 +664,7 @@ class ManticoreEVM(Manticore):
|
||||
ty, caller, address, value, data = context['_pending_transaction']
|
||||
if ty == 'CREATE_CONTRACT':
|
||||
world = state.platform
|
||||
world.storage[address]['code'] = world.last_return
|
||||
world.storage[address]['code'] = world.last_return_data
|
||||
|
||||
self.save(state)
|
||||
e.testcase = False #Do not generate a testcase file
|
||||
@@ -483,14 +678,15 @@ class ManticoreEVM(Manticore):
|
||||
''' INTERNAL USE
|
||||
When a state was just loaded from stoage we do the pending transaction
|
||||
'''
|
||||
|
||||
if state.context.get('processed', False):
|
||||
return
|
||||
world = state.platform
|
||||
state.context['processed'] = True
|
||||
with self.locked_context('seth') as context:
|
||||
#take current global transaction we need to apply to all running states
|
||||
ty, caller, address, value, data = context['_pending_transaction']
|
||||
txnum = len(state.context['tx'])
|
||||
|
||||
#Replace any none by symbolic values
|
||||
if value is None:
|
||||
value = state.new_symbolic_value(256, label='tx%d_value'%txnum)
|
||||
@@ -501,23 +697,21 @@ class ManticoreEVM(Manticore):
|
||||
if data[i] is not None:
|
||||
symbolic_data[i] = data[i]
|
||||
data = symbolic_data
|
||||
state.context['tx'].append((ty, caller, address, value, data))
|
||||
|
||||
|
||||
if ty == 'CALL':
|
||||
world.transaction(address=address, caller=caller, data=data, value=value)
|
||||
else:
|
||||
assert ty == 'CREATE_CONTRACT'
|
||||
world.create_contract(caller=caller, address=address, balance=value, init=data)
|
||||
state.context['tx'].append((ty, caller, address, value, data))
|
||||
|
||||
def _will_execute_instruction_callback(self, state, pc, instruction):
|
||||
''' INTERNAL USE '''
|
||||
assert state.constraints == state.platform.constraints
|
||||
assert state.platform.constraints == state.platform.current.constraints
|
||||
|
||||
#print state.platform.current
|
||||
with self.locked_context('coverage', set) as coverage:
|
||||
coverage.add((state.platform.current.address, state.platform.current.pc))
|
||||
|
||||
|
||||
def _did_execute_instruction_callback(self, state, prev_pc, pc, instruction):
|
||||
''' INTERNAL USE '''
|
||||
state.context.setdefault('seth.trace',[]).append((state.platform.current.address, pc))
|
||||
@@ -528,8 +722,21 @@ class ManticoreEVM(Manticore):
|
||||
for i in range(offset, offset+size):
|
||||
code_data.add((state.platform.current.address, i))
|
||||
|
||||
def report(self, state_id=None, ty=None):
|
||||
''' Prints a small report on state id '''
|
||||
|
||||
def get_metadata(self, address):
|
||||
''' Gets the solidity metadata for address.
|
||||
This is available only if address is a contract created from solidity
|
||||
'''
|
||||
try:
|
||||
md = self.metadata[address]
|
||||
except:
|
||||
md = SolidityMetadata(None, None, None, None, None, None)
|
||||
return md
|
||||
|
||||
def report(self, state, ty=None):
|
||||
''' Prints a small report on state. Prints a little something about state :) '''
|
||||
world = state.platform
|
||||
|
||||
output = StringIO.StringIO()
|
||||
|
||||
def compare_buffers(a, b):
|
||||
@@ -542,16 +749,13 @@ class ManticoreEVM(Manticore):
|
||||
return False
|
||||
return cond
|
||||
|
||||
state = self.load(state_id)
|
||||
world = state.platform
|
||||
trace = state.context['seth.trace']
|
||||
last_address, last_pc = trace[-1]
|
||||
|
||||
last_address, last_pc = trace[-1]
|
||||
#Try to recover metadata from solidity based contracts
|
||||
try:
|
||||
md_name, md_source_code, md_init_bytecode, md_metadata, md_metadata_runtime, md_hashes = self.context['seth']['metadata'][last_address]
|
||||
except:
|
||||
md_name, md_source_code, md_init_bytecode, md_metadata, md_metadata_runtime, md_hashes = None,None,None,None,None,None
|
||||
md_name, md_source_code, md_init_bytecode, md_metadata, md_metadata_runtime, md_hashes = None, None, None, None, None, None
|
||||
|
||||
# try to get the runtime bytecode from the account
|
||||
try:
|
||||
@@ -569,7 +773,7 @@ class ManticoreEVM(Manticore):
|
||||
try:
|
||||
# Magic number comes from here:
|
||||
# http://solidity.readthedocs.io/en/develop/metadata.html#encoding-of-the-metadata-hash-in-the-bytecode
|
||||
asm = list(evm.EVMDecoder.decode_all(runtime_bytecode[:-9-33-2]))
|
||||
asm = list(evm.EVMAsm.disassemble_all(runtime_bytecode[:-9-33-2]))
|
||||
asm_offset = 0
|
||||
pos = 0
|
||||
source_pos = md_metadata_runtime[pos]
|
||||
@@ -589,7 +793,7 @@ class ManticoreEVM(Manticore):
|
||||
for l in snippet.split('\n'):
|
||||
output.write(' %s %s\n'%(nl, l))
|
||||
nl+=1
|
||||
except:
|
||||
except Exception as e:
|
||||
output.write('\n')
|
||||
|
||||
output.write("BALANCES\n")
|
||||
@@ -606,9 +810,9 @@ class ManticoreEVM(Manticore):
|
||||
else:
|
||||
output.write('\t%x %d wei\n'%(address, account['balance']))
|
||||
|
||||
if state.platform.logs:
|
||||
if world.logs:
|
||||
output.write('LOGS:\n')
|
||||
for address, memlog, topics in state.platform.logs:
|
||||
for address, memlog, topics in world.logs:
|
||||
try:
|
||||
res = memlog
|
||||
if isinstance(memlog, Expression):
|
||||
@@ -628,7 +832,6 @@ class ManticoreEVM(Manticore):
|
||||
|
||||
output.write('\t %s: %r %s\n' %( hex(res1), ''.join(map(chr,res)), topics))
|
||||
except Exception,e:
|
||||
print e
|
||||
output.write('\t %r %r %r\n' % (address, repr(memlog), topics))
|
||||
|
||||
output.write('INPUT SYMBOLS\n')
|
||||
@@ -642,7 +845,7 @@ class ManticoreEVM(Manticore):
|
||||
try:
|
||||
output.write('\t %s: %s\n'%( expr.name, res.encode('hex')))
|
||||
except:
|
||||
output.write('\t %s: %s'% (expr.name, res))
|
||||
output.write('\t %s: %s\n'% (expr.name, res))
|
||||
|
||||
#print "Constraints:"
|
||||
#print state.constraints
|
||||
@@ -713,12 +916,48 @@ class ManticoreEVM(Manticore):
|
||||
output.write('%s %s\n'%(e, xdata))
|
||||
return output.getvalue()
|
||||
|
||||
def coverage(self, account_address):
|
||||
def global_coverage(self, account_address):
|
||||
''' Returns code coverage for the contract on `account_address`.
|
||||
This sums up all the visited code lines from any of the explored
|
||||
states.
|
||||
'''
|
||||
account_address = int(account_address)
|
||||
|
||||
#Search one state in which the account_address exists
|
||||
world=None
|
||||
for state_id in self.all_state_ids:
|
||||
world = self.get_world(state_id)
|
||||
if account_address in world.storage:
|
||||
break
|
||||
|
||||
seen = self.context['coverage'] #.union( self.context.get('code_data', set()))
|
||||
runtime_bytecode = world.storage[account_address]['code']
|
||||
|
||||
end = None
|
||||
if ''.join(runtime_bytecode[-44: -34]) =='\x00\xa1\x65\x62\x7a\x7a\x72\x30\x58\x20' \
|
||||
and ''.join(runtime_bytecode[-2:]) =='\x00\x29':
|
||||
end = -9-33-2 #Size of metadata at the end of most contracts
|
||||
|
||||
count, total = 0, 0
|
||||
for i in evm.EVMAsm.disassemble_all(runtime_bytecode[:end]) :
|
||||
if (account_address, i.offset) in seen:
|
||||
count += 1
|
||||
total += 1
|
||||
|
||||
return count*100.0/total
|
||||
|
||||
def report_coverage(self, account_address):
|
||||
''' Output a code coverage report for contract account_address '''
|
||||
account_address = int(account_address)
|
||||
#This will just pick one of the running states.
|
||||
#This assumes the code and the accounts are the same in all versions of the world
|
||||
world = self.get_world(self.running_state_ids[0])
|
||||
#Search one state in which the account_address exists
|
||||
world=None
|
||||
for state_id in self.all_state_ids:
|
||||
world = self.get_world(state_id)
|
||||
if account_address in world.storage:
|
||||
break
|
||||
|
||||
seen = self.context['coverage'] #.union( self.context.get('code_data', set()))
|
||||
runtime_bytecode = world.storage[account_address]['code']
|
||||
class bcolors:
|
||||
@@ -760,14 +999,3 @@ class ManticoreEVM(Manticore):
|
||||
output += "Coverage: %2.2f%%\n"% (count*100.0/total)
|
||||
return output
|
||||
|
||||
def _symbolic_sha3(self, state, data, known_hashes):
|
||||
''' INTERNAL USE '''
|
||||
|
||||
with self.locked_context('known_sha3', set) as known_sha3:
|
||||
state.platform._sha3.update(known_sha3)
|
||||
|
||||
def _concrete_sha3(self, state, buf, value):
|
||||
''' INTERNAL USE '''
|
||||
with self.locked_context('known_sha3', set) as known_sha3:
|
||||
known_sha3.add((buf,value))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user