def test_test_for_crash_with_retries_greybox_succeed_no_comparison(self):
    """Test test_for_crash_with_retries reproducing a crash with compare_crash
    set to False (greybox)."""
    mock_engine = mock.Mock()
    mock_engine.reproduce.side_effect = [
        engine.ReproduceResult(['cmd'], 0, 0, 'output'),
        engine.ReproduceResult(['cmd'], 1, 1, 'crash'),
    ]
    self.mock.get.return_value = mock_engine

    crash_result = testcase_manager.test_for_crash_with_retries(
        self.greybox_testcase, '/fuzz-testcase', 10, compare_crash=False)
    self.assertEqual(1, crash_result.return_code)
    self.assertEqual(1, crash_result.crash_time)
    self.assertEqual(self.GREYBOX_FUZZER_CRASH, crash_result.output)
    self.assertEqual(2, mock_engine.reproduce.call_count)
    mock_engine.reproduce.assert_has_calls([
        mock.call('/build_dir/target', '/fuzz-testcase', ['-arg1', '-arg2'],
                  120),
        mock.call('/build_dir/target', '/fuzz-testcase', ['-arg1', '-arg2'],
                  10),
    ])
    self.mock.log.assert_has_calls([
        mock.call(
            'No crash occurred (round 1).',
            output=self.GREYBOX_FUZZER_NO_CRASH),
        mock.call(
            'Crash occurred in 1 seconds (round 2). State:\nstate',
            output=self.GREYBOX_FUZZER_CRASH),
        mock.call('Crash stacktrace comparison skipped.')
    ])
  def test_test_for_reproducibility_greybox_succeed_after_multiple_tries(self):
    """Test test_for_reproducibility with with failure on first run and then
    succeed on remaining runs  (greybox)."""
    mock_engine = mock.Mock()
    mock_engine.reproduce.side_effect = [
        engine.ReproduceResult(['cmd'], 0, 0, 'output'),
        engine.ReproduceResult(['cmd'], 1, 1, 'crash'),
        engine.ReproduceResult(['cmd'], 1, 1, 'crash'),
    ]
    self.mock.get.return_value = mock_engine

    result = testcase_manager.test_for_reproducibility(
        'engine',
        'engine_target',
        '/fuzz-testcase',
        'state',
        expected_security_flag=False,
        test_timeout=10,
        http_flag=False,
        gestures=None)
    self.assertTrue(result)

    self.assertEqual(3, mock_engine.reproduce.call_count)
    self.mock.log.assert_has_calls([
        mock.call(
            'No crash occurred (round 1).',
            output=self.GREYBOX_FUZZER_NO_CRASH),
        mock.call(
            'Crash occurred in 1 seconds (round 2). State:\nstate',
            output=self.GREYBOX_FUZZER_CRASH),
        mock.call(
            'Crash occurred in 1 seconds (round 3). State:\nstate',
            output=self.GREYBOX_FUZZER_CRASH),
        mock.call('Crash is reproducible.'),
    ])
Ejemplo n.º 3
0
    def repro(self, repro_timeout: int,
              repro_args: List[str]) -> engine.ReproduceResult:
        """This is where crash repro'ing is done.
    Args:
      repro_timeout: The maximum time in seconds that repro job is allowed
          to run for.
      repro_args: A sequence of arguments to be passed to the executable.
    """
        logs.log('Running Syzkaller (syz-crush) against testcase.')
        additional_args = repro_args.copy()

        for retry_count in range(REPRO_RETRY_MAX):
            result = self.run_and_wait(additional_args, timeout=repro_timeout)
            log_location = re.search(REPRODUCE_LOG_LOCATION_PATTERN,
                                     result.output)

            # syz-crush stopped before capturing crash output
            if not log_location:
                continue

            # syz-crush did not reproduce any crash
            if not self._crash_was_reproducible(result.output):
                continue

            _type, log_index, log_dir = log_location.groups()  # pylint: disable=invalid-name
            try:
                reproducer_log_path = os.path.join(log_dir,
                                                   f'reproducer{log_index}')
                with open(reproducer_log_path, 'r') as f:
                    logs.log('Successfully reproduced crash.')
                    return engine.ReproduceResult(
                        command=result.command,
                        return_code=1,
                        time_executed=result.time_executed,
                        output=f.read(),
                    )
            except FileNotFoundError as e:
                logs.log('Reproducer log was not found. Rerunning syz-crush',
                         e)
                continue

        logs.log(f'Failed to reproduce crash after {retry_count} attempts.')
        return engine.ReproduceResult(
            command=result.command,
            return_code=0,
            time_executed=result.time_executed,
            output=result.output,
        )
