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:
parent
61babdbe21
commit
cf789a4291
@ -1,6 +1,8 @@
|
||||
''' Symbolic EVM implementation based on the yellow paper: http://gavwood.com/paper.pdf '''
|
||||
import random
|
||||
import copy
|
||||
import inspect
|
||||
from functools import wraps
|
||||
from ..utils.helpers import issymbolic, memoized
|
||||
from ..platforms.platform import *
|
||||
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, ))
|
||||
|
||||
|
||||
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):
|
||||
'''Machine State. The machine state is defined as
|
||||
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'''
|
||||
return len(self.bytecode)
|
||||
|
||||
@concretized_args(size='')
|
||||
def CODECOPY(self, mem_offset, code_offset, size):
|
||||
'''Copy code running in current environment to memory'''
|
||||
if issymbolic(size):
|
||||
raise ConcretizeStack(3)
|
||||
GCOPY = 3 # cost to copy one 32 byte word
|
||||
self._consume(GCOPY * ceil32(size) // 32)
|
||||
|
||||
@ -1775,11 +1813,9 @@ class EVM(Eventful):
|
||||
|
||||
##########################################################################
|
||||
# Logging Operations
|
||||
@concretized_args(size='ONE')
|
||||
def LOG(self, address, size, *topics):
|
||||
|
||||
if issymbolic(size):
|
||||
raise ConcretizeStack(2, policy='ONE')
|
||||
|
||||
memlog = self.read_buffer(address, size)
|
||||
|
||||
self.logs.append(EVMLog(self.address, memlog, topics))
|
||||
@ -1813,12 +1849,9 @@ class EVM(Eventful):
|
||||
code = self.read_buffer(offset, size)
|
||||
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):
|
||||
'''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)
|
||||
raise Call(gas, to, value, data, out_offset, out_size)
|
||||
|
||||
@ -2,9 +2,11 @@ import unittest
|
||||
import os
|
||||
|
||||
from manticore.core.smtlib import ConstraintSet, operators
|
||||
from manticore.core.smtlib.expression import BitVec
|
||||
from manticore.core.state import State
|
||||
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__))
|
||||
|
||||
@ -122,3 +124,47 @@ class EthTests(unittest.TestCase):
|
||||
x = mevm.create_account(balance=0)
|
||||
contract_account = mevm.solidity_create_contract(source_code,
|
||||
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)
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user