Skip to content

Commit f39e6ba

Browse files
feliamekilmerEric Hennenfent
authored
Manticore verifier (#1717)
* Add ONE failing test * Fix test * Try fix get-related * Snapshots * CC * Allow snapshooting just from main() * A test * Test and fix is_main * CC * review * ismain vs is_running * is main to is main script * Remove unused member variable * initial import manticore verifier * Fix verifier installation * clear ready states * CC and smoke test * CC * Increase cov * Move regression to other * blkn * blkn * Move get related * CC * fix concolic * lint * DivByZero default to zero * blkn * Update manticore/core/smtlib/solver.py Co-authored-by: Eric Kilmer <[email protected]> * Update manticore/core/smtlib/constraints.py Co-authored-by: Eric Kilmer <[email protected]> * Update manticore/core/smtlib/constraints.py Co-authored-by: Eric Kilmer <[email protected]> * remove odd string * lint * mypy lint * Update manticore/core/smtlib/visitors.py Co-authored-by: Eric Kilmer <[email protected]> * lint * Add Docs * blkn * blkn * Replace modulo with masks * Blacken * blkn * fix mypy * fix mypy * Add tests for signed LT behavior * New test * Fix constant folding * lint * blkn * lint * Fixes... * lint * Unittesting power * Permisive read_buffer * Fix optimize * Preserve precision in constant folding * Strip left-in print debugging * blkn * remove wasm_sym temporarily * Better simplification for constants * Add json * blkn * Better commmandline args * blkn * fix wasm * Fix * better commanlining * REmove get_related from the default path and fix arm test * blkn * fix related to tests * blkn * Fix bug in test and disable debug messages in Solver * smtlib config to disable multiple check-sat in newer z3 * Disable log test and fix merging poc vs variable migration * blkn * Avoid exception in some callbacks * can_rais at did_will * yikes! * blkn * Yices found one more states in truffle * Add a config constant to ignore symbolic balances * Add a config constant to ignore symbolic balances 2 * Relax truffle state count * Relax truffle state count 2 * Change cli name and test * Clear redy states in verifier workspace * Clear ready states in verifier workspace * Relative ws path on verifier output. Clean CREATE testcase example * Blkn * Improve coverage and funcid solving * lint * Better testcase generation at verifier * Better testcase generation at verifier and typos * Better output and default re * Test must revert * Better output and default re * move generate_testcase_ex to private method Co-authored-by: Eric Kilmer <[email protected]> Co-authored-by: Eric Hennenfent <[email protected]>
1 parent 61909a1 commit f39e6ba

File tree

13 files changed

+677
-77
lines changed

13 files changed

+677
-77
lines changed

manticore/__main__.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,9 +212,7 @@ def positive(value):
212212
)
213213

214214
eth_flags.add_argument(
215-
"--limit-loops",
216-
action="store_true",
217-
help="Avoid exploring constant functions for human transactions",
215+
"--limit-loops", action="store_true", help="Limit loops depth",
218216
)
219217

