Esempio n. 1
0
    def minimize_corpus(self, target_path, arguments, output_dir, input_dirs,
                        max_time):
        """Optional (but recommended): run corpus minimization.

    Args:
      target_path: Path to the target.
      arguments: Additional arguments needed for corpus minimization.
      output_dir: Output directory to place minimized corpus.
      input_dirs: Input corpora.
      max_time: Maximum allowed time for the minimization.

    Returns:
      A Result object.
    """
        runner = libfuzzer.get_runner(target_path)
        launcher.set_sanitizer_options(target_path)
        merge_tmp_dir = self._create_temp_corpus_dir('merge-workdir')

        merge_result = runner.merge([output_dir] + input_dirs,
                                    merge_timeout=max_time,
                                    tmp_dir=merge_tmp_dir,
                                    additional_args=arguments)

        if merge_result.timed_out:
            raise MergeError('Merging new testcases timed out')

        if merge_result.return_code != 0:
            raise MergeError('Merging new testcases failed')

        # TODO(ochang): Get crashes found during merge.
        return engine.Result(merge_result.output, merge_result.command, [], {},
                             merge_result.time_executed)
  def test_basic(self):
    """Test basic fuzzing session."""
    session = fuzz_task.FuzzingSession('libFuzzer', 'libfuzzer_asan_test', 60)
    session.testcase_directory = os.environ['FUZZ_INPUTS']
    session.data_directory = '/data_dir'

    os.environ['FUZZ_TARGET'] = 'test_target'
    os.environ['APP_REVISION'] = '1'

    expected_crashes = [engine.Crash('/input', 'stack', ['args'], 1.0)]

    engine_impl = mock.Mock()
    engine_impl.name = 'libFuzzer'
    engine_impl.prepare.return_value = engine.FuzzOptions(
        '/corpus', ['arg'], ['strategy_1', 'strategy_2'])
    engine_impl.fuzz.return_value = engine.Result(
        'logs', ['cmd'], expected_crashes, {'stat': 1}, 42.0)

    crashes, fuzzer_metadata = session.do_engine_fuzzing(engine_impl)
    self.assertDictEqual({
        'issue_components': 'component1,component2',
        'issue_labels': 'label1,label2',
        'issue_owners': '*****@*****.**',
    }, fuzzer_metadata)

    log_time = datetime.datetime(1970, 1, 1, 0, 0)
    self.mock.upload_log.assert_called_with(
        'Component revisions (build r1):\n'
        'component: rev\n\n'
        'Return code: 1\n\n'
        'Command: cmd\nBot: None\nTime ran: 42.0\n\n'
        'logs\n'
        'cf::fuzzing_strategies: strategy_1,strategy_2', log_time)
    self.mock.upload_testcase.assert_called_with('/input', log_time)

    self.assertEqual(1, len(crashes))
    self.assertEqual('/input', crashes[0].file_path)
    self.assertEqual(1, crashes[0].return_code)
    self.assertEqual('stack', crashes[0].unsymbolized_crash_stacktrace)
    self.assertEqual(1.0, crashes[0].crash_time)
    self.assertListEqual(['test_target', 'args'], crashes[0].arguments)
    upload_args = self.mock.upload_stats.call_args[0][0]
    testcase_run = upload_args[0]
    self.assertDictEqual({
        'build_revision': 1,
        'command': ['cmd'],
        'fuzzer': u'libFuzzer_test_target',
        'job': 'libfuzzer_asan_test',
        'kind': 'TestcaseRun',
        'stat': 1,
        'timestamp': 0.0,
    }, testcase_run.data)
Esempio n. 3
0
    def fuzz(self, target_path, options, reproducers_dir, max_time):
        """Run a fuzz session.

    Args:
      target_path: Path to the target.
      options: The FuzzOptions object returned by prepare().
      reproducers_dir: The directory to put reproducers in when crashes
          are found.
      max_time: Maximum allowed time for the fuzzing to run.

    Returns:
      A Result object.
    """
        profiler.start_if_needed('libfuzzer_fuzz')
        runner = libfuzzer.get_runner(target_path)
        launcher.set_sanitizer_options(target_path)

        artifact_prefix = self._artifact_prefix(
            os.path.abspath(reproducers_dir))

        # Directory to place new units.
        new_corpus_dir = self._create_temp_corpus_dir('new')

        corpus_directories = [new_corpus_dir] + options.fuzz_corpus_dirs
        fuzz_timeout = launcher.get_fuzz_timeout(options.is_mutations_run,
                                                 total_timeout=max_time)
        fuzz_result = runner.fuzz(corpus_directories,
                                  fuzz_timeout=fuzz_timeout,
                                  additional_args=options.arguments +
                                  [artifact_prefix],
                                  extra_env=options.extra_env)

        log_lines = fuzz_result.output.splitlines()
        # Output can be large, so save some memory by removing reference to the
        # original output which is no longer needed.
        fuzz_result.output = None

        # Check if we crashed, and get the crash testcase path.
        crash_testcase_file_path = None
        for line in log_lines:
            match = re.match(launcher.CRASH_TESTCASE_REGEX, line)
            if match:
                crash_testcase_file_path = match.group(1)
                break

        # Parse stats information based on libFuzzer output.
        parsed_stats = launcher.parse_log_stats(log_lines)

        # Extend parsed stats by additional performance features.
        parsed_stats.update(
            stats.parse_performance_features(log_lines,
                                             options.strategies,
                                             options.arguments,
                                             include_strategies=False))

        # Set some initial stat overrides.
        timeout_limit = fuzzer_utils.extract_argument(options.arguments,
                                                      constants.TIMEOUT_FLAG,
                                                      remove=False)

        expected_duration = runner.get_max_total_time(fuzz_timeout)
        actual_duration = int(fuzz_result.time_executed)
        fuzzing_time_percent = 100 * actual_duration / float(expected_duration)
        parsed_stats.update({
            'timeout_limit': int(timeout_limit),
            'expected_duration': expected_duration,
            'actual_duration': actual_duration,
            'fuzzing_time_percent': fuzzing_time_percent,
        })

        # Remove fuzzing arguments before merge and dictionary analysis step.
        arguments = options.arguments[:]
        launcher.remove_fuzzing_arguments(arguments)

        self._merge_new_units(target_path, options.corpus_dir, new_corpus_dir,
                              options.fuzz_corpus_dirs, arguments,
                              parsed_stats)

        # Add custom crash state based on fuzzer name (if needed).
        project_qualified_fuzzer_name = (
            data_types.fuzz_target_project_qualified_name(
                utils.current_project(), os.path.basename(target_path)))
        launcher.add_custom_crash_state_if_needed(
            project_qualified_fuzzer_name, log_lines, parsed_stats)

        fuzz_logs = '\n'.join(log_lines)
        crashes = []
        if crash_testcase_file_path:
            # Write the new testcase.
            # Copy crash testcase contents into the main testcase path.
            crashes.append(
                engine.Crash(crash_testcase_file_path, fuzz_logs, arguments,
                             actual_duration))

        launcher.analyze_and_update_recommended_dictionary(
            runner, project_qualified_fuzzer_name, log_lines,
            options.corpus_dir, arguments)

        return engine.Result(fuzz_logs, fuzz_result.command, crashes,
                             parsed_stats, fuzz_result.time_executed)