Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Symbolic tests rf #1431

Merged
merged 80 commits into from
May 17, 2019
Merged
Changes from 1 commit
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
4ae45d9
Add assertions to auto test gen
disconnect3d Jan 17, 2019
e1e2838
Add symbolic tests
disconnect3d Jan 21, 2019
39789d5
Make calldata symbolic
disconnect3d Jan 21, 2019
20a982f
EVM: Support exp aka pow (#1361)
disconnect3d Feb 11, 2019
167c354
Split ethereum_vm tests into concrete and symbolic
disconnect3d Feb 11, 2019
267ca6c
Fix travis tests
disconnect3d Feb 11, 2019
b0da22e
Split symbolic tests into two jobs
disconnect3d Feb 11, 2019
60c0053
Split VMTests even more
disconnect3d Feb 12, 2019
4c78ea8
More tests split
disconnect3d Feb 12, 2019
75a0f25
[WIP][WIP][WIP] Moving executor functionality to ManticoreBase and re…
feliam Mar 7, 2019
6fd87c3
Workspace locks
feliam Mar 12, 2019
fa7785b
Concurrency flavor configurable from commandline
feliam Mar 14, 2019
74209fe
Asserts and refactorrrrrrrs
feliam Mar 14, 2019
3749ab7
Remove unused callback
feliam Mar 18, 2019
1e61fc7
Some CC
feliam Mar 18, 2019
d0b6301
Some CC
feliam Mar 18, 2019
1a1d9e1
Some CC
feliam Mar 18, 2019
8730d0f
Fix solver vs Z3Solver
feliam Mar 18, 2019
b19d267
Make solver a singleton based on tid/pid. REfactor m._save. Fix some …
feliam Mar 20, 2019
32fb373
typo and evm bugfix
feliam Mar 20, 2019
84cccb6
Fix some tests referecing global solver
feliam Mar 21, 2019
b8f96b2
Fix concolic tests and more global solver refs
feliam Mar 21, 2019
e8d918a
Fix tests
feliam Mar 21, 2019
ce4c0fd
CC fixes
feliam Mar 21, 2019
d6eb5c7
Fix tests. Fix testcase id generation
feliam Mar 21, 2019
a7c083e
Move profiling to a plugin and fix tests
feliam Mar 21, 2019
4bd5c3b
Add solver intance ref to mem test
feliam Mar 22, 2019
86446bf
Fix mem workspace tests
feliam Mar 25, 2019
946e69c
Fix output checking tests
feliam Mar 25, 2019
943dfbe
Fix z3solver ref
feliam Mar 25, 2019
df387b1
Relax verbosity/log tests
feliam Mar 26, 2019
0203531
Moved Workers to its own file
feliam Mar 26, 2019
b8eb691
Relax output tests
feliam Mar 26, 2019
7c908fc
Relax output tests
feliam Mar 26, 2019
5e22b41
Fix profiling test
feliam Mar 26, 2019
6482053
merge
feliam Mar 26, 2019
dc73fc2
Fix more tests
feliam Mar 27, 2019
1ec6044
Default multiprocessing
feliam Mar 27, 2019
9ebb3b4
Try to clean mcore __del__
feliam Mar 27, 2019
6889f4d
Change Worker life span
feliam Mar 27, 2019
9d4b2e1
Fix Single mode
feliam Mar 28, 2019
f9dfa4c
CC
feliam Mar 28, 2019
7f9452b
Merge branch 'master' into dev-executor-refactor
feliam Mar 28, 2019
8c6155e
revert verbosity travis
feliam Mar 29, 2019
ff3941a
CC and solver ref fix
feliam Mar 29, 2019
b304ad2
Relax ouput checking tests and some bugfixes
feliam Mar 29, 2019
449ec92
running -> ready
feliam Mar 29, 2019
c02b4ee
Fixing teeests
feliam Mar 29, 2019
7f806e8
add weak cache to _load
feliam Mar 30, 2019
ba35d64
del debug prints
feliam Mar 30, 2019
1959066
Adding config.py support for Enums
feliam Apr 8, 2019
fb1f798
Try/Remove generate_testcase event as it never occurs online. Fix tests
feliam Apr 9, 2019
3e535d2
Fix CC
feliam Apr 9, 2019
110f7e6
Kill the cache when start/stop run. Remove debugprints. clean tests
feliam Apr 12, 2019
78665f1
Fix travis test _other_
feliam Apr 16, 2019
62938fd
Fix native tests and timeout
feliam Apr 18, 2019
ea7bac0
Fix state.must_be_true
feliam Apr 18, 2019
15dd6cb
fix CC
feliam Apr 18, 2019
da71509
Merge and fix more tests
feliam Apr 18, 2019
a6c9bf2
Changing fstat tets...:
feliam Apr 22, 2019
8ff874f
LLLLLLLLinux tests
feliam Apr 22, 2019
8ef7246
Skip unicorn concrete test for now
feliam Apr 25, 2019
1b83156
Try fix CodeClimate
feliam Apr 25, 2019
8d8c1d5
Try fix CodeClimate
feliam Apr 25, 2019
ed251fb
Update evm examples to newest solidity
feliam Apr 25, 2019
4b75e0d
Complete transformation of consts.mprocessing to enum
feliam Apr 25, 2019
e6cf11d
Add blank line (codeclimate)
Apr 26, 2019
1c25bff
Using the enum instead of the string
feliam Apr 30, 2019
ee9bb2c
Merge branch 'dev-executor-refactor' of github.com:trailofbits/mantic…
feliam Apr 30, 2019
cb5d416
Using the enum instead of the string
feliam Apr 30, 2019
4be711b
Merge branch 'master' into symbolic-tests
feliam May 7, 2019
7c6b179
fix conflicts
feliam May 7, 2019
d1fa6b5
Merge and fix
feliam May 9, 2019
813e4f5
merge
feliam May 10, 2019
a2c8974
CC and debug print
feliam May 10, 2019
425f8cc
Move fee consumption to checkpoint so it is not done twice. Fix front…
feliam May 10, 2019
b9fa459
Fix Job Count (and force travis rebuild)
May 16, 2019
8333f30
Add tx number to testcase log
feliam May 16, 2019
f4cfc8a
Merge branch 'symbolic-tests-rf' of github.com:trailofbits/manticore …
feliam May 16, 2019
3fd54d1
Del test verbosity
feliam May 17, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add symbolic tests
disconnect3d committed Jan 21, 2019

Verified

This commit was signed with the committer’s verified signature. The key has expired.
tvdeyen Thomas von Deyen
commit e1e2838b0a3a5295e49b3a07bad42668ccf6e254
164 changes: 128 additions & 36 deletions tests/auto_generators/make_VMTests.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
"""
This script generates VMTests that are used to check EVM's Frontier fork correctness.

$ cd manticore/tests/ethereum && mkdir VMTests
$ git clone https://github.com/ethereum/tests --depth=1
$ for i in ./tests/VMTests; do python ../auto_generators/make_VMTests.py ./tests/VMTests/$1; done
### TO GENERATE ALL:

## Initialize env:
cd manticore/tests/ethereum && mkdir VMTests_concrete && mkdir VMTests_symbolic
git clone https://github.com/ethereum/tests --depth=1

## Generate concrete tests:
for i in ./tests/VMTests/*; do python ../auto_generators/make_VMTests.py $i; done

## Generate symbolic tests:
$ for i in ./tests/VMTests/*; do python ../auto_generators/make_VMTests.py $i --symbolic; done

## Remove the eth tests repo
$ rm -rf ./tests # cleanup/remove the ethereum/tests repo


### To test just one:
python ../auto_generators/make_VMTests.py ./tests/VMTests/VMTests
"""
import json
import os
@@ -15,7 +29,7 @@
import pyevmasm as EVMAsm


def gen_test(testcase, filename, skip):
def gen_test(testcase, filename, symbolic, skip):
output = ''' @unittest.skip('Gas or performance related')\n''' if skip else ''

# Sanity checks
@@ -24,19 +38,19 @@ def gen_test(testcase, filename, skip):
testcase.pop('_info')
assert len(testcase.pop('callcreates', [])) == 0

output += generate_pre_output(testcase, filename)
output += generate_pre_output(testcase, filename, symbolic)

if 'post' not in testcase:
output += '''
#If test end in exception check it here
# If test end in exception check it here
self.assertTrue(result == 'THROW')'''
else:
output += generate_post_output(testcase)

return output


def generate_pre_output(testcase, filename):
def generate_pre_output(testcase, filename, symbolic):
testname = (os.path.split(filename)[1].replace('-', '_')).split('.')[0]
bytecode = unhexlify(testcase['exec']['code'][2:])
disassemble = ''
@@ -58,6 +72,27 @@ def test_{testname}(self):
Code: {disassemble}
"""
'''

if symbolic:
output += '''
solver = Z3Solver()

def solve(val):
results = solver.get_all_values(constraints, val)
# We constrain all values to single values!
self.assertEqual(len(results), 1)
return results[0]
'''
else:
output += '''
def solve(val):
"""
Those tests are **auto-generated** and `solve` is used in symbolic tests.
So yes, this returns just val; it makes it easier to generate tests like this.
"""
return val
'''

env = testcase['env']

gaslimit = int(env['currentGasLimit'], 0)
@@ -67,8 +102,29 @@ def test_{testname}(self):
coinbase = int(env['currentCoinbase'], 0)
output += f'''
constraints = ConstraintSet()
world = evm.EVMWorld(constraints, blocknumber={blocknumber}, timestamp={timestamp}, difficulty={difficulty},
coinbase={coinbase}, gaslimit={gaslimit})
'''

def format_bitvec(name, val, bits=256, symbolic_name=None):
if symbolic_name is None:
symbolic_name = name

return f'''
{name} = constraints.new_bitvec({bits}, name='{symbolic_name}')
constraints.add({name} == {val})
'''

def format_var(name, val):
return f'''
{name} = {val}'''

# Spawns/creates bitvectors in symbolic or pure variables in concrete
formatter = format_bitvec if symbolic else format_var
for var in ('blocknumber', 'timestamp', 'difficulty', 'coinbase', 'gaslimit'):
output += formatter(var, locals()[var])

output += f'''
world = evm.EVMWorld(constraints, blocknumber=blocknumber, timestamp=timestamp, difficulty=difficulty,
coinbase=coinbase, gaslimit=gaslimit)
'''

for address, account in testcase['pre'].items():
@@ -79,18 +135,37 @@ def test_{testname}(self):
account_nonce = int(account['nonce'], 0)
account_balance = int(account['balance'], 0)

output += f'''
bytecode = unhexlify('{account_code}')
world.create_account(
address={hex(account_address)},
balance={account_balance},
code=bytecode,
nonce={account_nonce}
)'''
if symbolic:
postfix = hex(account_address)
output += f'''
acc_addr = {hex(account_address)}
acc_code = unhexlify('{account_code}')
'''
output += format_bitvec('acc_balance', account_balance, symbolic_name=f'balance_{postfix}')
output += format_bitvec('acc_nonce', account_nonce, symbolic_name=f'nonce_{postfix}')
else:
output += f'''
acc_addr = {hex(account_address)}
acc_code = unhexlify('{account_code}')
acc_balance = {account_balance}
acc_nonce = {account_nonce}
'''

output += f'''
world.create_account(address=acc_addr, balance=acc_balance, code=acc_code, nonce=acc_nonce)
'''
for key, value in account['storage'].items():
output += f'''
world.set_storage_data({hex(account_address)}, {key}, {value})'''
if symbolic:
postfix = hex(account_address)
output += format_bitvec('key', key, symbolic_name=f'storage_key_{postfix}')
output += format_bitvec('value', value, symbolic_name=f'storage_value_{postfix}')
output += '''
world.set_storage_data(acc_addr, key, value)
'''
else:
output += f'''
world.set_storage_data(acc['address'], {key}, {value})
'''

address = int(testcase['exec']['address'], 0)
caller = int(testcase['exec']['caller'], 0)
@@ -108,7 +183,17 @@ def test_{testname}(self):

output += f'''
address = {hex(address)}
price = {hex(price)}'''
caller = {hex(caller)}'''

if symbolic:
output += format_bitvec('price', price)
output += format_bitvec('value', value)
output += format_bitvec('gas', gas)
else:
output += f'''
price = {hex(price)}
value = {value}
gas = {gas}'''

if calldata:
output += f'''
@@ -118,10 +203,6 @@ def test_{testname}(self):
data = ''"""

output += f'''
caller = {hex(caller)}
value = {value}
gas = {gas}

# open a fake tx, no funds send
world._open_transaction('CALL', address, price, data, caller, value, gas=gas)

@@ -137,7 +218,15 @@ def test_{testname}(self):
if result in ('RETURN', 'REVERT'):
returndata = to_constant(e.data)
except evm.StartTx as e:
self.fail('This tests should not initiate an internal tx (no CALLs allowed)')'''
self.fail('This tests should not initiate an internal tx (no CALLs allowed)')

# World sanity checks - those should not change, right?
self.assertEqual(solve(world.block_number()), {blocknumber})
self.assertEqual(solve(world.block_gaslimit()), {gaslimit})
self.assertEqual(solve(world.block_timestamp()), {timestamp})
self.assertEqual(solve(world.block_difficulty()), {difficulty})
self.assertEqual(solve(world.block_coinbase()), {coinbase})
'''

return output

@@ -154,19 +243,19 @@ def generate_post_output(testcase):
account_balance = int(account['balance'], 0)

output += f'''
# Add pos checks for account {hex(account_address)}
# Add post checks for account {hex(account_address)}
# check nonce, balance, code
self.assertEqual(world.get_nonce({hex(account_address)}), {account_nonce})
self.assertEqual(to_constant(world.get_balance({hex(account_address)})), {account_balance})
self.assertEqual(solve(world.get_nonce({hex(account_address)})), {account_nonce})
self.assertEqual(solve(world.get_balance({hex(account_address)})), {account_balance})
self.assertEqual(world.get_code({hex(account_address)}), unhexlify('{account_code}'))'''

if account['storage']:
output += '''
#check storage'''
# check storage'''

for key, value in account['storage'].items():
output += f'''
self.assertEqual(to_constant(world.get_storage_data({hex(account_address)}, {key})), {value})'''
self.assertEqual(solve(world.get_storage_data({hex(account_address)}, {key})), {value})'''

output += f'''
# check outs
@@ -182,7 +271,7 @@ def generate_post_output(testcase):

output += f'''
# test used gas
self.assertEqual(to_constant(world.current_vm.gas), {int(testcase['gas'], 0)})'''
self.assertEqual(solve(world.current_vm.gas), {int(testcase['gas'], 0)})'''

return output

@@ -192,6 +281,8 @@ def generate_post_output(testcase):
print(__doc__)
sys.exit()

symbolic = '--symbolic' in sys.argv

filename_or_folder = os.path.abspath(sys.argv[1])

output = f'''"""DO NOT MODIFY: Tests generated from `{os.path.join(*filename_or_folder.split('/')[-2:])}` with make_VMTests.py"""
@@ -206,7 +297,7 @@ def generate_post_output(testcase):
Binary,
)

