Implement user variadic model interface (#276)

* Add variadic decorator

* Add check for _variadic func attribute

* Expose @variadic at top level

* Use variable for the actual name of attr

* Update naming

* Use helper

* Maybe more pythonic?

* Add variadic to public API

* Add variadic decorator tests

* Clean

* Remove variadic parameter

* Add docstrings

* Document parameter

* Clean

* Better use of autodoc

* Update docstring

* Add todo comment
This commit is contained in:
Mark Mossberg
2017-05-31 15:48:19 -04:00
committed by GitHub
parent aeca64285a
commit 5b5392e54d
8 changed files with 58 additions and 17 deletions

View File

@@ -7,7 +7,8 @@ This API is under active development, and should be considered unstable.
Helpers
-------
.. autofunction:: manticore.issymbolic
.. automodule:: manticore
:members: issymbolic, variadic
Manticore
---------

View File

@@ -1,2 +1,3 @@
from .manticore import Manticore
from .models import variadic
from .utils.helpers import issymbolic

View File

@@ -220,16 +220,16 @@ class Abi(object):
yield base
base += word_bytes
def invoke(self, model, prefix_args=None, varargs=False):
def invoke(self, model, prefix_args=None):
'''
Invoke a callable `model` as if it was a native function. If `varargs`
is true, model receives a single argument that is a generator for
function arguments. Pass a tuple of arguments for `prefix_args` you'd
like to precede the actual arguments.
Invoke a callable `model` as if it was a native function. If
:func:`~manticore.models.isvariadic` returns true for `model`, `model` receives a single
argument that is a generator for function arguments. Pass a tuple of
arguments for `prefix_args` you'd like to precede the actual
arguments.
:param callable model: Python model of the function
:param tuple prefix_args: Parameters to pass to model before actual ones
:param bool varargs: Whether the function expects a variable number of arguments
:return: The result of calling `model`
'''
prefix_args = prefix_args or ()
@@ -255,8 +255,11 @@ class Abi(object):
descriptors = self.get_arguments()
argument_iter = imap(resolve_argument, descriptors)
# TODO(mark) this is here as a hack to avoid circular import issues
from ...models import isvariadic
try:
if varargs:
if isvariadic(model):
result = model(*(prefix_args + (argument_iter,)))
else:
argument_tuple = prefix_args + tuple(islice(argument_iter, nargs))
@@ -265,7 +268,7 @@ class Abi(object):
assert e.argnum >= len(prefix_args), "Can't concretize a constant arg"
idx = e.argnum - len(prefix_args)
# Arguments were lazily computed in case of varargs, so recompute here
# Arguments were lazily computed in case of variadic, so recompute here
descriptors = self.get_arguments()
src = next(islice(descriptors, idx, idx+1))

View File

@@ -243,10 +243,11 @@ class State(object):
def invoke_model(self, model):
'''
Invoke a `model`. A `model` is a callable whose first argument is a
:class:`~manticore.core.state.State`, and whose following arguments correspond to
the C function being modeled.
:class:`~manticore.core.state.State`. If the `model` models a normal (non-variadic)
function, the following arguments correspond to the arguments of the C function
being modeled. If the `model` models a variadic function, the following argument
is a generator object, which can be used to access function arguments dynamically.
:param callable model: Model to invoke
'''
# TODO(mark): this can't support varargs core models!
self.platform.invoke_model(model, prefix_args=(self,))

View File

@@ -3,6 +3,26 @@ from .utils.helpers import issymbolic
from .core.smtlib.solver import solver
from .core.smtlib.operators import ITEBV, ZEXTEND
VARIADIC_FUNC_ATTR = '_variadic'
def isvariadic(model):
"""
:param callable model: Function model
:return: Whether `model` models a variadic function
:rtype: bool
"""
return getattr(model, VARIADIC_FUNC_ATTR, False)
def variadic(func):
"""
A decorator used to mark a function model as variadic. This function should
take two parameters: a :class:`~manticore.core.state.State` object, and
a generator object for the arguments.
:param callable func: Function model
"""
setattr(func, VARIADIC_FUNC_ATTR, True)
return func
def _find_zero(cpu, constrs, ptr):
"""

View File

@@ -9,5 +9,5 @@ class Platform(object):
def __init__(self, path):
self._path = path
def invoke_model(self, model, prefix_args=None, varargs=False):
self._function_abi.invoke(model, prefix_args, varargs)
def invoke_model(self, model, prefix_args=None):
self._function_abi.invoke(model, prefix_args)

View File

@@ -12,6 +12,7 @@ import collections
import time
from manticore import Manticore, issymbolic
from manticore import variadic
from manticore.core.smtlib import BitVecVariable
from manticore.core.cpu.abstractcpu import ConcretizeArgument, ConcretizeRegister, ConcretizeMemory
from manticore.core.cpu.arm import Armv7Cpu, Armv7LinuxSyscallAbi, Armv7CdeclAbi
@@ -250,11 +251,12 @@ class ABITests(unittest.TestCase):
# save return
cpu.push(0x1234, cpu.address_bit_size)
@variadic
def test(params):
for val, idx in zip(params, range(1, 4)):
self.assertEqual(val, idx)
cpu.func_abi.invoke(test, varargs=True)
cpu.func_abi.invoke(test)
self.assertEquals(cpu.EIP, 0x1234)
@@ -333,11 +335,12 @@ class ABITests(unittest.TestCase):
# save return
cpu.push(0x1234, cpu.address_bit_size)
@variadic
def test(params):
for val, idx in zip(params, range(3)):
self.assertEqual(val, idx)
cpu.func_abi.invoke(test, varargs=True)
cpu.func_abi.invoke(test)
self.assertEquals(cpu.RIP, 0x1234)

View File

@@ -4,7 +4,19 @@ from manticore.core.smtlib import ConstraintSet, solver
from manticore.core.state import State
from manticore.platforms import linux
from manticore.models import strcmp, strlen
from manticore.models import variadic, isvariadic, strcmp, strlen
class ModelMiscTest(unittest.TestCase):
def test_variadic_dec(self):
@variadic
def f():
pass
self.assertTrue(isvariadic(f))
def test_no_variadic_dec(self):
def f():
pass
self.assertFalse(isvariadic(f))
class ModelTest(unittest.TestCase):