Ejemplo n.º 4
0
    def cleanse(self, target_path, arguments, input_path, output_path,
                max_time):
        """Optional (but recommended): Cleanse a testcase.

    Args:
      target_path: Path to the target.
      arguments: Additional arguments needed for testcase cleanse.
      input_path: Path to the reproducer input.
      output_path: Path to the cleansed output.
      max_time: Maximum allowed time for the cleanse.

    Returns:
      A ReproduceResult.

    Raises:
      TimeoutError: If the cleanse exceeds max_time.
    """
        runner = libfuzzer.get_runner(target_path)
        libfuzzer.set_sanitizer_options(target_path)

        cleanse_tmp_dir = self._create_temp_corpus_dir('cleanse-workdir')
        result = runner.cleanse_crash(input_path,
                                      output_path,
                                      max_time,
                                      artifact_prefix=cleanse_tmp_dir,
                                      additional_args=arguments)

        if result.timed_out:
            logs.log_error('Cleanse timed out.', fuzzer_output=result.output)
            raise TimeoutError('Cleanse timed out.')

        return engine.ReproduceResult(result.command, result.return_code,
                                      result.time_executed, result.output)
  def test_test_for_reproducibility_greybox_succeed(self):
    """Test test_for_reproducibility with success on all runs (greybox)."""
    mock_engine = mock.Mock()
    mock_engine.reproduce.return_value = engine.ReproduceResult(['cmd'], 1, 1,
                                                                'crash')
    self.mock.get.return_value = mock_engine

    result = testcase_manager.test_for_reproducibility(
        'engine',
        'engine_target',
        '/fuzz-testcase',
        'state',
        expected_security_flag=False,
        test_timeout=10,
        http_flag=False,
        gestures=None)
    self.assertTrue(result)

    # Only 2/3 runs needed to verify reproducibility.
    self.assertEqual(2, mock_engine.reproduce.call_count)
    self.mock.log.assert_has_calls([
        mock.call(
            'Crash occurred in 1 seconds (round 1). State:\nstate',
            output=self.GREYBOX_FUZZER_CRASH),
        mock.call(
            'Crash occurred in 1 seconds (round 2). State:\nstate',
            output=self.GREYBOX_FUZZER_CRASH),
        mock.call('Crash is reproducible.'),
    ])
  def test_test_for_crash_with_retries_greybox_fail(self):
    """Test test_for_crash_with_retries failing to reproduce a crash
    (greybox)."""
    mock_engine = mock.Mock()
    mock_engine.reproduce.return_value = engine.ReproduceResult(['cmd'], 0, 0,
                                                                'output')
    self.mock.get.return_value = mock_engine

    crash_result = testcase_manager.test_for_crash_with_retries(
        self.greybox_testcase, '/fuzz-testcase', 10)
    self.assertEqual(0, crash_result.return_code)
    self.assertEqual(0, crash_result.crash_time)
    self.assertEqual(self.GREYBOX_FUZZER_NO_CRASH, crash_result.output)
    self.assertEqual(3, mock_engine.reproduce.call_count)
    mock_engine.reproduce.assert_has_calls([
        mock.call('/build_dir/target', '/fuzz-testcase', ['-arg1', '-arg2'],
                  120),
        mock.call('/build_dir/target', '/fuzz-testcase', ['-arg1', '-arg2'],
                  10),
        mock.call('/build_dir/target', '/fuzz-testcase', ['-arg1', '-arg2'],
                  10),
    ])
    self.mock.log.assert_has_calls(
        [
            mock.call(
                'No crash occurred (round 1).',
                output=self.GREYBOX_FUZZER_NO_CRASH),
            mock.call(
                'No crash occurred (round 2).',
                output=self.GREYBOX_FUZZER_NO_CRASH),
            mock.call(
                'No crash occurred (round 3).',
                output=self.GREYBOX_FUZZER_NO_CRASH),
            mock.call("Didn't crash at all.")
        ])
Ejemplo n.º 7
0
def process_testcase(engine_name, tool_name, target_name, arguments,
                     testcase_path, output_path, timeout):
    """Process testcase on untrusted worker."""
    if tool_name == 'minimize':
        operation = untrusted_runner_pb2.ProcessTestcaseRequest.MINIMIZE
    else:
        operation = untrusted_runner_pb2.ProcessTestcaseRequest.CLEANSE

    rebased_testcase_path = file_host.rebase_to_worker_root(testcase_path)
    file_host.copy_file_to_worker(testcase_path, rebased_testcase_path)

    request = untrusted_runner_pb2.ProcessTestcaseRequest(
        engine=engine_name,
        operation=operation,
        target_name=target_name,
        arguments=arguments,
        testcase_path=file_host.rebase_to_worker_root(testcase_path),
        output_path=file_host.rebase_to_worker_root(output_path),
        timeout=timeout)

    response = host.stub().ProcessTestcase(request)

    rebased_output_path = file_host.rebase_to_worker_root(output_path)
    file_host.copy_file_from_worker(rebased_output_path, output_path)

    return engine.ReproduceResult(list(response.command), response.return_code,
                                  response.time_executed, response.output)