from manticore.core.smtlib import ConstraintSet
from manticore.core.smtlib import ConstraintSet, Z3Solver # Ignore unused import in non-symbolic tests!
from manticore.core.smtlib.visitors import to_constant
from manticore.platforms import evm

@@ -249,7 +340,7 @@ def tearDownClass(cls):
filename = os.path.join(folder, filename)
testcase = dict(json.loads(open(filename).read()))
for name, testcase in testcase.items():
output += gen_test(testcase, filename, skip='Performance' in filename) + '\n'
output += gen_test(testcase, filename, symbolic=symbolic, skip='Performance' in filename) + '\n'
cnt += 1

print(f'Generated {cnt} testcases from jsons in {folder}.', file=sys.stderr)
@@ -258,19 +349,20 @@ def tearDownClass(cls):
filename = os.path.abspath(filename_or_folder)
testcase = dict(json.loads(open(filename).read()))
for name, testcase in testcase.items():
output += gen_test(testcase, filename, skip='Performance' in filename) + '\n'
output += gen_test(testcase, filename, symbolic=symbolic, skip='Performance' in filename) + '\n'

output += '''

if __name__ == '__main__':
unittest.main()
'''
postfix = 'symbolic' if symbolic else 'concrete'

if folder:
testname = folder.split('/')[-1]
with open(f'VMTests/test_{testname}.py', 'w') as f:
with open(f'VMTests_{postfix}/test_{testname}.py', 'w') as f:
f.write(output)
with open(f'VMTests/__init__.py', 'w') as f:
with open(f'VMTests_{postfix}/__init__.py', 'w') as f:
f.write("# DO NOT DELETE")
print("Tests generated. If this is the only output, u did sth bad.", file=sys.stderr)
else:
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@
Binary,
)

