Introduce concretized_args decorator (#803)

* Introduce concretized_args decorator

* Add docs

* Cleanup

* Add basic expression counter

* Please codeclimate

* pep8

* update docstring

* Use default concretization policy when not provided

* Add concretizer tests
This commit is contained in:
Yan Ivnitskiy 2018-03-14 16:45:12 -04:00 committed by GitHub
parent 61babdbe21
commit cf789a4291
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 89 additions and 10 deletions

View File

@ -1,6 +1,8 @@
''' Symbolic EVM implementation based on the yellow paper: http://gavwood.com/paper.pdf ''' ''' Symbolic EVM implementation based on the yellow paper: http://gavwood.com/paper.pdf '''
import random import random
import copy import copy
import inspect
from functools import wraps
from ..utils.helpers import issymbolic, memoized from ..utils.helpers import issymbolic, memoized
from ..platforms.platform import * from ..platforms.platform import *
from ..core.smtlib import solver, TooManySolutions, Expression, Bool, BitVec, Array, Operators, Constant, BitVecConstant, ConstraintSet, \ from ..core.smtlib import solver, TooManySolutions, Expression, Bool, BitVec, Array, Operators, Constant, BitVecConstant, ConstraintSet, \
@ -1067,6 +1069,43 @@ class Sha3(EVMException):
return (self.__class__, (self.data, )) return (self.__class__, (self.data, ))
def concretized_args(**policies):
"""
Make sure an EVM instruction has all of its arguments concretized according to
provided policies.
Example decoration:
@concretized_args(size='ONE', address='')
def LOG(self, address, size, *topics):
...
The above will make sure that the |size| parameter to LOG is Concretized when symbolic
according to the 'ONE' policy and concretize |address| with the default policy.
:param policies: A kwargs list of argument names and their respective policies.
Provide None or '' as policy to use default.
:return: A function decorator
"""
def concretizer(func):
@wraps(func)
def wrapper(*args, **kwargs):
spec = inspect.getargspec(func)
for arg, policy in policies.items():
assert arg in spec.args, "Concretizer argument not found in wrapped function."
# index is 0-indexed, but ConcretizeStack is 1-indexed. However, this is correct
# since implementation method is always a bound method (self is param 0)
index = spec.args.index(arg)
if issymbolic(args[index]):
if policy:
raise ConcretizeStack(index, policy=policy)
else:
raise ConcretizeStack(index)
return func(*args, **kwargs)
return wrapper
return concretizer
class EVM(Eventful): class EVM(Eventful):
'''Machine State. The machine state is defined as '''Machine State. The machine state is defined as
the tuple (g, pc, m, i, s) which are the gas available, the the tuple (g, pc, m, i, s) which are the gas available, the
@ -1614,10 +1653,9 @@ class EVM(Eventful):
'''Get size of code running in current environment''' '''Get size of code running in current environment'''
return len(self.bytecode) return len(self.bytecode)
@concretized_args(size='')
def CODECOPY(self, mem_offset, code_offset, size): def CODECOPY(self, mem_offset, code_offset, size):
'''Copy code running in current environment to memory''' '''Copy code running in current environment to memory'''
if issymbolic(size):
raise ConcretizeStack(3)
GCOPY = 3 # cost to copy one 32 byte word GCOPY = 3 # cost to copy one 32 byte word
self._consume(GCOPY * ceil32(size) // 32) self._consume(GCOPY * ceil32(size) // 32)
@ -1775,11 +1813,9 @@ class EVM(Eventful):
########################################################################## ##########################################################################
# Logging Operations # Logging Operations
@concretized_args(size='ONE')
def LOG(self, address, size, *topics): def LOG(self, address, size, *topics):
if issymbolic(size):
raise ConcretizeStack(2, policy='ONE')
memlog = self.read_buffer(address, size) memlog = self.read_buffer(address, size)
self.logs.append(EVMLog(self.address, memlog, topics)) self.logs.append(EVMLog(self.address, memlog, topics))
@ -1813,12 +1849,9 @@ class EVM(Eventful):
code = self.read_buffer(offset, size) code = self.read_buffer(offset, size)
raise Create(value, code) raise Create(value, code)
@concretized_args(in_offset='SAMPLED', in_size='SAMPLED')
def CALL(self, gas, to, value, in_offset, in_size, out_offset, out_size): def CALL(self, gas, to, value, in_offset, in_size, out_offset, out_size):
'''Message-call into an account''' '''Message-call into an account'''
if issymbolic(in_offset):
raise ConcretizeStack(4, policy='SAMPLED')
if issymbolic(in_size):
raise ConcretizeStack(5, policy='SAMPLED')
data = self.read_buffer(in_offset, in_size) data = self.read_buffer(in_offset, in_size)
raise Call(gas, to, value, data, out_offset, out_size) raise Call(gas, to, value, data, out_offset, out_size)

View File

@ -2,9 +2,11 @@ import unittest
import os import os
from manticore.core.smtlib import ConstraintSet, operators from manticore.core.smtlib import ConstraintSet, operators
from manticore.core.smtlib.expression import BitVec
from manticore.core.state import State from manticore.core.state import State
from manticore.ethereum import ManticoreEVM, IntegerOverflow, Detector from manticore.ethereum import ManticoreEVM, IntegerOverflow, Detector
from manticore.platforms.evm import EVMWorld from manticore.platforms.evm import EVMWorld, ConcretizeStack, concretized_args
THIS_DIR = os.path.dirname(os.path.abspath(__file__)) THIS_DIR = os.path.dirname(os.path.abspath(__file__))
@ -122,3 +124,47 @@ class EthTests(unittest.TestCase):
x = mevm.create_account(balance=0) x = mevm.create_account(balance=0)
contract_account = mevm.solidity_create_contract(source_code, contract_account = mevm.solidity_create_contract(source_code,
contract_name="C", owner=owner, args=[x]) contract_name="C", owner=owner, args=[x])
class EthHelpers(unittest.TestCase):
def setUp(self):
self.bv = BitVec(256)
def test_concretizer(self):
policy = 'SOME_NONSTANDARD_POLICY'
@concretized_args(a=policy)
def inner_func(self, a, b):
return a, b
with self.assertRaises(ConcretizeStack) as cm:
inner_func(None, self.bv, 34)
self.assertEquals(cm.exception.pos, 1)
self.assertEquals(cm.exception.policy, policy)
def test_concretizer_default(self):
@concretized_args(b='')
def inner_func(self, a, b):
return a, b
with self.assertRaises(ConcretizeStack) as cm:
inner_func(None, 34, self.bv)
self.assertEquals(cm.exception.pos, 2)
# Make sure the policy isn't blank, i.e. we didn't pass through
# a falsifiable value, and we selected a default
self.assertTrue(cm.exception.policy)
self.assertNotEquals(cm.exception.policy, '')
def test_concretizer_doesnt_overreach(self):
@concretized_args(b='')
def inner_func(self, a, b):
return a, b
# Make sure we don't raise when a param is symbolic and its concretization
# wasn't requested.
inner_func(None, self.bv, 123)