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
Beispiel #2
0
 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)
Beispiel #4
0
 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")))
Beispiel #5
0
 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'))
Beispiel #6
0
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,
     )
Beispiel #8
0
    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