Ejemplo n.º 1
0
  def fuzz(self):
    """Starts the fuzz target run for the length of time specified by duration.

    Returns:
      (test_case, stack trace, time in seconds) on crash or
      (None, None, time in seconds) on timeout or error.
    """
    logging.info('Fuzzer %s, started.', self.target_name)
    docker_container = utils.get_container_name()
    command = ['docker', 'run', '--rm', '--privileged']
    if docker_container:
      command += [
          '--volumes-from', docker_container, '-e', 'OUT=' + self.out_dir
      ]
    else:
      command += ['-v', '%s:%s' % (self.out_dir, '/out')]

    command += [
        '-e', 'FUZZING_ENGINE=libfuzzer', '-e', 'SANITIZER=address', '-e',
        'RUN_FUZZER_MODE=interactive', 'gcr.io/oss-fuzz-base/base-runner',
        'bash', '-c'
    ]

    run_fuzzer_command = 'run_fuzzer {fuzz_target} {options}'.format(
        fuzz_target=self.target_name,
        options=LIBFUZZER_OPTIONS + ' -max_total_time=' + str(self.duration))

    # If corpus can be downloaded use it for fuzzing.
    latest_corpus_path = self.download_latest_corpus()
    if latest_corpus_path:
      run_fuzzer_command = run_fuzzer_command + ' ' + latest_corpus_path
    command.append(run_fuzzer_command)

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

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

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

    # Crash has been discovered.
    logging.info('Fuzzer %s, ended before timeout.', self.target_name)
    err_str = err.decode('ascii')
    test_case = self.get_test_case(err_str)
    if not test_case:
      logging.error('No test case found in stack trace: %s.', err_str)
      return None, None
    if self.check_reproducibility_and_regression(test_case):
      return test_case, err_str
    return None, None
Ejemplo n.º 2
0
def check_fuzzer_build(out_dir):
    """Checks the integrity of the built fuzzers.

  Args:
    out_dir: The directory containing the fuzzer binaries.

  Returns:
    True if fuzzers are correct.
  """
    if not os.path.exists(out_dir):
        logging.error('Invalid out directory: %s.', out_dir)
        return False
    if not os.listdir(out_dir):
        logging.error('No fuzzers found in out directory: %s.', out_dir)
        return False

    command = [
        '--cap-add', 'SYS_PTRACE', '-e', 'FUZZING_ENGINE=' + DEFAULT_ENGINE,
        '-e', 'SANITIZER=' + DEFAULT_SANITIZER, '-e',
        'ARCHITECTURE=' + DEFAULT_ARCHITECTURE
    ]
    container = utils.get_container_name()
    if container:
        command += ['-e', 'OUT=' + out_dir, '--volumes-from', container]
    else:
        command += ['-v', '%s:/out' % out_dir]
    command.extend(['-t', 'gcr.io/oss-fuzz-base/base-runner', 'test_all'])
    exit_code = helper.docker_run(command)
    if exit_code:
        logging.error('Check fuzzer build failed.')
        return False
    return True
Ejemplo n.º 3
0
    def build_fuzzers(self):
        """Moves the source code we want to fuzz into the project builder and builds
    the fuzzers from that source code. Returns True on success."""
        image_src_path = os.path.dirname(self.image_repo_path)
        command = get_common_docker_args(self.sanitizer)
        container = utils.get_container_name()

        if container:
            command.extend(
                ['-e', 'OUT=' + self.out_dir, '--volumes-from', container])
            rm_path = os.path.join(self.image_repo_path, '*')

            bash_command = 'rm -rf {0} && cp -r {1} {2} && compile'.format(
                rm_path, self.host_repo_path, image_src_path)
        else:
            # TODO(metzman): Figure out if we can eliminate this branch.
            command.extend([
                '-e', 'OUT=' + '/out', '-v',
                '%s:%s' % (self.host_repo_path, self.image_repo_path), '-v',
                '%s:%s' % (self.out_dir, '/out')
            ])
            bash_command = 'compile'

        command.extend([
            'gcr.io/oss-fuzz/' + self.project_name,
            '/bin/bash',
            '-c',
        ])
        command.append(bash_command)
        logging.info('Building with %s sanitizer.', self.sanitizer)
        if helper.docker_run(command):
            # docker_run returns nonzero on failure.
            logging.error('Building fuzzers failed.')
            return False
        return True