Ejemplo n.º 8
0
    def reproduce(self, target_path, input_path, arguments, max_time):  # pylint: disable=unused-argument
        """Reproduce a crash given an input.
       Example: ./syz-crush -config my.cfg -infinite=false -restart_time=20s
        crash-qemu-1-1455745459265726910

    Args:
      target_path: Path to the target.
      input_path: Path to the reproducer input.
      arguments: Additional arguments needed for reproduction.
      max_time: Maximum allowed time for the reproduction.

    Returns:
      A ReproduceResult.
    """
        binary_dir = self.prepare_binary_path()
        syzkaller_runner = runner.get_runner(
            os.path.join(binary_dir, constants.SYZ_REPRO))
        repro_args = runner.get_config()
        repro_args.extend([
            '-infinite=false', '-restart_time={}s'.format(REPRO_TIME),
            input_path
        ])
        result = syzkaller_runner.repro(max_time, repro_args=repro_args)

        return engine.ReproduceResult(result.command, result.return_code,
                                      result.time_executed, result.output)
Ejemplo n.º 9
0
    def reproduce(self, target_path, input_path, arguments, max_time):
        """Reproduce a crash given an input.

    Args:
      target_path: Path to the target.
      input_path: Path to the reproducer input.
      arguments: Additional arguments needed for reproduction.
      max_time: Maximum allowed time for the reproduction.

    Returns:
      A ReproduceResult.

    Raises:
      TimeoutError: If the reproduction exceeds max_time.
    """
        runner = libfuzzer.get_runner(target_path)
        libfuzzer.set_sanitizer_options(target_path)

        # Remove fuzzing specific arguments. This is only really needed for legacy
        # testcases, and can be removed in the distant future.
        arguments = arguments[:]
        libfuzzer.remove_fuzzing_arguments(arguments)

        runs_argument = constants.RUNS_FLAG + str(constants.RUNS_TO_REPRODUCE)
        arguments.append(runs_argument)

        result = runner.run_single_testcase(input_path,
                                            timeout=max_time,
                                            additional_args=arguments)

        if result.timed_out:
            raise TimeoutError('Reproducing timed out\n' + result.output)

        return engine.ReproduceResult(result.command, result.return_code,
                                      result.time_executed, result.output)
Ejemplo n.º 10
0
    def reproduce(self, target_path, input_path, arguments, max_time):
        """Reproduce a crash given an input.

    Args:
      target_path: Path to the target.
      input_path: Path to the reproducer input.
      arguments: Additional arguments needed for reproduction.
      max_time: Maximum allowed time for the reproduction.

    Returns:
      A ReproduceResult.
    """
        del arguments
        del max_time
        config = launcher.AflConfig.from_target_path(target_path)
        input_directory = None  # Not required for reproduction.
        runner = launcher.prepare_runner(target_path, config, input_path,
                                         input_directory)

        reproduce_result = _run_single_testcase(runner, input_path)

        command = reproduce_result.command
        return_code = reproduce_result.return_code
        time_executed = reproduce_result.time_executed
        output = runner.fuzzer_stderr

        return engine.ReproduceResult(command, return_code, time_executed,
                                      output)
Ejemplo n.º 11
0
    def minimize(self, minimize_args: List[str]) -> engine.ReproduceResult:
        """Minimizing crash testcase.
    Args:
      minimize_args: list of arguments to be passed to syz-repro.
    """
        logs.log(
            'Running Syzkaller Minimization (syz-repro) against testcase.')
        additional_args = minimize_args.copy()
        result = self.run_and_wait(additional_args)

        if result.return_code:
            logs.log('Successfully minimized crash.')
        else:
            logs.log('Failed to minimize crash.')
        logs.log('Syzkaller minimize testcase stopped.')

        return engine.ReproduceResult(result.command, result.return_code,
                                      result.time_executed, result.output)
