Skip to content

Commit

Permalink
Merge branch 'master' into dev-expressions
Browse files Browse the repository at this point in the history
* master:
  Syscall specific hooks (#2389)
  TUI Support Infrastructure (#1620)
  • Loading branch information
ekilmer committed Feb 18, 2021
2 parents 2c6cffa + d85a9a2 commit 38a261c
Show file tree
Hide file tree
Showing 18 changed files with 1,102 additions and 72 deletions.
47 changes: 40 additions & 7 deletions manticore/core/manticore.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import itertools
import logging
import sys
import time
import typing
import random
import weakref
Expand All @@ -21,11 +22,18 @@
from ..utils.deprecated import deprecated
from ..utils.enums import StateLists, MProcessingType
from ..utils.event import Eventful
from ..utils.helpers import PickleSerializer, pretty_print_state_descriptors
from ..utils.helpers import PickleSerializer, pretty_print_state_descriptors, deque
from ..utils.log import set_verbosity
from ..utils.nointerrupt import WithKeyboardInterruptAs
from .workspace import Workspace, Testcase
from .worker import WorkerSingle, WorkerThread, WorkerProcess, DaemonThread
from .worker import (
WorkerSingle,
WorkerThread,
WorkerProcess,
DaemonThread,
LogCaptureWorker,
state_monitor,
)

from multiprocessing.managers import SyncManager
import threading
Expand Down Expand Up @@ -88,6 +96,7 @@ def wait_for(self, condition, *args, **kwargs):
self._terminated_states = []
self._busy_states = []
self._killed_states = []
self._log_queue = deque(maxlen=5000)
self._shared_context = {}

def _manticore_threading(self):
Expand All @@ -99,6 +108,7 @@ def _manticore_threading(self):
self._terminated_states = []
self._busy_states = []
self._killed_states = []
self._log_queue = deque(maxlen=5000)
self._shared_context = {}

def _manticore_multiprocessing(self):
Expand All @@ -120,6 +130,9 @@ def raise_signal():
self._terminated_states = self._manager.list()
self._busy_states = self._manager.list()
self._killed_states = self._manager.list()
# The multiprocessing queue is much slower than the deque when it gets full, so we
# triple the size in order to prevent that from happening.
self._log_queue = self._manager.Queue(15000)
self._shared_context = self._manager.dict()
self._context_value_types = {list: self._manager.list, dict: self._manager.dict}

Expand Down Expand Up @@ -370,8 +383,10 @@ def __init__(
# Workers will use manticore __dict__ So lets spawn them last
self._workers = [self._worker_type(id=i, manticore=self) for i in range(consts.procs)]

# We won't create the daemons until .run() is called
self._daemon_threads: typing.List[DaemonThread] = []
# Create log capture worker. We won't create the rest of the daemons until .run() is called
self._daemon_threads: typing.Dict[int, DaemonThread] = {
-1: LogCaptureWorker(id=-1, manticore=self)
}
self._daemon_callbacks: typing.List[typing.Callable] = []

self._snapshot = None
Expand Down Expand Up @@ -1102,21 +1117,27 @@ def run(self):
# User subscription to events is disabled from now on
self.subscribe = None

self.register_daemon(state_monitor)
self._daemon_threads[-1].start() # Start log capture worker

# Passing generators to callbacks is a bit hairy because the first callback would drain it if we didn't
# clone the iterator in event.py. We're preserving the old API here, but it's something to avoid in the future.
self._publish("will_run", self.ready_states)
self._running.value = True

# start all the workers!
for w in self._workers:
w.start()

# Create each daemon thread and pass it `self`
if not self._daemon_threads: # Don't recreate the threads if we call run multiple times
for i, cb in enumerate(self._daemon_callbacks):
for i, cb in enumerate(self._daemon_callbacks):
if (
i not in self._daemon_threads
): # Don't recreate the threads if we call run multiple times
dt = DaemonThread(
id=i, manticore=self
) # Potentially duplicated ids with workers. Don't mix!
self._daemon_threads.append(dt)
self._daemon_threads[dt.id] = dt
dt.start(cb)

# Main process. Lets just wait and capture CTRL+C at main
Expand Down Expand Up @@ -1173,6 +1194,17 @@ def finalize(self):
self.generate_testcase(state)
self.remove_all()

def wait_for_log_purge(self):
"""
If a client has accessed the log server, and there are still buffered logs,
waits up to 2 seconds for the client to retrieve the logs.
"""
if self._daemon_threads[-1].activated:
for _ in range(8):
if self._log_queue.empty():
break
time.sleep(0.25)

############################################################################
############################################################################
############################################################################
Expand All @@ -1188,6 +1220,7 @@ def save_run_data(self):
config.save(f)

logger.info("Results in %s", self._output.store.uri)
self.wait_for_log_purge()

def introspect(self) -> typing.Dict[int, StateDescriptor]:
"""
Expand Down
18 changes: 18 additions & 0 deletions manticore/core/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,24 @@ def get_state_descriptors(self) -> typing.Dict[int, StateDescriptor]:
out = context.copy() # TODO: is this necessary to break out of the lock?
return out

def did_kill_state_callback(self, state, ex: Exception):
"""
Capture other state-killing exceptions so we can get the corresponding message
:param state: State that was killed
:param ex: The exception w/ the termination message
"""
state_id = state.id
with self.locked_context("manticore_state", dict) as context:
if state_id not in context:
logger.warning(
"Caught killing of state %s, but failed to capture its initialization",
state_id,
)
context.setdefault(state_id, StateDescriptor(state_id=state_id)).termination_msg = repr(
ex
)

@property
def unique_name(self) -> str:
return IntrospectionAPIPlugin.NAME
31 changes: 31 additions & 0 deletions manticore/core/state.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
syntax = "proto3";

package mserialize;

message LogMessage{
string content = 1;
}

message State{

enum StateType{
READY = 0;
BUSY = 1;
KILLED = 2;
TERMINATED = 3;
}

int32 id = 2; // state ID
StateType type = 3; // Type of state
string reason = 4; // Reason for execution stopping
int32 num_executing = 5; // number of executing instructions
int32 wait_time = 6;
}

message StateList{
repeated State states = 7;
}

message MessageList{
repeated LogMessage messages = 8;
}
Loading

0 comments on commit 38a261c

Please sign in to comment.