Ejemplo n.º 4
0
def get_base_docker_run_args(workspace,
                             sanitizer=constants.DEFAULT_SANITIZER,
                             language=constants.DEFAULT_LANGUAGE,
                             architecture=constants.DEFAULT_ARCHITECTURE,
                             docker_in_docker=False):
  """Returns arguments that should be passed to every invocation of 'docker
  run'."""
  docker_args = _DEFAULT_DOCKER_RUN_ARGS.copy()
  env_mapping = {
      'SANITIZER': sanitizer,
      'ARCHITECTURE': architecture,
      'FUZZING_LANGUAGE': language,
      'OUT': workspace.out
  }
  docker_args += get_docker_env_vars(env_mapping)
  docker_container = environment.get('CFL_CONTAINER_ID',
                                     utils.get_container_name())
  logging.info('Docker container: %s.', docker_container)
  if docker_container and not docker_in_docker:
    # Don't map specific volumes if in a docker container, it breaks when
    # running a sibling container.
    docker_args += ['--volumes-from', docker_container]
  else:
    docker_args += _get_args_mapping_host_path_to_container(workspace.workspace)
  return docker_args, docker_container
Ejemplo n.º 5
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):
            raise ReproduceError(f'Target {target_path} not found.')

        os.chmod(target_path, stat.S_IRWXO)

        target_dirname = os.path.dirname(target_path)
        command = ['docker', 'run', '--rm', '--privileged']
        container = utils.get_container_name()
        if container:
            command += [
                '--volumes-from', container, '-e', 'OUT=' + target_dirname,
                '-e', 'TESTCASE=' + testcase
            ]
        else:
            command += [
                '-v',
                f'{target_dirname}:/out',
                '-v',
                f'{testcase}:/testcase',
            ]

        command += [
            '-t', docker.BASE_RUNNER_TAG, 'reproduce', self.target_name,
            '-runs=100'
        ]

        logging.info('Running reproduce command: %s.', ' '.join(command))
        for _ in range(REPRODUCE_ATTEMPTS):
            _, _, returncode = utils.execute(command)
            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
Ejemplo n.º 6
0
def check_fuzzer_build(out_dir, sanitizer='address'):
  """Checks the integrity of the built fuzzers.

  Args:
    out_dir: The directory containing the fuzzer binaries.
    sanitizer: The sanitizer the fuzzers are built with.

  Returns:
    True if fuzzers are correct.
  """
  if not os.path.exists(out_dir):
    logging.error('Invalid out directory: %s.', out_dir)
    return False
  if not os.listdir(out_dir):
    logging.error('No fuzzers found in out directory: %s.', out_dir)
    return False

  command = [
      '--cap-add',
      'SYS_PTRACE',
      '-e',
      'FUZZING_ENGINE=' + DEFAULT_ENGINE,
      '-e',
      'SANITIZER=' + sanitizer,
      '-e',
      'ARCHITECTURE=' + DEFAULT_ARCHITECTURE,
      '-e',
      'CIFUZZ=True',
      '-e',
      'FUZZING_LANGUAGE=c++',  # FIXME: Add proper support.
  ]

  # Set ALLOWED_BROKEN_TARGETS_PERCENTAGE in docker if specified by user.
  allowed_broken_targets_percentage = os.getenv(
      'ALLOWED_BROKEN_TARGETS_PERCENTAGE')
  if allowed_broken_targets_percentage is not None:
    command += [
        '-e',
        ('ALLOWED_BROKEN_TARGETS_PERCENTAGE=' +
         allowed_broken_targets_percentage)
    ]

  container = utils.get_container_name()
  if container:
    command += ['-e', 'OUT=' + out_dir, '--volumes-from', container]
  else:
    command += ['-v', '%s:/out' % out_dir]
  command.extend(['-t', 'gcr.io/oss-fuzz-base/base-runner', 'test_all'])
  exit_code = helper.docker_run(command)
  if exit_code:
    logging.error('Check fuzzer build failed.')
    return False
  return True
