Beispiel #1
0
def check_fuzzer_build(config):
    """Checks the integrity of the built fuzzers.

  Args:
    config: The config object.

  Returns:
    True if fuzzers pass OSS-Fuzz's build check.
  """
    workspace = workspace_utils.Workspace(config)
    if not os.path.exists(workspace.out):
        logging.error('Invalid out directory: %s.', workspace.out)
        return False
    if not os.listdir(workspace.out):
        logging.error('No fuzzers found in out directory: %s.', workspace.out)
        return False

    env = base_runner_utils.get_env(config, workspace)
    if config.allowed_broken_targets_percentage is not None:
        env['ALLOWED_BROKEN_TARGETS_PERCENTAGE'] = (
            config.allowed_broken_targets_percentage)

    stdout, stderr, retcode = utils.execute('test_all.py', env=env)
    print(f'Build check: stdout: {stdout}\nstderr: {stderr}')
    if retcode == 0:
        logging.info('Build check passed.')
        return True
    logging.error('Build check failed.')
    return False
Beispiel #2
0
  def fuzz(self, use_corpus=True, extra_libfuzzer_options=None):
    """Starts the fuzz target run for the length of time specified by duration.

    Returns:
      FuzzResult namedtuple with stacktrace and testcase if applicable.
    """
    logging.info('Running fuzzer: %s.', self.target_name)
    if extra_libfuzzer_options is None:
      extra_libfuzzer_options = []
    env = base_runner_utils.get_env(self.config, self.workspace)
    # TODO(metzman): Is this needed?
    env['RUN_FUZZER_MODE'] = 'interactive'

    if use_corpus:
      # If corpus can be downloaded, use it for fuzzing.
      self._download_corpus()
      env['CORPUS_DIR'] = self.latest_corpus_path

    options = LIBFUZZER_OPTIONS.copy() + [
        f'-max_total_time={self.duration}',
        # Make sure libFuzzer artifact files don't pollute $OUT.
        f'-artifact_prefix={self.workspace.artifacts}/'
    ] + extra_libfuzzer_options
    command = ['run_fuzzer', self.target_name] + options

    logging.info('Running command: %s', command)
    process = subprocess.Popen(command,
                               env=env,
                               stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE)

    try:
      _, stderr = process.communicate(timeout=self.duration + BUFFER_TIME)
    except subprocess.TimeoutExpired:
      logging.error('Fuzzer %s timed out, ending fuzzing.', self.target_name)
      return FuzzResult(None, None, self.latest_corpus_path)

    # Libfuzzer timeout was reached.
    if not process.returncode:
      logging.info('Fuzzer %s finished with no crashes discovered.',
                   self.target_name)
      return FuzzResult(None, None, self.latest_corpus_path)

    # Crash was discovered.
    logging.info('Fuzzer %s, ended before timeout.', self.target_name)
    testcase = get_testcase(stderr)
    if not testcase:
      logging.error(b'No testcase found in stacktrace: %s.', stderr)
      return FuzzResult(None, None, self.latest_corpus_path)

    utils.binary_print(b'Fuzzer: %s. Detected bug:\n%s' %
                       (self.target_name.encode(), stderr))
    if self.is_crash_reportable(testcase):
      # We found a bug in the fuzz target and we will report it.
      return FuzzResult(testcase, stderr, self.latest_corpus_path)

    # We found a bug but we won't report it.
    return FuzzResult(None, None, self.latest_corpus_path)
Beispiel #3
0
def run_coverage_command(config, workspace):
    """Runs the coverage command in base-runner to generate a coverage report."""
    env = base_runner_utils.get_env(config, workspace)
    env['HTTP_PORT'] = ''
    env['COVERAGE_EXTRA_ARGS'] = ''
    env['CORPUS_DIR'] = workspace.corpora
    env['COVERAGE_OUTPUT_DIR'] = workspace.coverage_report
    command = 'coverage'
    return utils.execute(command, env=env)
Beispiel #4
0
  def is_reproducible(self, testcase, target_path):
    """Checks if the testcase reproduces.

      Args:
        testcase: The path to the testcase to be tested.
        target_path: The path to the fuzz target to be tested

      Returns:
        True if crash is reproducible and we were able to run the
        binary.

      Raises:
        ReproduceError if we can't attempt to reproduce the crash.
    """
    if not os.path.exists(target_path):
      logging.info('Target: %s does not exist.', target_path)
      raise ReproduceError(f'Target {target_path} not found.')

    os.chmod(target_path, stat.S_IRWXO)

    env = base_runner_utils.get_env(self.config, self.workspace)
    env['TESTCASE'] = testcase
    command = ['reproduce', self.target_name, '-runs=100']

    logging.info('Running reproduce command: %s.', ' '.join(command))
    for _ in range(REPRODUCE_ATTEMPTS):
      _, _, returncode = utils.execute(command, env=env)

      if returncode != 0:
        logging.info('Reproduce command returned: %s. Reproducible on %s.',
                     returncode, target_path)

        return True

    logging.info('Reproduce command returned 0. Not reproducible on %s.',
                 target_path)
    return False