Пример #1
0
    def fuzz(self,
             fuzz_timeout,
             additional_args,
             unused_additional_args=None,
             unused_extra_env=None):
        """This is where actual syzkaller fuzzing is done."""
        additional_args = copy.copy(additional_args)
        fuzz_result = self.run_and_wait(additional_args, timeout=fuzz_timeout)

        log_lines = utils.decode_to_unicode(fuzz_result.output).splitlines()
        fuzz_result.output = None
        crash_testcase_file_path = self.get_testcase_path(log_lines)

        #TODO(hzawawy): remove once syzkaller code is completed.
        if not crash_testcase_file_path and fuzz_result.return_code:
            crash_testcase_file_path = self._create_empty_testcase_file()

        fuzz_logs = '\n'.join(log_lines)

        # TODO(hzawawy): Parse stats information and add them to FuzzResult
        parsed_stats = []

        crashes = []
        if crash_testcase_file_path:
            #TODO(hzawawy): add repro arguments
            reproduce_arguments = []
            actual_duration = int(fuzz_result.time_executed)
            # Write the new testcase.
            # Copy crash testcase contents into the main testcase path.
            crashes.append(
                engine.Crash(crash_testcase_file_path, fuzz_logs,
                             reproduce_arguments, actual_duration))

        return engine.FuzzResult(fuzz_logs, fuzz_result.command, crashes,
                                 parsed_stats, fuzz_result.time_executed)
Пример #2
0
def engine_fuzz(engine_impl, target_name, sync_corpus_directory,
                testcase_directory):
  """Run engine fuzzer on untrusted worker."""
  request = untrusted_runner_pb2.EngineFuzzRequest(
      engine=engine_impl.name,
      target_name=target_name,
      sync_corpus_directory=file_host.rebase_to_worker_root(
          sync_corpus_directory),
      testcase_directory=file_host.rebase_to_worker_root(testcase_directory))

  response = host.stub().EngineFuzz(request)
  crashes = [
      engine.Crash(
          input_path=file_host.rebase_to_host_root(crash.input_path),
          stacktrace=crash.stacktrace,
          reproduce_args=crash.reproduce_args,
          crash_time=crash.crash_time) for crash in response.crashes
  ]

  unpacked_stats = _unpack_values(response.stats)
  unpacked_strategies = _unpack_values(response.strategies)

  result = engine.FuzzResult(
      logs=response.logs,
      command=list(response.command),
      crashes=crashes,
      stats=unpacked_stats,
      time_executed=response.time_executed)

  file_host.pull_testcases_from_worker()
  return result, dict(response.fuzzer_metadata), unpacked_strategies