Ejemplo n.º 7
0
def get_base_docker_run_args(workspace, sanitizer='address', language='c++'):
    """Returns arguments that should be passed to every invocation of 'docker
  run'."""
    docker_args = _DEFAULT_DOCKER_RUN_ARGS.copy()
    docker_args += [
        '-e', f'SANITIZER={sanitizer}', '-e', f'FUZZING_LANGUAGE={language}',
        '-e', 'OUT=' + workspace.out
    ]
    docker_container = utils.get_container_name()
    if docker_container:
        # Don't map specific volumes if in a docker container, it breaks when
        # running a sibling container.
        docker_args += ['--volumes-from', docker_container]
    else:
        docker_args += _get_args_mapping_host_path_to_container(
            workspace.workspace)
    return docker_args, docker_container
Ejemplo n.º 8
0
    def is_reproducible(self, test_case, target_path):
        """Checks if the test case reproduces.

      Args:
        test_case: The path to the test case to be tested.
        target_path: The path to the fuzz target to be tested

      Returns:
        True if crash is reproducible.
    """
        if not os.path.exists(test_case):
            logging.error('Test case %s is not found.', test_case)
            return False
        if os.path.exists(target_path):
            os.chmod(os.path.join(target_path, self.target_name), stat.S_IRWXO)

        command = ['docker', 'run', '--rm', '--privileged']
        container = utils.get_container_name()
        if container:
            command += [
                '--volumes-from', container, '-e', 'OUT=' + target_path, '-e',
                'TESTCASE=' + test_case
            ]
        else:
            command += [
                '-v',
                '%s:/out' % target_path, '-v',
                '%s:/testcase' % test_case
            ]

        command += [
            '-t', 'gcr.io/oss-fuzz-base/base-runner', 'reproduce',
            self.target_name, '-runs=100'
        ]

        logging.info('Running reproduce command: %s.', ' '.join(command))
        for _ in range(REPRODUCE_ATTEMPTS):
            out, _, err_code = utils.execute(command)
            if err_code:
                logging.error('Output for the reproduce command:\n%s.', out)
                return True
        return False
Ejemplo n.º 9
0
    def fuzz(self):
        """Starts the fuzz target run for the length of time specified by duration.

    Returns:
      (test_case, stack trace) if found or (None, None) on timeout or error.
    """
        logging.info('Fuzzer %s, started.', self.target_name)
        docker_container = utils.get_container_name()
        command = ['docker', 'run', '--rm', '--privileged']
        if docker_container:
            command += [
                '--volumes-from', docker_container, '-e', 'OUT=' + self.out_dir
            ]
        else:
            command += ['-v', '%s:%s' % (self.out_dir, '/out')]

        command += [
            '-e', 'FUZZING_ENGINE=libfuzzer', '-e', 'SANITIZER=address', '-e',
            'RUN_FUZZER_MODE=interactive', 'gcr.io/oss-fuzz-base/base-runner',
            'bash', '-c', 'run_fuzzer {fuzz_target} {options}'.format(
                fuzz_target=self.target_name, options=LIBFUZZER_OPTIONS)
        ]
        logging.info('Running command: %s', ' '.join(command))
        process = subprocess.Popen(command,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE)

        try:
            _, err = process.communicate(timeout=self.duration)
        except subprocess.TimeoutExpired:
            logging.info('Fuzzer %s, finished with timeout.', self.target_name)
            return None, None

        logging.info('Fuzzer %s, ended before timeout.', self.target_name)
        err_str = err.decode('ascii')
        test_case = self.get_test_case(err_str)
        if not test_case:
            logging.error('No test case found in stack trace: %s.', err_str)
            return None, None
        if self.check_reproducibility_and_regression(test_case):
            return test_case, err_str
        return None, None