220218
eth_flags.add_argument(

manticore/core/manticore.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from ..utils.helpers import PickleSerializer
2424
from ..utils.log import set_verbosity
2525
from ..utils.nointerrupt import WithKeyboardInterruptAs
26-
from .workspace import Workspace
26+
from .workspace import Workspace, Testcase
2727
from .worker import WorkerSingle, WorkerThread, WorkerProcess
2828

2929
from multiprocessing.managers import SyncManager
@@ -403,8 +403,7 @@ def goto_snapshot(self):
403403
in a snapshot """
404404
if not self._snapshot:
405405
raise ManticoreError("No snapshot to go to")
406-
for state_id in tuple(self._ready_states):
407-
self._ready_states.remove(state_id)
406+
self.clear_ready_states()
408407
for state_id in self._snapshot:
409408
self._ready_states.append(state_id)
410409
self._snapshot = None
@@ -421,13 +420,23 @@ def clear_snapshot(self):
421420
@sync
422421
@at_not_running
423422
def clear_terminated_states(self):
424-
""" Simply remove all states from terminated list """
423+
""" Remove all states from the terminated list """
425424
terminated_states_ids = tuple(self._terminated_states)
426425
for state_id in terminated_states_ids:
427426
self._terminated_states.remove(state_id)
428427
self._remove(state_id)
429428
assert self.count_terminated_states() == 0
430429

430+
@sync
431+
@at_not_running
432+
def clear_ready_states(self):
433+
""" Remove all states from the ready list """
434+
ready_states_ids = tuple(self._ready_states)
435+
for state_id in ready_states_ids:
436+
self._ready_states.remove(state_id)
437+
self._remove(state_id)
438+
assert self.count_ready_states() == 0
439+
431440
def __str__(self):
432441
return f"<{str(type(self))[8:-2]}| Alive States: {self.count_ready_states()}; Running States: {self.count_busy_states()} Terminated States: {self.count_terminated_states()} Killed States: {self.count_killed_states()} Started: {self._running.value} Killed: {self._killed.value}>"
433442

@@ -833,7 +842,7 @@ def count_terminated_states(self):
833842
""" Terminated states count """
834843
return len(self._terminated_states)
835844

836-
def generate_testcase(self, state, message: str = "test", name: str = "test"):
845+
def generate_testcase(self, state, message: str = "test", name: str = "test") -> Testcase:
837846
if message == "test" and hasattr(state, "_terminated_by") and state._terminated_by:
838847
message = str(state._terminated_by)
839848
testcase = self._output.testcase(prefix=name)

manticore/core/smtlib/solver.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -593,7 +593,7 @@ def get_value(self, constraints: ConstraintSet, *expressions):
593593
"""
594594
values = []
595595
start = time.time()
596-
with constraints as temp_cs:
596+
with constraints.related_to(*expressions) as temp_cs:
597597
for expression in expressions:
598598
if not issymbolic(expression):
599599
values.append(expression)

manticore/core/workspace.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def __exit__(self, *excinfo):
4444
consts.add("dir", default=".", description="Location of where to create workspace directories")
4545

4646

47-
class WorkspaceTestcase:
47+
class Testcase:
4848
"""
4949
A `WorkspaceTestCase` represents a test case input generated by Manticore.
5050
"""
@@ -519,8 +519,8 @@ def __init__(self, desc=None):
519519
self._descriptor = desc
520520
self._store = Store.fromdescriptor(desc)
521521

522-
def testcase(self, prefix: str = "test") -> WorkspaceTestcase:
523-
return WorkspaceTestcase(self, prefix)
522+
def testcase(self, prefix: str = "test") -> Testcase:
523+
return Testcase(self, prefix)
524524

525525
@property
526526
def store(self):
@@ -587,7 +587,7 @@ def _named_stream(self, name, binary=False, lock=False):
587587
yield s
588588

589589
# Remove/move ...
590-
def save_testcase(self, state: StateBase, testcase: WorkspaceTestcase, message: str = ""):
590+
def save_testcase(self, state: StateBase, testcase: Testcase, message: str = ""):
591591
"""
592592
Save the environment from `state` to storage. Return a state id
593593
describing it, which should be an int or a string.

manticore/ethereum/manticore.py

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313

1414
from crytic_compile import CryticCompile, InvalidCompilation, is_supported
1515

16-
from ..core.manticore import ManticoreBase
16+
from ..core.manticore import ManticoreBase, Testcase, ManticoreError
17+
1718
from ..core.smtlib import (
1819
ConstraintSet,
1920
Array,
@@ -1536,10 +1537,6 @@ def current_location(self, state):
15361537

15371538
def generate_testcase(self, state, message="", only_if=None, name="user"):
15381539
"""
1539-
Generate a testcase to the workspace for the given program state. The details of what
1540-
a testcase is depends on the type of Platform the state is, but involves serializing the state,
1541-
and generating an input (concretizing symbolic variables) to trigger this state.
1542-
15431540
The only_if parameter should be a symbolic expression. If this argument is provided, and the expression
15441541
*can be true* in this state, a testcase is generated such that the expression will be true in the state.
15451542
If it *is impossible* for the expression to be true in the state, a testcase is not generated.
@@ -1552,29 +1549,38 @@ def generate_testcase(self, state, message="", only_if=None, name="user"):
15521549
15531550
m.generate_testcase(state, 'balance CAN be 0', only_if=balance == 0)
15541551
# testcase generated with an input that will violate invariant (make balance == 0)
1552+
"""
1553+
try:
1554+
with state as temp_state:
1555+
if only_if is not None:
1556+
temp_state.constrain(only_if)
1557+
return self._generate_testcase_ex(temp_state, message, name=name)
1558+
except ManticoreError:
1559+
return None
1560+
1561+
def _generate_testcase_ex(self, state, message="", name="user"):
1562+
"""
1563+
Generate a testcase in the outputspace for the given program state.
1564+
An exception is raised if the state is unsound or unfeasible
1565+
1566+
On return you get a Testcase containing all the informations about the state.
1567+
A Testcase is a collection of files living at the OutputSpace.
1568+
Registered plugins and other parts of Manticore add files to the Testcase.
1569+
User can add more streams to is like this:
1570+
1571+
testcase = m.generate_testcase_ex(state)
1572+
with testcase.open_stream("txt") as descf:
1573+
descf.write("This testcase is interesting!")
15551574
15561575
:param manticore.core.state.State state:
15571576
:param str message: longer description of the testcase condition
1558-
:param manticore.core.smtlib.Bool only_if: only if this expr can be true, generate testcase. if is None, generate testcase unconditionally.
1559-
:param str name: short string used as the prefix for the workspace key (e.g. filename prefix for testcase files)
1560-
:return: If a testcase was generated
1577+
:param str name: short string used as the prefix for the outputspace key (e.g. filename prefix for testcase files)
1578+
:return: a Testcase
15611579
:rtype: bool
15621580
"""
1563-
"""
1564-
Create a serialized description of a given state.
1565-
:param state: The state to generate information about
1566-
:param message: Accompanying message
1567-
"""
1581+
# Refuse to generate a testcase from an unsound state
15681582
if not self.fix_unsound_symbolication(state):
1569-
return False
1570-
1571-
if only_if is not None:
1572-
with state as temp_state:
1573-
temp_state.constrain(only_if)
1574-
if temp_state.is_feasible():
1575-
return self.generate_testcase(temp_state, message, only_if=None, name=name)
1576-
else:
1577-
return False
1583+
raise ManticoreError("Trying to generate a testcase out of an unsound state path")
15781584

15791585
blockchain = state.platform
15801586

@@ -1584,8 +1590,6 @@ def generate_testcase(self, state, message="", only_if=None, name="user"):
15841590
testcase = super().generate_testcase(
15851591
state, message + f"({len(blockchain.human_transactions)} txs)", name=name
15861592
)
1587-
# TODO(mark): Refactor ManticoreOutput to let the platform be more in control
1588-
# so this function can be fully ported to EVMWorld.generate_workspace_files.
15891593

15901594
local_findings = set()
15911595
for detector in self.detectors.values():
@@ -1865,8 +1869,8 @@ def ready_sound_states(self):
18651869
This tries to solve any symbolic imprecision added by unsound_symbolication
18661870
and then iterates over the resultant set.
18671871
1868-
This is the recommended to iterate over resultant steas after an exploration
1869-
that included unsound symbolication
1872+
This is the recommended way to iterate over the resultant states after
1873+
an exploration that included unsound symbolication
18701874
18711875
"""
18721876
self.fix_unsound_all()

0 commit comments

Comments
 (0)