예제 #1
0
    def config_server(self, iteration_timeout):
        assert self.server is None
        log.debug("starting sapphire server")
        # have client error pages (code 4XX) call window.close() after a few seconds
        sapphire.Sapphire.CLOSE_CLIENT_ERROR = 1
        # launch http server used to serve test cases
        self.server = sapphire.Sapphire(timeout=iteration_timeout)
        # add include paths to server
        for url_path, target_path in self.iomanager.server_map.includes:
            self.server.add_include(url_path, target_path)
        # add dynamic responses to the server
        for dyn_rsp in self.iomanager.server_map.dynamic_responses:
            self.server.add_dynamic_response(dyn_rsp["url"],
                                             dyn_rsp["callback"],
                                             mime_type=dyn_rsp["mime"])

        def _dyn_resp_close():
            self.target.close()
            return b"<h1>Close Browser</h1>"

        self.server.add_dynamic_response("/close_browser",
                                         _dyn_resp_close,
                                         mime_type="text/html")
예제 #2
0
    def _run(self, testcase, temp_prefix):
        """Run a single iteration against the target and determine if it is interesting. This is the
        low-level iteration function used by `interesting`.

        Args:
            testcase (TestCase): The testcase to serve
            temp_prefix (str): A unique prefix for any files written during this iteration.

        Returns:
            Report: Report from reduced testcase if still interesting else None.
        """
        interesting = None

        # if target is closed and server is alive, we should restart it or else the first request
        #   against /first_test will 404
        if self._target.closed and self._server is not None:
            self._server.close()
            self._server = None
        self._server_map.dynamic.clear()
        self._server_map.redirect.clear()

        # launch sapphire if needed
        if self._server is None:
            # have client error pages (code 4XX) call window.close() after a few seconds
            self._server = sapphire.Sapphire(auto_close=2)

            if not self._no_harness:
                harness = os.path.join(os.path.dirname(__file__), '..', 'common', 'harness.html')
                with open(harness, 'rb') as harness_fp:
                    harness = harness_fp.read()
                self._server_map.set_dynamic_response("grz_harness", lambda: harness, mime_type="text/html")
            self._server_map.set_redirect("grz_current_test", str(self.landing_page), required=False)

        runner = Runner(self._server, self._target, self._idle_threshold, self._idle_timeout)
        if self._no_harness:
            self._server.timeout = self._iter_timeout
        else:
            # wait a few extra seconds to avoid races between the harness & sapphire timing out
            self._server.timeout = self._iter_timeout + 10

        # (re)launch Target
        if self._target.closed:
            if self._no_harness:
                location = runner.location(
                    "/grz_current_test",
                    self._server.port)
            else:
                location = runner.location(
                    "/grz_harness",
                    self._server.port,
                    close_after=self._target.rl_reset,
                    forced_close=self._target.forced_close,
                    timeout=self._iter_timeout)
            # Try to launch the browser, retry 4 times at most
            runner.launch(location, env_mod=self._env_mod, max_retries=4, retry_delay=15)
            self._target.step()

        if not self._no_harness:
            def _dyn_resp_close():  # pragma: no cover
                if self.target.monitor.is_healthy():
                    # delay to help catch window close/shutdown related crashes
                    time.sleep(0.1)
                    self.target.close()
                return b"<h1>Close Browser</h1>"
            self._server_map.set_dynamic_response("grz_close_browser", _dyn_resp_close, mime_type="text/html")
            self._server_map.set_redirect("grz_next_test", str(self.landing_page), required=True)

        # run test case
        result = runner.run(self._ignore, self._server_map, testcase, wait_for_callback=self._no_harness)
        testcase.duration = result.duration

        # handle failure if detected
        if result.status == RunResult.FAILED:
            self._target.close()
            testcase.purge_optional(result.served)

            # save logs
            result_logs = temp_prefix + "_logs"
            if not os.path.exists(result_logs):
                os.mkdir(result_logs)
            self._target.save_logs(result_logs)

            # create report
            report = Report(result_logs, self._target.binary)

            short_sig = report.crash_info.createShortSignature()
            if short_sig == "No crash detected":
                # XXX: need to change this to support reducing timeouts?
                LOG.info("Uninteresting: no crash detected")
            elif self._orig_sig is None or self._orig_sig.matches(report.crash_info):
                interesting = report
                LOG.info("Interesting: %s", short_sig)
                if self._orig_sig is None and not self._any_crash:
                    self._orig_sig = report.crash_signature
            else:
                LOG.info("Uninteresting: different signature: %s", short_sig)
                self.on_other_crash_found(testcase, report)

        elif result.status == RunResult.IGNORED:
            LOG.info("Uninteresting: ignored")
            self._target.close()

        else:
            LOG.info("Uninteresting: no failure detected")

        # trigger relaunch by closing the browser if needed
        self._target.check_relaunch()

        return interesting