Ejemplo n.º 10
0
def check_fuzzer_build(out_dir,
                       sanitizer,
                       language,
                       allowed_broken_targets_percentage=None):
    """Checks the integrity of the built fuzzers.

  Args:
    out_dir: The directory containing the fuzzer binaries.
    sanitizer: The sanitizer the fuzzers are built with.

  Returns:
    True if fuzzers are correct.
  """
    if not os.path.exists(out_dir):
        logging.error('Invalid out directory: %s.', out_dir)
        return False
    if not os.listdir(out_dir):
        logging.error('No fuzzers found in out directory: %s.', out_dir)
        return False

    command = get_common_docker_args(sanitizer, language)

    if allowed_broken_targets_percentage is not None:
        command += [
            '-e',
            ('ALLOWED_BROKEN_TARGETS_PERCENTAGE=' +
             allowed_broken_targets_percentage)
        ]

    container = utils.get_container_name()
    if container:
        command += ['-e', 'OUT=' + out_dir, '--volumes-from', container]
    else:
        command += ['-v', '%s:/out' % out_dir]
    command.extend(['-t', 'gcr.io/oss-fuzz-base/base-runner', 'test_all.py'])
    exit_code = helper.docker_run(command)
    logging.info('check fuzzer build exit code: %d', exit_code)
    if exit_code:
        logging.error('Check fuzzer build failed.')
        return False
    return True
Ejemplo n.º 11
0
    def build_fuzzers(self):
        """Moves the source code we want to fuzz into the project builder and builds
    the fuzzers from that source code. Returns True on success."""
        docker_args = get_common_docker_args(self.config.sanitizer,
                                             self.config.language)
        container = utils.get_container_name()

        if container:
            docker_args.extend(
                _get_docker_build_fuzzers_args_container(
                    self.out_dir, container))
        else:
            docker_args.extend(
                _get_docker_build_fuzzers_args_not_container(
                    self.out_dir, self.host_repo_path))

        if self.config.sanitizer == 'memory':
            docker_args.extend(
                _get_docker_build_fuzzers_args_msan(self.work_dir))
            self.handle_msan_prebuild(container)

        docker_args.extend([
            'gcr.io/oss-fuzz/' + self.config.project_name,
            '/bin/bash',
            '-c',
        ])
        rm_path = os.path.join(self.image_repo_path, '*')
        image_src_path = os.path.dirname(self.image_repo_path)
        bash_command = 'rm -rf {0} && cp -r {1} {2} && compile'.format(
            rm_path, self.host_repo_path, image_src_path)
        docker_args.append(bash_command)
        logging.info('Building with %s sanitizer.', self.config.sanitizer)
        if helper.docker_run(docker_args):
            # docker_run returns nonzero on failure.
            logging.error('Building fuzzers failed.')
            return False

        if self.config.sanitizer == 'memory':
            self.handle_msan_postbuild(container)
        return True
Ejemplo n.º 12
0
    def fuzz(self):
        """Starts the fuzz target run for the length of time specified by duration.

    Returns:
      FuzzResult namedtuple with stacktrace and testcase if applicable.
    """
        logging.info('Fuzzer %s, started.', self.target_name)
        docker_container = utils.get_container_name()
        command = ['docker', 'run', '--rm', '--privileged']
        if docker_container:
            command += [
                '--volumes-from', docker_container, '-e', 'OUT=' + self.out_dir
            ]
        else:
            command += ['-v', '%s:%s' % (self.out_dir, '/out')]

        command += [
            '-e', 'FUZZING_ENGINE=libfuzzer', '-e',
            'SANITIZER=' + self.config.sanitizer, '-e', 'CIFUZZ=True', '-e',
            'RUN_FUZZER_MODE=interactive', docker.BASE_RUNNER_TAG, 'bash', '-c'
        ]

        run_fuzzer_command = 'run_fuzzer {fuzz_target} {options}'.format(
            fuzz_target=self.target_name,
            options=LIBFUZZER_OPTIONS + ' -max_total_time=' +
            str(self.duration))

        # If corpus can be downloaded use it for fuzzing.
        self.latest_corpus_path = self.clusterfuzz_deployment.download_corpus(
            self.target_name, self.out_dir)
        if self.latest_corpus_path:
            run_fuzzer_command = run_fuzzer_command + ' ' + self.latest_corpus_path
        command.append(run_fuzzer_command)

        logging.info('Running command: %s', ' '.join(command))
        process = subprocess.Popen(command,
                                   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)

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

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

        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)

        # We found a bug but we won't report it.
        return FuzzResult(None, None)
