def diff_behavior(ctxfn1: FunctionInfo, ctxfn2: FunctionInfo, options: AnalysisOptions) -> Union[str, List[BehaviorDiff]]: fn1, sig1 = ctxfn1.callable() fn2, sig2 = ctxfn2.callable() debug("Resolved signature:", sig1) all_diffs: List[BehaviorDiff] = [] half1, half2 = options.split_limits(0.5) with condition_parser(options.analysis_kind), Patched(): # We attempt both orderings of functions. This helps by: # (1) avoiding code path explosions in one of the functions # (2) using both signatures (in case they differ) all_diffs.extend(diff_behavior_with_signature(fn1, fn2, sig1, half1)) all_diffs.extend( diff.reverse() for diff in diff_behavior_with_signature(fn2, fn1, sig2, half2)) debug("diff candidates:", all_diffs) # greedily pick results: result_diffs = [] opcodeset1 = set(i.offset for i in dis.get_instructions(fn1.__code__)) opcodeset2 = set(i.offset for i in dis.get_instructions(fn2.__code__)) while all_diffs: scorer = diff_scorer(opcodeset1, opcodeset2) selection = max(all_diffs, key=scorer) (num_opcodes, _) = scorer(selection) debug("Considering input", selection.args, " num opcodes=", num_opcodes) if num_opcodes == 0: break all_diffs.remove(selection) result_diffs.append(selection) coverage1, coverage2 = selection.coverage1, selection.coverage2 if coverage1 is not None and coverage2 is not None: opcodeset1 -= coverage1.offsets_covered opcodeset2 -= coverage2.offsets_covered return result_diffs
def test_isfinite(self): space = SimpleStateSpace() with Patched(enabled=lambda: True), StateSpaceContext(space): x = SmtFloat('symfloat') self.assertTrue(math.isfinite(x)) self.assertTrue(math.isfinite(2.3)) self.assertFalse(math.isfinite(float('nan')))
def symbolic_run( self, fn: Callable[[TrackingStateSpace], object] ) -> Tuple[object, Optional[BaseException], TrackingStateSpace]: search_root = SinglePathNode(True) with Patched(enabled=lambda: True): for itr in range(1, 200): debug('iteration', itr) space = TrackingStateSpace(time.monotonic() + 10.0, 1.0, search_root=search_root) try: with StateSpaceContext(space): ret = (realize(fn(space)), None, space) space.check_deferred_assumptions() return ret except IgnoreAttempt as e: debug('ignore iteration attempt: ', str(e)) pass except BaseException as e: debug(traceback.format_exc()) return (None, e, space) top_analysis, space_exhausted = space.bubble_status( CallAnalysis()) if space_exhausted: return ( None, CrosshairInternal(f'exhausted after {itr} iterations'), space) return (None, CrosshairInternal( 'Unable to find a successful symbolic execution'), space)
def test_isfinite(self): space = SimpleStateSpace() with Patched(), StateSpaceContext(space): x = SymbolicFloat("symfloat") self.assertTrue(math.isfinite(x)) self.assertTrue(math.isfinite(2.3)) self.assertFalse(math.isfinite(float("nan")))
def symbolic_run( self, fn: Callable[[TrackingStateSpace], object] ) -> Tuple[object, Optional[BaseException]]: search_root = SinglePathNode(True) patched_builtins = Patched( {IdentityWrapper(builtins): builtin_patches()}, enabled=lambda: True) with patched_builtins: for itr in range(1, 200): debug('iteration', itr) space = TrackingStateSpace(time.time() + 10.0, 1.0, search_root=search_root) try: return (realize(fn(space)), None) except IgnoreAttempt as e: debug('ignore iteration attempt: ', str(e)) pass except BaseException as e: #traceback.print_exc() return (None, e) top_analysis, space_exhausted = space.bubble_status( CallAnalysis()) if space_exhausted: return ( None, CrosshairInternal(f'exhausted after {itr} iterations')) return (None, CrosshairInternal( 'Unable to find a successful symbolic execution'))
def path_cover(ctxfn: FunctionInfo, options: AnalysisOptions, coverage_type: CoverageType) -> List[PathSummary]: fn, sig = ctxfn.callable() search_root = SinglePathNode(True) condition_start = time.monotonic() paths: List[PathSummary] = [] for i in range(1, options.max_iterations): debug("Iteration ", i) itr_start = time.monotonic() if itr_start > condition_start + options.per_condition_timeout: debug( "Stopping due to --per_condition_timeout=", options.per_condition_timeout, ) break space = StateSpace( execution_deadline=itr_start + options.per_path_timeout, model_check_timeout=options.per_path_timeout / 2, search_root=search_root, ) with condition_parser(options.analysis_kind), Patched( ), COMPOSITE_TRACER, StateSpaceContext(space): summary = None try: summary = run_iteration(fn, sig, space) verification_status = VerificationStatus.CONFIRMED except UnexploredPath: verification_status = VerificationStatus.UNKNOWN debug("Verification status:", verification_status) top_analysis, exhausted = space.bubble_status( CallAnalysis(verification_status)) debug("Path tree stats", search_root.stats()) if summary: paths.append(summary) if exhausted: debug("Stopping due to code path exhaustion. (yay!)") break opcodes_found: Set[int] = set() selected: List[PathSummary] = [] while paths: next_best = max( paths, key=lambda p: len(p.coverage.offsets_covered - opcodes_found)) cur_offsets = next_best.coverage.offsets_covered if coverage_type == CoverageType.OPCODE: if len(cur_offsets - opcodes_found) == 0: break selected.append(next_best) opcodes_found |= cur_offsets paths = [p for p in paths if p is not next_best] return selected
def symbolic_run( self, fn: Callable[[StateSpace, Dict[str, object]], object], typed_args: Dict[str, type], ) -> Tuple[object, # return value Optional[Dict[str, object]], # arguments after execution Optional[BaseException], # exception thrown, if any StateSpace, ]: search_root = SinglePathNode(True) with COMPOSITE_TRACER, Patched(): for itr in range(1, 200): debug("iteration", itr) space = StateSpace(time.monotonic() + 10.0, 1.0, search_root=search_root) symbolic_args = {} try: with StateSpaceContext(space): symbolic_args = { name: proxy_for_type(typ, name) for name, typ in typed_args.items() } ret = fn(space, symbolic_args) ret = (deep_realize(ret), symbolic_args, None, space) space.check_deferred_assumptions() return ret except IgnoreAttempt as e: debug("ignore iteration attempt: ", str(e)) pass except BaseException as e: debug(traceback.format_exc()) return (None, symbolic_args, e, space) top_analysis, space_exhausted = space.bubble_status( CallAnalysis()) if space_exhausted: return ( None, symbolic_args, CrosshairInternal(f"exhausted after {itr} iterations"), space, ) return ( None, None, CrosshairInternal( "Unable to find a successful symbolic execution"), space, )
def run_trial( self, expr_str: str, arg_type_roots: Dict[str, Type], trial_desc: str ) -> TrialStatus: expr = expr_str.format(*arg_type_roots.keys()) typed_args = { name: gen_type(self.r, type_root) for name, type_root in arg_type_roots.items() } literal_args = { name: value_for_type(typ, self.r) for name, typ in typed_args.items() } def symbolic_checker( space: StateSpace, symbolic_args: Dict[str, object] ) -> object: for name in typed_args.keys(): literal, symbolic = literal_args[name], symbolic_args[name] if isinstance(literal, (set, frozenset, dict)): assert isinstance(symbolic, Sized) # We need not only equality, but equal ordering, because some operations # like pop() are order-dependent: if len(literal) != len(symbolic): raise IgnoreAttempt( f'symbolic "{name}" not equal to literal "{name}"' ) if isinstance(literal, Mapping): assert isinstance(symbolic, Mapping) literal, symbolic = list(literal.items()), list( symbolic.items() ) else: assert isinstance(symbolic, Iterable) literal, symbolic = list(literal), list(symbolic) if literal != symbolic: raise IgnoreAttempt( f'symbolic "{name}" not equal to literal "{name}"' ) return eval(expr, symbolic_args.copy()) with self.subTest( msg=f"Trial {trial_desc}: evaluating {expr} with {literal_args}" ): debug(f" ===== {expr} with {literal_args} ===== ") compiled = compile(expr, "<string>", "eval") postexec_literal_args = copy.deepcopy(literal_args) literal_ret, literal_exc = self.runexpr(expr, postexec_literal_args) ( symbolic_ret, postexec_symbolic_args, symbolic_exc, space, ) = self.symbolic_run(symbolic_checker, typed_args) if isinstance(symbolic_exc, CrosshairUnsupported): return TrialStatus.UNSUPPORTED with Patched(), StateSpaceContext(space), COMPOSITE_TRACER: # compare iterators as the values they produce: if isinstance(literal_ret, Iterable) and isinstance( symbolic_ret, Iterable ): literal_ret = list(literal_ret) symbolic_ret = list(symbolic_ret) rets_differ = bool(literal_ret != symbolic_ret) postexec_args_differ = bool( postexec_literal_args != postexec_symbolic_args ) if ( rets_differ or postexec_args_differ or type(literal_exc) != type(symbolic_exc) ): debug( f" ***** BEGIN FAILURE FOR {expr} WITH {literal_args} ***** " ) debug(f" ***** Expected: {literal_ret} / {literal_exc}") debug(f" ***** {postexec_literal_args}") debug(f" ***** Symbolic result: {symbolic_ret} / {symbolic_exc}") debug(f" ***** {postexec_symbolic_args}") debug(f" ***** END FAILURE FOR {expr} ***** ") self.assertEqual( (literal_ret, literal_exc), (symbolic_ret, symbolic_exc) ) debug(" OK ret= ", literal_ret, symbolic_ret) debug(" OK exc= ", literal_exc, symbolic_exc) return TrialStatus.NORMAL