Ejemplo n.º 12
0
    def reproduce(self, target_path, input_path, arguments, max_time):  # pylint: disable=unused-argument
        """Reproduce a crash given an input.

    Args:
      target_path: Path to the target.
      input_path: Path to the reproducer input.
      arguments: Additional arguments needed for reproduction.
      max_time: Maximum allowed time for the reproduction.

    Returns:
      A ReproduceResult.
    """
        os.chmod(target_path, 0o775)
        runner = new_process.UnicodeProcessRunner(target_path)
        with open(input_path) as f:
            result = runner.run_and_wait(timeout=max_time, stdin=f)

        return engine.ReproduceResult(result.command, result.return_code,
                                      result.time_executed, result.output)
Ejemplo n.º 13
0
    def repro(self, repro_timeout, repro_args):
        """This is where crash repro'ing is done.
    Args:
      repro_timeout: The maximum time in seconds that repro job is allowed
          to run for.
      repro_args: A sequence of arguments to be passed to the executable.
    """
        logs.log('Running Syzkaller testcase.')
        additional_args = copy.copy(repro_args)
        result = self.run_and_wait(additional_args, timeout=repro_timeout)
        result.return_code = self._crash_was_reproducible(result.output)

        if result.return_code:
            logs.log('Successfully reproduced crash.')
        else:
            logs.log('Failed to reproduce crash.')
        logs.log('Syzkaller repro testcase stopped.')
        return engine.ReproduceResult(result.command, result.return_code,
                                      result.time_executed, result.output)
Ejemplo n.º 14
0
    def test_test_for_crash_with_retries_greybox_legacy(self):
        """Test test_for_crash_with_retries reproducing a legacy crash (greybox)."""
        mock_engine = mock.Mock()
        mock_engine.reproduce.side_effect = [
            engine.ReproduceResult(['cmd'], 1, 1, 'crash'),
        ]
        self.mock.get.return_value = mock_engine

        with open('/flags-testcase', 'w', encoding='utf-8') as f:
            f.write('%TESTCASE% target -arg1 -arg2')

        testcase_manager.test_for_crash_with_retries(self.greybox_testcase,
                                                     '/fuzz-testcase', 10)
        mock_engine.reproduce.assert_has_calls([
            mock.call('/build_dir/target', '/fuzz-testcase',
                      ['-arg1', '-arg2'], 120),
        ])
        self.mock.log.assert_has_calls([
            mock.call('Crash occurred in 1 seconds (round 1). State:\nstate',
                      output=self.GREYBOX_FUZZER_CRASH),
            mock.call('Crash stacktrace is similar to original stacktrace.')
        ])
Ejemplo n.º 15
0
def engine_reproduce(engine_impl, target_name, testcase_path, arguments,
                     timeout):
    """Run engine reproduce on untrusted worker."""
    rebased_testcase_path = file_host.rebase_to_worker_root(testcase_path)
    file_host.copy_file_to_worker(testcase_path, rebased_testcase_path)

    request = untrusted_runner_pb2.EngineReproduceRequest(
        engine=engine_impl.name,
        target_name=target_name,
        testcase_path=rebased_testcase_path,
        arguments=arguments,
        timeout=timeout)

    try:
        response = host.stub().EngineReproduce(request)
    except grpc.RpcError as e:
        if 'TargetNotFoundError' in repr(e):
            # Resurface the right exception.
            raise testcase_manager.TargetNotFoundError(
                'Failed to find target ' + target_name)
        raise

    return engine.ReproduceResult(list(response.command), response.return_code,
                                  response.time_executed, response.output)
Ejemplo n.º 16
0
from clusterfuzz.fuzz import engine

import clusterfuzz_deployment
import fuzz_target
import test_helpers
import workspace_utils

# NOTE: This integration test relies on
# https://github.com/google/oss-fuzz/tree/master/projects/example project.
EXAMPLE_PROJECT = 'example'

# An example fuzzer that triggers an error.
EXAMPLE_FUZZER = 'example_crash_fuzzer'

# Mock return values for engine_impl.reproduce.
EXECUTE_SUCCESS_RESULT = engine.ReproduceResult([], 0, 0, '')
EXECUTE_FAILURE_RESULT = engine.ReproduceResult([], 1, 0, '')

TEST_DATA_PATH = os.path.join(os.path.dirname(__file__), 'test_data')


def _create_config(**kwargs):
    """Creates a config object and then sets every attribute that is a key in
  |kwargs| to the corresponding value. Asserts that each key in |kwargs| is an
  attribute of Config."""
    defaults = {
        'cfl_platform': 'github',
        'oss_fuzz_project_name': EXAMPLE_PROJECT,
        'workspace': '/workspace'
    }
    for default_key, default_value in defaults.items():