def find_model_value_for_function(self, expr: z3.ExprRef) -> object: # TODO: this need to go into a tree node that returns UNKNOWN or worse # (because it just returns one example function; it's not covering the space) if self.solver.check() != z3.sat: raise CrosshairInternal('model unexpectedly became unsatisfiable') finterp = self.solver.model()[expr] if self.solver.check() != z3.sat: raise CrosshairInternal( 'could not confirm model satisfiability after fixing value') return finterp
def enabled_enforcement(self): prev = self.fns_enforcing assert prev is None self.fns_enforcing = set() if not COMPOSITE_TRACER.set_enabled(self, True): raise CrosshairInternal("Cannot enable enforcement") try: yield None finally: self.fns_enforcing = prev if not COMPOSITE_TRACER.set_enabled(self, prev): raise CrosshairInternal( "Tracing handler stack is inconsistent")
def pool_worker_main(item: WorkItemInput, output: multiprocessing.queues.Queue) -> None: try: # TODO figure out a more reliable way to suppress this. Redirect output? # Ignore ctrl-c in workers to reduce noisy tracebacks (the parent will kill us): signal.signal(signal.SIGINT, signal.SIG_IGN) if hasattr(os, 'nice'): # analysis should run at a low priority os.nice(10) set_debug(False) filename, options, deadline = item stats: Counter[str] = Counter() options.stats = stats _, module_name = extract_module_from_file(filename) try: module = load_by_qualname(module_name) except NotFound: return except ErrorDuringImport as e: orig, frame = e.args message = AnalysisMessage(MessageType.IMPORT_ERR, str(orig), frame.filename, frame.lineno, 0, '') output.put((filename, stats, [message])) debug(f'Not analyzing "{filename}" because import failed: {e}') return messages = analyze_any(module, options) output.put((filename, stats, messages)) except BaseException as e: raise CrosshairInternal( 'Worker failed while analyzing ' + filename) from e
def register_type(typ: Type, creator: Union[Type, Callable]) -> None: assert typ is origin_of(typ), \ f'Only origin types may be registered, not "{typ}": try "{origin_of(typ)}" instead.' if typ in _SIMPLE_PROXIES: raise CrosshairInternal(f'Duplicate type "{typ}" registered') _SIMPLE_PROXIES[typ] = creator
def register_patch(entity: object, patch_value: Callable, attr_name: Optional[str] = None): if attr_name in _PATCH_REGISTRATIONS[IdentityWrapper(entity)]: raise CrosshairInternal(f'Doubly registered patch: {object} . {attr_name}') if attr_name is None: attr_name = getattr(patch_value, '__name__', None) assert attr_name is not None _PATCH_REGISTRATIONS[IdentityWrapper(entity)][attr_name] = patch_value
def find_model_value_for_function(self, expr: z3.ExprRef) -> object: # TODO: this need to go into a tree node that returns UNKNOWN or worse # (because it just returns one example function; it's not covering the space) if not solver_is_sat(self.solver): raise CrosshairInternal( 'model unexpectedly became unsatisfiable') return self.solver.model()[expr]
def __init__(self, rand: random.Random, expr: z3.ExprRef, solver: z3.Solver): if self.condition_value is None: if not solver_is_sat(solver): debug('bad solver', solver.sexpr()) raise CrosshairInternal('unexpected un sat') self.condition_value = solver.model().evaluate(expr, model_completion=True) WorstResultNode.__init__(self, rand, expr == self.condition_value, solver)
def __init__(self, rand: random.Random, expr: z3.ExprRef, solver: z3.Solver): if not solver_is_sat(solver): debug("Solver unexpectedly unsat; solver state:", solver.sexpr()) raise CrosshairInternal("Unexpected unsat from solver") self.condition_value = solver.model().evaluate(expr, model_completion=True) self._stats_key = f"realize_{expr}" if z3.is_const(expr) else None WorstResultNode.__init__(self, rand, expr == self.condition_value, solver)
def find_model_value_for_function(self, expr: z3.ExprRef) -> object: if not solver_is_sat(self.solver): raise CrosshairInternal("model unexpectedly became unsatisfiable") # TODO: this need to go into a tree node that returns UNKNOWN or worse # (because it just returns one example function; it's not covering the space) # TODO: note this is also unsound - after completion, the solver isn't # bound to the returned interpretation. (but don't know how to add the # right constraints) Maybe just use arrays instead. return self.solver.model()[expr]
def __init__(self, rand: random.Random, expr: z3.ExprRef, solver: z3.Solver): RandomizedBinaryPathNode.__init__(self, rand) notexpr = z3.Not(expr) could_be_true = solver_is_sat(solver, expr) could_be_false = solver_is_sat(solver, notexpr) if (not could_be_true) and (not could_be_false): debug(' *** Reached impossible code path *** ') debug('Current solver state:\n', str(solver)) raise CrosshairInternal('Reached impossible code path') elif not could_be_true: self.forced_path = False elif not could_be_false: self.forced_path = True
def __init__(self, rand: random.Random, expr: z3.ExprRef, solver: z3.Solver): super().__init__(rand) notexpr = z3.Not(expr) could_be_true = solver_is_sat(solver, expr) could_be_false = solver_is_sat(solver, notexpr) if (not could_be_true) and (not could_be_false): debug(" *** Reached impossible code path *** ") debug("Current solver state:\n", str(solver)) raise CrosshairInternal("Reached impossible code path") elif not could_be_true: self.forced_path = False elif not could_be_false: self.forced_path = True self._expr = expr # note: this is only used for debugging
def pool_worker_main(item: WorkItemInput, output: multiprocessing.queues.Queue) -> None: try: # TODO figure out a more reliable way to suppress this. Redirect output? # Ignore ctrl-c in workers to reduce noisy tracebacks (the parent will kill us): signal.signal(signal.SIGINT, signal.SIG_IGN) if hasattr(os, 'nice'): # analysis should run at a low priority os.nice(10) set_debug(False) (stats, messages) = pool_worker_process_item(item) filename = item[0] output.put((filename, stats, messages)) except BaseException as e: raise CrosshairInternal('Worker failed while analyzing ' + filename) from e
def choose_possible(self, expr: z3.ExprRef, favor_true=False) -> bool: with self.framework(): if time.time() > self.execution_deadline: debug('Path execution timeout after making ', len(self.choices_made), ' choices.') raise PathTimeout if self.solver.check() != z3.sat: debug('bad solver', self.solver) raise CrosshairInternal('unexpected un sat') notexpr = z3.Not(expr) if self.search_position.is_stem(): self.search_position = self.search_position.grow_into( WorstResultNode(self._random, expr, self.solver)) self.search_position = self.search_position.simplify() node = self.search_position # NOTE: format_stack() is more human readable, but it pulls source file contents, # so it is (1) slow, and (2) unstable when source code changes while we are checking. statedesc = '\n'.join(map(str, traceback.extract_stack())) assert isinstance(node, SearchTreeNode) if node.statehash is None: node.statehash = statedesc else: if node.statehash != statedesc: debug(self.choices_made) debug(' *** Begin Not Deterministic Debug *** ') debug(' First state: ', len(node.statehash)) debug(node.statehash) debug(' Last state: ', len(statedesc)) debug(statedesc) debug(' Stack Diff: ') import difflib debug('\n'.join( difflib.context_diff(node.statehash.split('\n'), statedesc.split('\n')))) debug(' *** End Not Deterministic Debug *** ') raise NotDeterministic() choose_true, stem = node.choose(favor_true=favor_true) assert isinstance(self.search_position, SearchTreeNode) self.choices_made.append(self.search_position) self.search_position = stem expr = expr if choose_true else notexpr #debug('CHOOSE', expr) self.add(expr) return choose_true
def analyze_any(entity: object, options: AnalysisOptions) -> List[AnalysisMessage]: if inspect.isclass(entity): return analyze_class(cast(Type, entity), options) elif inspect.isfunction(entity): self_class: Optional[type] = None fn = cast(Callable, entity) if fn.__name__ != fn.__qualname__: self_thing = walk_qualname(sys.modules[fn.__module__], fn.__qualname__.split('.')[-2]) assert isinstance(self_thing, type) self_class = self_thing return analyze_function(fn, options, self_type=self_class) elif inspect.ismodule(entity): return analyze_module(cast(types.ModuleType, entity), options) else: raise CrosshairInternal('Entity type not analyzable: ' + str(type(entity)))
def __init__(self, rand: random.Random, expr: z3.ExprRef, solver: z3.Solver): RandomizedBinaryPathNode.__init__(self, rand) notexpr = z3.Not(expr) true_sat, false_sat = solver.check(expr), solver.check(notexpr) if true_sat == z3.unknown or false_sat == z3.unknown: raise UnknownSatisfiability could_be_true = (true_sat == z3.sat) could_be_false = (false_sat == z3.sat) if (not could_be_true) and (not could_be_false): debug(' *** Reached impossible code path *** ', true_sat, false_sat, expr) debug('Current solver state:\n', str(solver)) raise CrosshairInternal('Reached impossible code path') elif not could_be_true: self.forced_path = False elif not could_be_false: self.forced_path = True
def pool_worker_main(item: WorkItemInput, output: multiprocessing.queues.Queue) -> None: try: # TODO figure out a more reliable way to suppress this. Redirect output? # Ignore ctrl-c in workers to reduce noisy tracebacks (the parent will kill us): signal.signal(signal.SIGINT, signal.SIG_IGN) if hasattr(os, 'nice'): # <- is this the right way to detect availability? os.nice(10) # analysis should run at a low priority set_debug(False) member, options, deadline = item stats: Counter[str] = Counter() options.stats = stats try: fn = member.get_member() except NotFound: return messages = analyze_any(fn, options) output.put((member, stats, messages)) except BaseException as e: raise CrosshairInternal('Worker failed while analyzing ' + member.qual_name) from e
def choose_possible(self, expr: z3.ExprRef, favor_true=False) -> bool: with self.framework(): notexpr = z3.Not(expr) true_sat, false_sat = self.check(expr), self.check(notexpr) could_be_true = (true_sat == z3.sat) could_be_false = (false_sat == z3.sat) if (not could_be_true) and (not could_be_false): raise CrosshairInternal('Reached impossible code path') else: log, idx = self.execution_log, self.log_index if idx >= len(log): if idx == len(log): debug('Precise path replay unsuccessful.') return False debug('decide_true = ', self.execution_log[self.log_index]) decide_true = (self.execution_log[self.log_index] == '1') self.log_index += 1 expr = expr if decide_true else notexpr debug('REPLAY CHOICE', expr) self.add(expr) if not self.solver.check(): debug('Precise path replay unsuccessful.') return decide_true
def uniq(self): self.next_uniq += 1 if self.next_uniq >= 1000000: raise CrosshairInternal('Exhausted var space') return '{:06d}'.format(self.next_uniq)