Ejemplo n.º 13
0
def build_fuzzers(project_name,
                  project_repo_name,
                  workspace,
                  pr_ref=None,
                  commit_sha=None,
                  sanitizer='address'):
    """Builds all of the fuzzers for a specific OSS-Fuzz project.

  Args:
    project_name: The name of the OSS-Fuzz project being built.
    project_repo_name: The name of the projects repo.
    workspace: The location in a shared volume to store a git repo and build
      artifacts.
    pr_ref: The pull request reference to be built.
    commit_sha: The commit sha for the project to be built at.
    sanitizer: The sanitizer the fuzzers should be built with.

  Returns:
    True if build succeeded or False on failure.
  """
    # Validate inputs.
    assert pr_ref or commit_sha
    if not os.path.exists(workspace):
        logging.error('Invalid workspace: %s.', workspace)
        return False

    logging.info("Using %s sanitizer.", sanitizer)

    out_dir = os.path.join(workspace, 'out')
    os.makedirs(out_dir, exist_ok=True)

    # Build Fuzzers using docker run.
    inferred_url, project_builder_repo_path = (
        build_specified_commit.detect_main_repo(project_name,
                                                repo_name=project_repo_name))
    if not inferred_url or not project_builder_repo_path:
        logging.error('Could not detect repo from project %s.', project_name)
        return False
    project_repo_name = os.path.basename(project_builder_repo_path)
    src_in_project_builder = os.path.dirname(project_builder_repo_path)

    manual_src_path = os.getenv('MANUAL_SRC_PATH')
    if manual_src_path:
        if not os.path.exists(manual_src_path):
            logging.error(
                'MANUAL_SRC_PATH: %s does not exist. '
                'Are you mounting it correctly?', manual_src_path)
            return False
        # This is the path taken outside of GitHub actions.
        git_workspace = os.path.dirname(manual_src_path)
    else:
        git_workspace = os.path.join(workspace, 'storage')
        os.makedirs(git_workspace, exist_ok=True)

    # Checkout projects repo in the shared volume.
    build_repo_manager = repo_manager.RepoManager(inferred_url,
                                                  git_workspace,
                                                  repo_name=project_repo_name)

    if not manual_src_path:
        checkout_specified_commit(build_repo_manager, pr_ref, commit_sha)

    command = [
        '--cap-add',
        'SYS_PTRACE',
        '-e',
        'FUZZING_ENGINE=' + DEFAULT_ENGINE,
        '-e',
        'SANITIZER=' + sanitizer,
        '-e',
        'ARCHITECTURE=' + DEFAULT_ARCHITECTURE,
        '-e',
        'FUZZING_LANGUAGE=c++',  # FIXME: Add proper support.
    ]
    container = utils.get_container_name()
    if container:
        command += ['-e', 'OUT=' + out_dir, '--volumes-from', container]
        bash_command = 'rm -rf {0} && cp -r {1} {2} && compile'.format(
            os.path.join(src_in_project_builder, project_repo_name, '*'),
            os.path.join(git_workspace, project_repo_name),
            src_in_project_builder)
    else:
        command += [
            '-e', 'OUT=' + '/out', '-v',
            '%s:%s' %
            (os.path.join(git_workspace, project_repo_name),
             os.path.join(src_in_project_builder, project_repo_name)), '-v',
            '%s:%s' % (out_dir, '/out')
        ]
        bash_command = 'compile'

    command.extend([
        'gcr.io/oss-fuzz/' + project_name,
        '/bin/bash',
        '-c',
    ])
    command.append(bash_command)
    if helper.docker_run(command):
        logging.error('Building fuzzers failed.')
        return False
    remove_unaffected_fuzzers(project_name, out_dir,
                              build_repo_manager.get_git_diff(),
                              project_builder_repo_path)
    return True
