Add infrastructure for core models (#244)

* Rename libc.py to models.py

* Clean old unused libc.py code

* Make models top level importable

* Add State level model invocation function

So user is not required to pass in state at to a platform level func

* Explicitly mark what is in the public API

Protects against accidentally making something a public API just because
it has a docstring

* clean

* Move models.py to top level

* Rm models

* Fix docstring typo

* Add default param name, move comment

* Update docstring
This commit is contained in:
Mark Mossberg
2017-05-11 13:25:43 -04:00
committed by GitHub
parent a0717aa661
commit 1e76998eb7
7 changed files with 16 additions and 175 deletions
+2 -2
View File
@@ -13,13 +13,13 @@ Manticore
---------
.. autoclass:: manticore.Manticore
:members:
:members: add_hook, hook, run, terminate, verbosity
State
-----
.. autoclass:: manticore.core.state.State
:members:
:members: abandon, constrain, new_symbolic_buffer, new_symbolic_value, solve_n, solve_one, symbolicate_buffer, invoke_model
Cpu
---
+1 -1
View File
@@ -1,2 +1,2 @@
from .manticore import Manticore
from .utils.helpers import issymbolic
from .utils.helpers import issymbolic
+1 -1
View File
@@ -385,7 +385,7 @@ class Cpu(object):
:param str register: register name (as listed in `self.all_registers`)
:return: register value
:rtype int or long or Expression
:rtype: int or long or Expression
'''
return self._regfile.read(register)
+11
View File
@@ -234,3 +234,14 @@ class State(object):
self.branches[key] += 1
except KeyError:
self.branches[key] = 1
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.
:param callable model: Model to invoke
'''
# TODO(mark): this can't support varargs core models!
self.platform.invoke_model(model, prefix_args=(self,))
+1 -1
View File
@@ -304,7 +304,7 @@ class Manticore(object):
'''
Add a callback to be invoked on executing a program counter. Pass `None`
for pc to invoke callback on every instruction. `callback` should be a callable
that takes one :class:`~manticore.core.executor.State` argument.
that takes one :class:`~manticore.core.state.State` argument.
:param pc: Address of instruction to hook
:type pc: int or None
View File
-170
View File
@@ -1,170 +0,0 @@
import sys, os, struct
import StringIO
import logging
import random
from ..core.smtlib import solver, Expression, Operators
#, Interruption, Syscall, ConcretizeRegister, ConcretizeMemory, ConcretizeArgument, IgnoreAPI
from ..core.cpu.abstractcpu import Interruption, Syscall, \
ConcretizeRegister, ConcretizeArgument, IgnoreAPI, \
ConcretizeMemory
from ..core.memory import MemoryException
from ..core.executor import ForkState
from ..utils.helpers import issymbolic
logger = logging.getLogger("MODEL")
def _memset_range(cpu, dst, value, rng):
minval, maxval, symb_size = rng
if minval == maxval:
# no range, just write N elements
for i in xrange(0, maxval):
cpu.write_int(dst+i, value, 8)
else:
# up to minval doesn't depend on range
for i in xrange(0, minval):
cpu.write_int(dst+i, value, 8)
# write range dependent values
for i in xrange(minval, maxval):
cur_v = cpu.read_int(dst+i, 8)
cpu.write_int(dst+i, Operators.ITEBV(8, symb_size >= i, value, cur_v), 8)
return dst
def _memmove_range(cpu, dst, src, rng):
minval, maxval, symb_size = rng
# read source bytes
src_bytes = [cpu.read_int(src+i, 8) for i in xrange(0, maxval)]
if minval == maxval:
# no range, just write N elements
for (i,b) in enumerate(src_bytes):
cpu.write_int(dst+i, b, 8)
else:
# up to minval doesn't depend on range
for i in xrange(0, minval):
cpu.write_int(dst+i, src_bytes[i], 8)
# write range dependent values
for i in xrange(minval, maxval):
cur_v = cpu.read_int(dst+i, 8)
cpu.write_int(dst+i, Operators.ITEBV(8, symb_size >= i, src_bytes[i], cur_v), 8)
return dst
class strings(object):
@staticmethod
def memcpy(state, dst, src, size):
return strings.memmove(state, dst, src, size)
@staticmethod
def memmove(state, dst, src, size):
"""void *memmove(void *dest, const void *src, size_t n);"""
cpu = state.cpu
if issymbolic(size):
single_sol = solver.get_all_values(state.constraints, size, maxcnt=2, silent=True)
if len(single_sol) == 1:
size = single_sol[0]
logger.info("memmoving single solution size: {:d}".format(size))
return _memmove_range(cpu, dst, src, (size, size, None) )
else:
conc_min, conc_max = solver.minmax(state.constraints, size)
logger.info("memmoving sizes: {:d} - {:d}".format(conc_min, conc_max))
return _memmove_range(cpu, dst, src, (conc_min, conc_max, size) )
else:
# concrete case
logger.info("memmoving concrete size: {:d}".format(size))
return _memmove_range(cpu, dst, src, (size, size, None) )
@staticmethod
def memset(state, dst, char, size):
cpu = state.cpu
if issymbolic(size):
single_sol = solver.get_all_values(state.constraints, size, maxcnt=2, silent=True)
if len(single_sol) == 1:
size = single_sol[0]
logger.info("memsetting single solution size: {:d}".format(size))
return _memset_range(cpu, dst, char, (size, size, None) )
else:
conc_min, conc_max = solver.minmax(state.constraints, size)
logger.info("memsetting in a range: {:d} - {:d}".format(conc_min, conc_max))
return _memset_range(cpu, dst, char, (conc_min, conc_max, size) )
else:
# concrete case
logger.info("memsetting concrete size: {:d}".format(size))
return _memset_range(cpu, dst, char, (size, size, None) )
@staticmethod
def strlen(state, src):
cpu = state.cpu
count = 0
while True:
value = cpu.read_int(src+count, 8)
if issymbolic(value):
if solver.can_be_true(state.constraints, value==0):
raise ForkState(value==0)
elif value == 0:
break
count += 1
return count
@staticmethod
def strcpy(state, dst, src):
cpu = state.cpu
s = []
i = 0
while True:
value = cpu.read_int(src, 8)
if not cpu.mem.isWritable(dst+i):
raise MemoryException("No access writing", dst+i)
if issymbolic(value):
if solver.can_be_true(state.constraints, value==0):
raise ConcretizeMemory(src+i)
else:
break
elif value == 0:
break
s.append(value)
for i in xrange(len(s)):
cpu.write_int(dst+i, s[i], 8)
class heap(object):
@staticmethod
def malloc(cpu, size):
if issymbolic(size):
logger.info("malloc(Symbolic Size); concretizing size")
raise ConcretizeArgument(0)
else:
raise IgnoreAPI("malloc({:08x})".format(size))
@staticmethod
def realloc(cpu, ptr, size):
if issymbolic(size):
logger.info("realloc({}, Symbolic Size); concretizing size".format(str(ptr)))
raise ConcretizeArgument(1)
else:
raise IgnoreAPI("realloc({}, {:08x})".format(str(ptr), size))
@staticmethod
def calloc(cpu, count, size):
if issymbolic(size):
logger.info("calloc({}, Symbolic Size); concretizing size".format(str(count)))
raise ConcretizeArgument(1)
if issymbolic(count):
logger.info("calloc(Symbolic count, {}); concretizing count".format(str(size)))
raise ConcretizeArgument(0)
raise IgnoreAPI("calloc({:08x}, {:08x})".format(count, size))