Skip to content

Commit 0baebc2

Browse files
committed
Merge branch 'master' into ek/fix-concretize-syscall-args
* master: Fix inheritance magic and add plugin enable/disable (#1696) Add hashing to constraintSets (#1703) Reduce log messages (#1700) Updated Proc error message to be more informative (#1704) Move states that selfdestruct to the ready queue. (#1699) Reduce concrete hash log messages. (#1695)
2 parents ba684db + 55e2486 commit 0baebc2

File tree

11 files changed

+147
-110
lines changed

11 files changed

+147
-110
lines changed

manticore/core/manticore.py

Lines changed: 62 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -79,17 +79,65 @@ def to_class(self):
7979

8080

8181
class ManticoreBase(Eventful):
82-
def __new__(cls, *args, **kwargs):
83-
if cls in (ManticoreBase, ManticoreSingle, ManticoreThreading, ManticoreMultiprocessing):
84-
raise ManticoreError("Should not instantiate this")
82+
def _manticore_single(self):
83+
self._worker_type = WorkerSingle
8584

86-
cl = consts.mprocessing.to_class()
87-
# change ManticoreBase for the more specific class
88-
bases = {cl if issubclass(base, ManticoreBase) else base for base in cls.__bases__}
89-
cls.__bases__ = tuple(bases)
85+
class FakeLock:
86+
def _nothing(self, *args, **kwargs):
87+
pass
9088

91-
random.seed(consts.seed)
92-
return super().__new__(cls)
89+
acquire = _nothing
90+
release = _nothing
91+
__enter__ = _nothing
92+
__exit__ = _nothing
93+
notify_all = _nothing
94+
wait = _nothing
95+
96+
def wait_for(self, condition, *args, **kwargs):
97+
if not condition():
98+
raise Exception("Deadlock: Waiting for CTRL+C")
99+
100+
self._lock = FakeLock()
101+
self._killed = ctypes.c_bool(False)
102+
self._running = ctypes.c_bool(False)
103+
self._ready_states = []
104+
self._terminated_states = []
105+
self._busy_states = []
106+
self._killed_states = []
107+
self._shared_context = {}
108+
109+
def _manticore_threading(self):
110+
self._worker_type = WorkerThread
111+
self._lock = threading.Condition()
112+
self._killed = ctypes.c_bool(False)
113+
self._running = ctypes.c_bool(False)
114+
self._ready_states = []
115+
self._terminated_states = []
116+
self._busy_states = []
117+
self._killed_states = []
118+
self._shared_context = {}
119+
120+
def _manticore_multiprocessing(self):
121+
def raise_signal():
122+
signal.signal(signal.SIGINT, signal.SIG_IGN)
123+
124+
self._worker_type = WorkerProcess
125+
# This is the global manager that will handle all shared memory access
126+
# See. https://docs.python.org/3/library/multiprocessing.html#multiprocessing.managers.SyncManager
127+
self._manager = SyncManager()
128+
self._manager.start(raise_signal)
129+
# The main manticore lock. Acquire this for accessing shared objects
130+
# THINKME: we use the same lock to access states lists and shared contexts
131+
self._lock = self._manager.Condition()
132+
self._killed = self._manager.Value(bool, False)
133+
self._running = self._manager.Value(bool, False)
134+
# List of state ids of States on storage
135+
self._ready_states = self._manager.list()
136+
self._terminated_states = self._manager.list()
137+
self._busy_states = self._manager.list()
138+
self._killed_states = self._manager.list()
139+
self._shared_context = self._manager.dict()
140+
self._context_value_types = {list: self._manager.list, dict: self._manager.dict}
93141

94142
# Decorators added first for convenience.
95143
def sync(func: Callable) -> Callable: # type: ignore
@@ -255,6 +303,11 @@ def __init__(self, initial_state, workspace_url=None, outputspace_url=None, **kw
255303
:param kwargs: other kwargs, e.g.
256304
"""
257305
super().__init__()
306+
random.seed(consts.seed)
307+
{consts.mprocessing.single: self._manticore_single,
308+
consts.mprocessing.threading: self._manticore_threading,
309+
consts.mprocessing.multiprocessing: self._manticore_multiprocessing
310+
}[consts.mprocessing]()
258311

259312
if any(
260313
not hasattr(self, x)
@@ -1008,82 +1061,3 @@ def save_run_data(self):
10081061
config.save(f)
10091062

10101063
logger.info("Results in %s", self._output.store.uri)
1011-
1012-
1013-
class ManticoreSingle(ManticoreBase):
1014-
_worker_type = WorkerSingle
1015-
1016-
def __init__(self, *args, **kwargs):
1017-
class FakeLock:
1018-
def _nothing(self, *args, **kwargs):
1019-
pass
1020-
1021-
acquire = _nothing
1022-
release = _nothing
1023-
__enter__ = _nothing
1024-
__exit__ = _nothing
1025-
notify_all = _nothing
1026-
wait = _nothing
1027-
1028-
def wait_for(self, condition, *args, **kwargs):
1029-
if not condition():
1030-
raise Exception("Deadlock: Waiting for CTRL+C")
1031-
1032-
self._lock = FakeLock()
1033-
self._killed = ctypes.c_bool(False)
1034-
self._running = ctypes.c_bool(False)
1035-
1036-
self._ready_states = []
1037-
self._terminated_states = []
1038-
self._busy_states = []
1039-
self._killed_states = []
1040-
1041-
self._shared_context = {}
1042-
super().__init__(*args, **kwargs)
1043-
1044-
1045-
class ManticoreThreading(ManticoreBase):
1046-
_worker_type = WorkerThread
1047-
1048-
def __init__(self, *args, **kwargs):
1049-
self._lock = threading.Condition()
1050-
self._killed = ctypes.c_bool(False)
1051-
self._running = ctypes.c_bool(False)
1052-
1053-
self._ready_states = []
1054-
self._terminated_states = []
1055-
self._busy_states = []
1056-
self._killed_states = []
1057-
1058-
self._shared_context = {}
1059-
1060-
super().__init__(*args, **kwargs)
1061-
1062-
1063-
def raise_signal():
1064-
signal.signal(signal.SIGINT, signal.SIG_IGN)
1065-
1066-
1067-
class ManticoreMultiprocessing(ManticoreBase):
1068-
_worker_type = WorkerProcess
1069-
1070-
def __init__(self, *args, **kwargs):
1071-
# This is the global manager that will handle all shared memory access
1072-
# See. https://docs.python.org/3/library/multiprocessing.html#multiprocessing.managers.SyncManager
1073-
self._manager = SyncManager()
1074-
self._manager.start(raise_signal)
1075-
# The main manticore lock. Acquire this for accessing shared objects
1076-
# THINKME: we use the same lock to access states lists and shared contexts
1077-
self._lock = self._manager.Condition()
1078-
self._killed = self._manager.Value(bool, False)
1079-
self._running = self._manager.Value(bool, False)
1080-
1081-
# List of state ids of States on storage
1082-
self._ready_states = self._manager.list()
1083-
self._terminated_states = self._manager.list()
1084-
self._busy_states = self._manager.list()
1085-
self._killed_states = self._manager.list()
1086-
self._shared_context = self._manager.dict()
1087-
self._context_value_types = {list: self._manager.list, dict: self._manager.dict}
1088-
1089-
super().__init__(*args, **kwargs)

manticore/core/plugin.py

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,51 @@
33
import cProfile
44
import pstats
55
import threading
6+
from functools import wraps
67

78
from .smtlib import issymbolic
89

910
logger = logging.getLogger(__name__)
1011

1112

1213
class Plugin:
14+
1315
def __init__(self):
1416
self.manticore = None
17+
self._enabled_key = f"{str(type(self))}_enabled_{hash(self)}"
18+
self._plugin_context_name = f"{str(type(self))}_context_{hash(self)}"
19+
self.__decorate_callbacks()
20+
21+
def __decorate_callbacks(self):
22+
for attr in self.__dict__:
23+
if attr.endswith('_callback'):
24+
method = getattr(self, attr)
25+
if callable(method):
26+
setattr(self, attr, self._if_enabled(method))
27+
28+
def enable(self):
29+
""" Enable all callbacks """
30+
with self.manticore.locked_context() as context:
31+
context[self._enabled_key] = True
32+
33+
def disable(self):
34+
""" Disable all callbacks """
35+
with self.manticore.locked_context() as context:
36+
context[self._enabled_key] = False
37+
38+
def is_enabled(self):
39+
""" True if callbacks are enabled """
40+
with self.manticore.locked_context() as context:
41+
return context.get(self._enabled_key, True)
42+
43+
@staticmethod
44+
def _if_enabled(f):
45+
""" decorator used to guard callbacks """
46+
@wraps(f)
47+
def g(self, *args, **kwargs):
48+
if self.is_enabled():
49+
return f(self, *args, **kwargs)
50+
return g
1551

1652
@property
1753
def name(self):
@@ -25,7 +61,7 @@ def locked_context(self, key=None, value_type=list):
2561
when parallel analysis is activated. Code within the `with` block is executed
2662
atomically, so access of shared variables should occur within.
2763
"""
28-
plugin_context_name = str(type(self))
64+
plugin_context_name = self._plugin_context_name
2965
with self.manticore.locked_context(plugin_context_name, dict) as context:
3066
if key is None:
3167
yield context
@@ -37,7 +73,7 @@ def locked_context(self, key=None, value_type=list):
3773
@property
3874
def context(self):
3975
""" Convenient access to shared context """
40-
plugin_context_name = str(type(self))
76+
plugin_context_name = self._plugin_context_name
4177
if plugin_context_name not in self.manticore.context:
4278
self.manticore.context[plugin_context_name] = {}
4379
return self.manticore.context[plugin_context_name]

manticore/core/smtlib/constraints.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,16 @@
1717
Constant,
1818
)
1919
from .visitors import GetDeclarations, TranslatorSmtlib, get_variables, simplify, replace
20+
from ...utils import config
2021
import logging
2122

2223
logger = logging.getLogger(__name__)
2324

25+
consts = config.get_group("smt")
26+
consts.add(
27+
"related_constraints", default=False, description="Try slicing the current path constraint to contain only related items"
28+
)
29+
2430

2531
class ConstraintException(SmtlibError):
2632
"""
@@ -56,6 +62,9 @@ def __reduce__(self):
5662
},
5763
)
5864

65+
def __hash__(self):
66+
return hash(self.constraints)
67+
5968
def __enter__(self) -> "ConstraintSet":
6069
assert self._child is None
6170
self._child = self.__class__()
@@ -96,7 +105,6 @@ def add(self, constraint) -> None:
96105
self._constraints = [constraint]
97106
else:
98107
return
99-
100108
self._constraints.append(constraint)
101109

102110
def _get_sid(self) -> int:
@@ -118,7 +126,8 @@ def __get_related(self, related_to=None):
118126
# satisfiable one, {}.
119127
# In light of the above, the core __get_related logic is currently disabled.
120128
# if related_to is not None:
121-
if False:
129+
# feliam: This assumes the previous constraints are already SAT (normal SE forking)
130+
if consts.related_constraints and related_to is not None:
122131
number_of_constraints = len(self.constraints)
123132
remaining_constraints = set(self.constraints)
124133
related_variables = get_variables(related_to)

manticore/core/smtlib/solver.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import collections
1919
import shlex
2020
import time
21+
from functools import lru_cache
2122
from typing import Dict, Tuple
2223
from subprocess import PIPE, Popen
2324
import re
@@ -421,6 +422,7 @@ def _pop(self):
421422
"""Recall the last pushed constraint store and state."""
422423
self._send("(pop 1)")
423424

425+
@lru_cache(maxsize=32)
424426
def can_be_true(self, constraints: ConstraintSet, expression: Union[bool, Bool] = True) -> bool:
425427
"""Check if two potentially symbolic values can be equal"""
426428
if isinstance(expression, bool):
@@ -438,6 +440,7 @@ def can_be_true(self, constraints: ConstraintSet, expression: Union[bool, Bool]
438440
return self._is_sat()
439441

440442
# get-all-values min max minmax
443+
@lru_cache(maxsize=32)
441444
def get_all_values(self, constraints, expression, maxcnt=None, silent=False):
442445
"""Returns a list with all the possible values for the symbol x"""
443446
if not isinstance(expression, Expression):

manticore/core/worker.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ def run(self, *args):
132132
assert current_state is None
133133
# Handling Forking and terminating exceptions
134134
except Concretize as exc:
135-
logger.info("[%r] Performing %r", self.id, exc.message)
135+
logger.debug("[%r] Performing %r", self.id, exc.message)
136136
# The fork() method can decides which state to keep
137137
# exploring. For example when the fork results in a
138138
# single state it is better to just keep going.

manticore/ethereum/manticore.py

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -871,10 +871,10 @@ def _transaction(self, sort, caller, value=0, address=None, data=None, gas=21000
871871
caller = int(caller)
872872
# Defaults, call data is empty
873873
if data is None:
874-
data = bytearray(b"")
875-
if isinstance(data, (str, bytes)):
876-
data = bytearray(data)
877-
if not isinstance(data, (bytearray, Array)):
874+
data = b""
875+
if isinstance(data, str):
876+
data = bytes(data)
877+
if not isinstance(data, (bytes, Array)):
878878
raise TypeError("code bad type")
879879

880880
# Check types
@@ -1184,12 +1184,13 @@ def _on_unsound_symbolication(self, state, func, data, result):
11841184
if value is not None:
11851185
with self.locked_context("ethereum", dict) as ethereum_context:
11861186
global_known_pairs = ethereum_context.get(f"symbolic_func_conc_{name}", set())
1187-
global_known_pairs.add((data, value))
1188-
ethereum_context[f"symbolic_func_conc_{name}"] = global_known_pairs
1187+
if (data, value) not in global_known_pairs:
1188+
global_known_pairs.add((data, value))
1189+
ethereum_context[f"symbolic_func_conc_{name}"] = global_known_pairs
1190+
logger.info(f"Found a concrete {name} {data} -> {value}")
11891191
concrete_pairs = state.context.get(f"symbolic_func_conc_{name}", set())
11901192
concrete_pairs.add((data, value))
11911193
state.context[f"symbolic_func_conc_{name}"] = concrete_pairs
1192-
logger.info(f"Found a concrete {name} {data} -> {value}")
11931194
else:
11941195
# we can not calculate the concrete value lets use a fresh symbol
11951196
with self.locked_context("ethereum", dict) as ethereum_context:
@@ -1425,16 +1426,13 @@ def _terminate_state_callback(self, state, e):
14251426
# generate a testcase. FIXME This should be configurable as REVERT and
14261427
# THROW; it actually changes the balance and nonce? of some accounts
14271428

1428-
if tx.result in {"SELFDESTRUCT", "REVERT", "THROW", "TXERROR"}:
1429+
if tx.return_value == 0:
14291430
pass
1430-
elif tx.result in {"RETURN", "STOP"}:
1431+
else:
14311432
# if not a revert, we save the state for further transactions
14321433
with self.locked_context("ethereum.saved_states", list) as saved_states:
14331434
saved_states.append(state.id)
14341435

1435-
else:
1436-
logger.debug("Exception in state. Discarding it")
1437-
14381436
# Callbacks
14391437
def _did_evm_execute_instruction_callback(self, state, instruction, arguments, result):
14401438
""" INTERNAL USE """

manticore/ethereum/plugins.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import sys
22

33
from functools import reduce
4-
54
import re
65

76
from ..core.plugin import Plugin

manticore/platforms/evm.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2416,7 +2416,7 @@ def _open_transaction(self, sort, address, price, bytecode_or_data, caller, valu
24162416
)
24172417
if sort == "CREATE":
24182418
bytecode = bytecode_or_data
2419-
data = bytearray()
2419+
data = bytes()
24202420
else:
24212421
bytecode = self.get_code(address)
24222422
data = bytecode_or_data
@@ -2790,7 +2790,6 @@ def calculate_new_address(sender=None, nonce=None):
27902790
return new_address
27912791

27922792
def execute(self):
2793-
27942793
self._process_pending_transaction()
27952794
if self.current_vm is None:
27962795
raise TerminateState("Trying to execute an empty transaction", testcase=False)

0 commit comments

Comments
 (0)