Пример #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 FuzzResult object.
        """
    runner = _get_runner()
    arguments = _DEFAULT_ARGUMENTS[:]
    arguments.extend(options.arguments)
    arguments.extend([
        "--input",
        options.corpus_dir,
        "--workspace",
        reproducers_dir,
        "--run_time",
        str(max_time),
        "--",
        target_path,
    ])

    fuzz_result = runner.run_and_wait(
        additional_args=arguments, timeout=max_time + _CLEAN_EXIT_SECS)
    log_lines = fuzz_result.output.splitlines()
    sanitizer_stacktrace = _find_sanitizer_stacktrace(reproducers_dir)

    crashes = []
    stats = None
    for line in log_lines:
      reproducer_path = _get_reproducer_path(line)
      if reproducer_path:
        crashes.append(
            engine.Crash(
                reproducer_path,
                sanitizer_stacktrace,
                [],
                int(fuzz_result.time_executed),
            ))
        continue

      stats = _get_stats(line)

    if stats is None:
      stats = {}

    return engine.FuzzResult(
        fuzz_result.output,
        fuzz_result.command,
        crashes,
        stats,
        fuzz_result.time_executed,
    )
Пример #4
0
    def fuzz(self, target_path, options, reproducers_dir, max_time):
        """Run a fuzzing session.
    Args:
      target_path: Path to the fuzzer script or binary.
      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 FuzzResult object.
    """
        # For blackbox fuzzers, |target_path| supplies the path to the fuzzer script
        # rather than a target in the build archive.
        fuzzer_path = target_path
        os.chmod(fuzzer_path, 0o775)

        app_path = environment.get_value('APP_PATH')
        app_args = testcase_manager.get_command_line_for_application(
            get_arguments_only=True).strip()
        corpus_dir = options.corpus_dir
        command_line_args = _get_arguments(app_path, app_args)
        command_line_args.append(f'--input_dir={corpus_dir}')

        result = _run_with_interpreter_if_needed(fuzzer_path,
                                                 command_line_args, max_time)
        crashes = []
        for testcase_path in os.listdir(reproducers_dir):
            if not testcase_path.startswith(TESTCASE_PREFIX):
                continue

            output_path = OUTPUT_PREFIX + testcase_path[len(TESTCASE_PREFIX):]
            absolute_output_path = os.path.join(reproducers_dir, output_path)

            # If no output was written for a test case, skip it.
            if not os.path.exists(absolute_output_path):
                continue

            with open(absolute_output_path, 'r', errors='replace') as handle:
                output = handle.read()

            # Filter obviously non-crashing test cases. Crashes still follow the
            # normal flow in fuzz task to ensure that the state should not be ignored
            # for other reasons, but we don't want to log every test case for the
            # fuzzers that don't do their own crash processing.
            state = stack_analyzer.get_crash_data(output)
            if not state.crash_type:
                continue

            full_testcase_path = os.path.join(reproducers_dir, testcase_path)
            crash = engine.Crash(full_testcase_path, output, options.arguments,
                                 int(result.time_executed))
            crashes.append(crash)

        # TODO(mbarbella): Support stats.
        stats = {}

        return engine.FuzzResult(result.output, result.command, crashes, stats,
                                 result.time_executed)
Пример #5
0
    def fuzz(self,
             fuzz_timeout,
             additional_args,
             unused_additional_args=None,
             unused_extra_env=None):
        """This is where actual syzkaller fuzzing is done.
    Args:
      fuzz_timeout: The maximum time in seconds that fuzz job is allowed
          to run for.
      additional_args: A sequence of additional arguments to be passed to
          the executable.
    """
        logs.log('Running Syzkaller.')
        additional_args = copy.copy(additional_args)
        fuzz_result = self.run_and_wait(additional_args, timeout=fuzz_timeout)
        logs.log('Syzkaller stopped, fuzzing timed out: {}'.format(
            fuzz_result.time_executed))

        fuzz_logs = ''
        crashes = []
        parsed_stats = {}
        visited = set()
        for subdir, _, files in os.walk(get_work_dir()):
            for file in files:
                # Each crash typically have 2 files: reportN and logN. Similar crashes
                # are grouped together in subfolders. unique_crash puts together the
                # subfolder name and reportN.
                unique_crash = os.path.join(subdir, file)
                if fnmatch.fnmatch(file,
                                   'report*') and unique_crash not in visited:
                    visited.add(unique_crash)
                    log_lines = utils.read_data_from_file(
                        os.path.join(subdir,
                                     file), eval_data=False).decode('utf-8')
                    fuzz_logs += log_lines + '\n'

                    # Since each crash (report file) has a corresponding log file
                    # that contains the syscalls that caused the crash. This file is
                    # located in the same subfolder and has the same number.
                    # E.g. ./439c37d288d4f26a33a6c7e5c57a97791453a447/report15 and
                    # ./439c37d288d4f26a33a6c7e5c57a97791453a447/log15.
                    crash_testcase_file_path = os.path.join(
                        subdir, 'log' + file[len('report'):])

                    # TODO(hzawawy): Parse stats information and add them to FuzzResult.

                    if crash_testcase_file_path:
                        reproduce_arguments = [unique_crash]
                        actual_duration = int(fuzz_result.time_executed)
                        # Write the new testcase.
                        # Copy crash testcase contents into the main testcase path.
                        crashes.append(
                            engine.Crash(crash_testcase_file_path, log_lines,
                                         reproduce_arguments, actual_duration))

        return engine.FuzzResult(fuzz_logs, fuzz_result.command, crashes,
                                 parsed_stats, fuzz_result.time_executed)
Пример #6
0
  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)
Пример #7
0
def engine_fuzz(engine_impl, target_name, sync_corpus_directory,
                testcase_directory):
  """Run engine fuzzer on untrusted worker."""
  request = untrusted_runner_pb2.EngineFuzzRequest(
      engine=engine_impl.name,
      target_name=target_name,
      sync_corpus_directory=file_host.rebase_to_worker_root(
          sync_corpus_directory),
      testcase_directory=file_host.rebase_to_worker_root(testcase_directory))

  response = host.stub().EngineFuzz(request)
  crashes = [
      engine.Crash(
          input_path=file_host.rebase_to_host_root(crash.input_path),
          stacktrace=utils.decode_to_unicode(crash.stacktrace),
          reproduce_args=crash.reproduce_args,
          crash_time=crash.crash_time) for crash in response.crashes
  ]

  unpacked_stats = {}
  for key, packed_value in six.iteritems(response.stats):
    if packed_value.Is(wrappers_pb2.DoubleValue.DESCRIPTOR):
      value = wrappers_pb2.DoubleValue()
    elif packed_value.Is(wrappers_pb2.Int32Value.DESCRIPTOR):
      value = wrappers_pb2.Int32Value()
    elif packed_value.Is(wrappers_pb2.StringValue.DESCRIPTOR):
      value = wrappers_pb2.StringValue()
    else:
      raise ValueError('Unknown stat type for ' + key)

    packed_value.Unpack(value)
    unpacked_stats[key] = value.value

  result = engine.FuzzResult(
      logs=utils.decode_to_unicode(response.logs),
      command=list(response.command),
      crashes=crashes,
      stats=unpacked_stats,
      time_executed=response.time_executed)

  file_host.pull_testcases_from_worker()
  return result, dict(response.fuzzer_metadata)
Пример #8
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)
Пример #9
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 FuzzResult object.
    """
        profiler.start_if_needed('libfuzzer_fuzz')
        runner = libfuzzer.get_runner(target_path)
        libfuzzer.set_sanitizer_options(target_path)

        # 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 = libfuzzer.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=reproducers_dir,
                                  extra_env=options.extra_env)

        if (not environment.get_value('USE_MINIJAIL') and
                fuzz_result.return_code == constants.LIBFUZZER_ERROR_EXITCODE):
            # Minijail returns 1 if the exit code is nonzero.
            # Otherwise: we can assume that a return code of 1 means that libFuzzer
            # itself ran into an error.
            logs.log_error(ENGINE_ERROR_MESSAGE,
                           engine_output=fuzz_result.output)

        log_lines = utils.decode_to_unicode(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 = runner.get_testcase_path(log_lines)

        # Parse stats information based on libFuzzer output.
        parsed_stats = libfuzzer.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[:]
        libfuzzer.remove_fuzzing_arguments(arguments)

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

        fuzz_logs = '\n'.join(log_lines)
        crashes = []
        if crash_testcase_file_path:
            # Use higher timeout for reproduction.
            reproduce_arguments = arguments[:]
            libfuzzer.fix_timeout_argument_for_reproduction(
                reproduce_arguments)

            # Write the new testcase.
            # Copy crash testcase contents into the main testcase path.
            crashes.append(
                engine.Crash(crash_testcase_file_path, fuzz_logs,
                             reproduce_arguments, actual_duration))

        project_qualified_fuzzer_name = (
            data_types.fuzz_target_project_qualified_name(
                utils.current_project(), os.path.basename(target_path)))
        libfuzzer.analyze_and_update_recommended_dictionary(
            runner, project_qualified_fuzzer_name, log_lines,
            options.corpus_dir, arguments)

        return engine.FuzzResult(fuzz_logs, fuzz_result.command, crashes,
                                 parsed_stats, fuzz_result.time_executed)
Пример #10
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 FuzzResult object.
        """
        profiler.start_if_needed("libfuzzer_fuzz")
        runner = libfuzzer.get_runner(target_path)
        libfuzzer.set_sanitizer_options(target_path)

        # 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 = libfuzzer.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=reproducers_dir,
            extra_env=options.extra_env,
        )

        project_qualified_fuzzer_name = _project_qualified_fuzzer_name(
            target_path)
        dict_error_match = DICT_PARSING_FAILED_REGEX.search(fuzz_result.output)
        if dict_error_match:
            logs.log_error(
                "Dictionary parsing failed (target={target}, line={line}).".
                format(target=project_qualified_fuzzer_name,
                       line=dict_error_match.group(1)),
                engine_output=fuzz_result.output,
            )
        elif (not environment.get_value("USE_MINIJAIL") and
              fuzz_result.return_code == constants.LIBFUZZER_ERROR_EXITCODE):
            # Minijail returns 1 if the exit code is nonzero.
            # Otherwise: we can assume that a return code of 1 means that libFuzzer
            # itself ran into an error.
            logs.log_error(
                ENGINE_ERROR_MESSAGE + " (target={target}).".format(
                    target=project_qualified_fuzzer_name),
                engine_output=fuzz_result.output,
            )

        log_lines = utils.decode_to_unicode(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 = runner.get_testcase_path(log_lines)

        # If we exited with a non-zero return code with no crash file in output from
        # libFuzzer, this is most likely a startup crash. Use an empty testcase to
        # to store it as a crash.
        if not crash_testcase_file_path and fuzz_result.return_code:
            crash_testcase_file_path = self._create_empty_testcase_file()

        # Parse stats information based on libFuzzer output.
        parsed_stats = libfuzzer.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))

        # 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[:]
        libfuzzer.remove_fuzzing_arguments(arguments)
        self._merge_new_units(
            target_path,
            options.corpus_dir,
            new_corpus_dir,
            options.fuzz_corpus_dirs,
            arguments,
            parsed_stats,
        )

        fuzz_logs = "\n".join(log_lines)
        crashes = []
        if crash_testcase_file_path:
            # Use higher timeout for reproduction.
            reproduce_arguments = arguments[:]
            libfuzzer.fix_timeout_argument_for_reproduction(
                reproduce_arguments)

            # Write the new testcase.
            # Copy crash testcase contents into the main testcase path.
            crashes.append(
                engine.Crash(
                    crash_testcase_file_path,
                    fuzz_logs,
                    reproduce_arguments,
                    actual_duration,
                ))

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

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