예제 #3
0
파일: interesting.py 프로젝트: La0/grizzly
    def _run(self, testcase, temp_prefix):
        """Run a single iteration against the target and determine if it is interesting. This is the
        low-level iteration function used by `interesting`.

        Args:
            testcase (TestCase): The testcase to serve
            temp_prefix (str): A unique prefix for any files written during this iteration.

        Returns:
            bool: True if reduced testcase is still interesting.
        """
        result = False

        # if target is closed and server is alive, we should restart it or else the first request
        #   against /first_test will 404
        if self.target.closed and self.server is not None:
            self.server.close()
            self.server = None

        # launch sapphire if needed
        if self.server is None:
            # have client error pages (code 4XX) call window.close() after a few seconds
            sapphire.Sapphire.CLOSE_CLIENT_ERROR = 2
            self.server = sapphire.Sapphire()

            if not self.no_harness:
                harness = os.path.join(os.path.dirname(__file__), '..',
                                       'common', 'harness.html')
                with open(harness, 'rb') as harness_fp:
                    harness = harness_fp.read()

                def _dyn_resp_close():
                    self.target.close()
                    return b"<h1>Close Browser</h1>"

                self.server.add_dynamic_response("/close_browser",
                                                 _dyn_resp_close,
                                                 mime_type="text/html")
                self.server.add_dynamic_response("/harness",
                                                 lambda: harness,
                                                 mime_type="text/html")
                self.server.set_redirect("/first_test",
                                         str(self.landing_page),
                                         required=True)

        if self.no_harness:
            self.server.timeout = self.iter_timeout
        else:
            # wait a few extra seconds to avoid races between the harness & sapphire timing out
            self.server.timeout = self.iter_timeout + 10

        # (re)launch Target
        if self.target.closed:
            # Try to launch the browser at most, 4 times
            for retries in reversed(range(4)):
                try:
                    self.target.launch(self.location, env_mod=self.env_mod)
                    break
                except ffpuppet.LaunchError as exc:
                    if retries:
                        LOG.warning(str(exc))
                        time.sleep(15)
                    else:
                        raise
            self.target.step()

        try:
            idle_timeout_event = threading.Event()
            iteration_done_event = threading.Event()
            if self.idle_poll:
                monitor_launched = threading.Event()
                poll = threading.Thread(target=self.monitor_process,
                                        args=(iteration_done_event,
                                              idle_timeout_event,
                                              monitor_launched))
                poll.start()
                assert monitor_launched.wait(
                    30), "Failed to launch monitoring thread"

            def keep_waiting():
                return self.target.monitor.is_healthy(
                ) and not idle_timeout_event.is_set()

            if not self.no_harness:
                self.server.set_redirect("/next_test",
                                         str(self.landing_page),
                                         required=True)

            # serve the testcase
            server_status, files_served = self.server.serve_testcase(
                testcase, continue_cb=keep_waiting, forever=self.no_harness)

            # attempt to detect a failure
            failure_detected = self.target.detect_failure(
                self.ignore, server_status == sapphire.SERVED_TIMEOUT)

            # handle failure if detected
            if failure_detected == Target.RESULT_FAILURE:
                self.target.close()
                testcase.purge_optional(files_served)

                # save logs
                result_logs = temp_prefix + "_logs"
                if not os.path.exists(result_logs):
                    os.mkdir(result_logs)
                self.target.save_logs(result_logs, meta=True)

                # create a CrashInfo
                crash = FuzzManagerReporter.create_crash_info(
                    Report.from_path(result_logs), self.target.binary)

                short_sig = crash.createShortSignature()
                if short_sig == "No crash detected":
                    # XXX: need to change this to support reducing timeouts?
                    LOG.info("Uninteresting: no crash detected")
                elif self.orig_sig is None or self.orig_sig.matches(crash):
                    result = True
                    LOG.info("Interesting: %s", short_sig)
                    if self.orig_sig is None and not self.any_crash:
                        max_frames = FuzzManagerReporter.signature_max_frames(
                            crash, 5)
                        self.orig_sig = crash.createCrashSignature(
                            maxFrames=max_frames)
                else:
                    LOG.info("Uninteresting: different signature: %s",
                             short_sig)
                    if self.alt_crash_cb is not None:
                        self.alt_crash_cb(testcase, temp_prefix)  # pylint: disable=not-callable

            elif failure_detected == Target.RESULT_IGNORED:
                LOG.info("Uninteresting: ignored")
                self.target.close()

                # save logs
                result_logs = temp_prefix + "_logs"
                os.mkdir(result_logs)
                self.target.save_logs(result_logs, meta=True)

            else:
                LOG.info("Uninteresting: no failure detected")

            # trigger relaunch by closing the browser if needed
            self.target.check_relaunch()

        finally:
            iteration_done_event.set()
            if self.idle_poll:
                poll.join()

        return result