from manticore.core.smtlib import ConstraintSet
from manticore.core.smtlib import ConstraintSet, Z3Solver # Ignore unused import in non-symbolic tests!
from manticore.core.smtlib.visitors import to_constant
from manticore.platforms import evm

@@ -48,24 +48,36 @@ def test_suicide(self):
SELFDESTRUCT
"""

def solve(val):
"""
Those tests are **auto-generated** and `solve` is used in symbolic tests.
So yes, this returns just val; it makes it easier to generate tests like this.
"""
return val

constraints = ConstraintSet()
world = evm.EVMWorld(constraints, blocknumber=0, timestamp=1, difficulty=256,
coinbase=244687034288125203496486448490407391986876152250, gaslimit=1000000)

blocknumber = 0
timestamp = 1
difficulty = 256
coinbase = 244687034288125203496486448490407391986876152250
gaslimit = 1000000
world = evm.EVMWorld(constraints, blocknumber=blocknumber, timestamp=timestamp, difficulty=difficulty,
coinbase=coinbase, gaslimit=gaslimit)

bytecode = unhexlify('33ff')
world.create_account(
address=0xf572e5295c57f15886f9b263e2f6d2d6c7b5ec6,
balance=100000000000000000000000,
code=bytecode,
nonce=0
)
acc_addr = 0xf572e5295c57f15886f9b263e2f6d2d6c7b5ec6
acc_code = unhexlify('33ff')
acc_balance = 100000000000000000000000
acc_nonce = 0

world.create_account(address=acc_addr, balance=acc_balance, code=acc_code, nonce=acc_nonce)

address = 0xf572e5295c57f15886f9b263e2f6d2d6c7b5ec6
price = 0x5af3107a4000
data = ''
caller = 0xcd1722f3947def4cf144679da39c4c32bdc35681
price = 0x5af3107a4000
value = 1000000000000000000
gas = 100000

data = ''
# open a fake tx, no funds send
world._open_transaction('CALL', address, price, data, caller, value, gas=gas)

@@ -82,10 +94,18 @@ def test_suicide(self):
returndata = to_constant(e.data)
except evm.StartTx as e:
self.fail('This tests should not initiate an internal tx (no CALLs allowed)')
# Add pos checks for account 0xcd1722f3947def4cf144679da39c4c32bdc35681

# World sanity checks - those should not change, right?
self.assertEqual(solve(world.block_number()), 0)
self.assertEqual(solve(world.block_gaslimit()), 1000000)
self.assertEqual(solve(world.block_timestamp()), 1)
self.assertEqual(solve(world.block_difficulty()), 256)
self.assertEqual(solve(world.block_coinbase()), 244687034288125203496486448490407391986876152250)

# Add post checks for account 0xcd1722f3947def4cf144679da39c4c32bdc35681
# check nonce, balance, code
self.assertEqual(world.get_nonce(0xcd1722f3947def4cf144679da39c4c32bdc35681), 0)
self.assertEqual(to_constant(world.get_balance(0xcd1722f3947def4cf144679da39c4c32bdc35681)), 100000000000000000000000)
self.assertEqual(solve(world.get_nonce(0xcd1722f3947def4cf144679da39c4c32bdc35681)), 0)
self.assertEqual(solve(world.get_balance(0xcd1722f3947def4cf144679da39c4c32bdc35681)), 100000000000000000000000)
self.assertEqual(world.get_code(0xcd1722f3947def4cf144679da39c4c32bdc35681), unhexlify(''))
# check outs
self.assertEqual(returndata, unhexlify(''))
@@ -95,7 +115,7 @@ def test_suicide(self):
self.assertEqual(sha3.keccak_256(data).hexdigest(), '1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347')

# test used gas
self.assertEqual(to_constant(world.current_vm.gas), 99998)
self.assertEqual(solve(world.current_vm.gas), 99998)


if __name__ == '__main__':
Loading