Ejemplo n.º 14
0
def build_fuzzers(project_name,
                  project_repo_name,
                  workspace,
                  pr_ref=None,
                  commit_sha=None):
    """Builds all of the fuzzers for a specific OSS-Fuzz project.

  Args:
    project_name: The name of the OSS-Fuzz project being built.
    project_repo_name: The name of the projects repo.
    workspace: The location in a shared volume to store a git repo and build
      artifacts.
    pr_ref: The pull request reference to be built.
    commit_sha: The commit sha for the project to be built at.

  Returns:
    True if build succeeded or False on failure.
  """
    # Validate inputs.
    assert pr_ref or commit_sha
    if not os.path.exists(workspace):
        logging.error('Invalid workspace: %s.', workspace)
        return False

    git_workspace = os.path.join(workspace, 'storage')
    os.makedirs(git_workspace, exist_ok=True)
    out_dir = os.path.join(workspace, 'out')
    os.makedirs(out_dir, exist_ok=True)

    # Detect repo information.
    inferred_url, oss_fuzz_repo_path = build_specified_commit.detect_main_repo(
        project_name, repo_name=project_repo_name)
    if not inferred_url or not oss_fuzz_repo_path:
        logging.error('Could not detect repo from project %s.', project_name)
        return False
    src_in_docker = os.path.dirname(oss_fuzz_repo_path)
    oss_fuzz_repo_name = os.path.basename(oss_fuzz_repo_path)

    # Checkout projects repo in the shared volume.
    build_repo_manager = repo_manager.RepoManager(inferred_url,
                                                  git_workspace,
                                                  repo_name=oss_fuzz_repo_name)
    try:
        if pr_ref:
            build_repo_manager.checkout_pr(pr_ref)
        else:
            build_repo_manager.checkout_commit(commit_sha)
    except RuntimeError:
        logging.error('Can not check out requested state.')
        return False
    except ValueError:
        logging.error('Invalid commit SHA requested %s.', commit_sha)
        return False

    # Build Fuzzers using docker run.
    command = [
        '--cap-add', 'SYS_PTRACE', '-e', 'FUZZING_ENGINE=' + DEFAULT_ENGINE,
        '-e', 'SANITIZER=' + DEFAULT_SANITIZER, '-e',
        'ARCHITECTURE=' + DEFAULT_ARCHITECTURE
    ]
    container = utils.get_container_name()
    if container:
        command += ['-e', 'OUT=' + out_dir, '--volumes-from', container]
        bash_command = 'rm -rf {0} && cp -r {1} {2} && compile'.format(
            os.path.join(src_in_docker, oss_fuzz_repo_name, '*'),
            os.path.join(git_workspace, oss_fuzz_repo_name), src_in_docker)
    else:
        command += [
            '-e', 'OUT=' + '/out', '-v',
            '%s:%s' % (os.path.join(git_workspace, oss_fuzz_repo_name),
                       os.path.join(src_in_docker, oss_fuzz_repo_name)), '-v',
            '%s:%s' % (out_dir, '/out')
        ]
        bash_command = 'compile'

    command.extend([
        'gcr.io/oss-fuzz/' + project_name,
        '/bin/bash',
        '-c',
    ])
    command.append(bash_command)
    if helper.docker_run(command):
        logging.error('Building fuzzers failed.')
        return False
    remove_unaffected_fuzzers(project_name, out_dir,
                              build_repo_manager.get_git_diff(),
                              oss_fuzz_repo_path)
    return True
Ejemplo n.º 15
0
    def fuzz(self):
        """Starts the fuzz target run for the length of time specified by duration.

    Returns:
      FuzzResult namedtuple with stacktrace and testcase if applicable.
    """
        logging.info('Fuzzer %s, started.', self.target_name)
        docker_container = utils.get_container_name()
        command_arguments = []
        if docker_container:
            command_arguments += [
                '--volumes-from', docker_container, '-e', 'OUT=' + self.out_dir
            ]
        else:
            command_arguments += ['-v', '%s:%s' % (self.out_dir, '/out')]

        command_arguments += [
            '-e', 'FUZZING_ENGINE=libfuzzer', '-e',
            'SANITIZER=' + self.config.sanitizer, '-e', 'CIFUZZ=True', '-e',
            'RUN_FUZZER_MODE=interactive', docker.BASE_RUNNER_TAG, 'bash', '-c'
        ]

        run_fuzzer_command = 'run_fuzzer {fuzz_target} {options}'.format(
            fuzz_target=self.target_name,
            options=LIBFUZZER_OPTIONS + ' -max_total_time=' +
            str(self.duration))

        # If corpus can be downloaded use it for fuzzing.
        self.latest_corpus_path = self.clusterfuzz_deployment.download_corpus(
            self.target_name, self.out_dir)
        if self.latest_corpus_path:
            run_fuzzer_command = run_fuzzer_command + ' ' + self.latest_corpus_path
        command_arguments.append(run_fuzzer_command)
        result = docker.run_container_command(command_arguments,
                                              timeout=self.duration +
                                              BUFFER_TIME)

        if result.timed_out:
            logging.info('Stopped docker container before timeout.')

        if not result.retcode:
            logging.info('Fuzzer %s finished with no crashes discovered.',
                         self.target_name)
            return FuzzResult(None, None)

        if result.stderr is None:
            return FuzzResult(None, None)

        # Crash was discovered.
        logging.info('Fuzzer %s, ended before timeout.', self.target_name)

        # TODO(metzman): Replace this with artifact_prefix so we don't have to
        # parse.
        testcase = self.get_testcase(result.stderr)

        if not testcase:
            logging.error(b'No testcase found in stacktrace: %s.',
                          result.stderr)
            return FuzzResult(None, None)

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

        # We found a bug but we won't report it.
        return FuzzResult(None, None)
Ejemplo n.º 16
0
def build_fuzzers(project_name, project_repo_name, commit_sha, git_workspace,
                  out_dir):
    """Builds all of the fuzzers for a specific OSS-Fuzz project.

  Args:
    project_name: The name of the OSS-Fuzz project being built.
    project_repo_name: The name of the projects repo.
    commit_sha: The commit SHA to be checked out and fuzzed.
    git_workspace: The location in the shared volume to store git repos.
    out_dir: The location in the shared volume to store output artifacts.

  Returns:
    True if build succeeded or False on failure.
  """
    if not os.path.exists(git_workspace):
        logging.error('Invalid git workspace: %s.', format(git_workspace))
        return False
    if not os.path.exists(out_dir):
        logging.error('Invalid out directory %s.', format(out_dir))
        return False

    inferred_url, oss_fuzz_repo_path = build_specified_commit.detect_main_repo(
        project_name, repo_name=project_repo_name)
    if not inferred_url or not oss_fuzz_repo_path:
        logging.error('Could not detect repo from project %s.', project_name)
        return False
    src_in_docker = os.path.dirname(oss_fuzz_repo_path)
    oss_fuzz_repo_name = os.path.basename(oss_fuzz_repo_path)

    # Checkout projects repo in the shared volume.
    build_repo_manager = repo_manager.RepoManager(inferred_url,
                                                  git_workspace,
                                                  repo_name=oss_fuzz_repo_name)
    try:
        build_repo_manager.checkout_commit(commit_sha)
    except repo_manager.RepoManagerError:
        logging.error('Specified commit does not exist.')
        # NOTE: Remove return statement for testing.
        return False

    command = [
        '--cap-add', 'SYS_PTRACE', '-e', 'FUZZING_ENGINE=libfuzzer', '-e',
        'SANITIZER=address', '-e', 'ARCHITECTURE=x86_64'
    ]
    container = utils.get_container_name()
    if container:
        command += ['-e', 'OUT=' + out_dir, '--volumes-from', container]
        bash_command = 'rm -rf {0} && cp -r {1} {2} && compile'.format(
            os.path.join(src_in_docker, oss_fuzz_repo_name, '*'),
            os.path.join(git_workspace, oss_fuzz_repo_name), src_in_docker)
    else:
        command += [
            '-e', 'OUT=' + '/out', '-v',
            '%s:%s' % (os.path.join(git_workspace, oss_fuzz_repo_name),
                       os.path.join(src_in_docker, oss_fuzz_repo_name)), '-v',
            '%s:%s' % (out_dir, '/out')
        ]
        bash_command = 'compile'

    command.extend([
        'gcr.io/oss-fuzz/' + project_name,
        '/bin/bash',
        '-c',
    ])
    command.append(bash_command)

    if helper.docker_run(command):
        logging.error('Building fuzzers failed.')
        return False
    return True