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.'), ])
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(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.") ])
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: raise TimeoutError('Cleanse timed out\n' + result.output) return engine.ReproduceResult(result.command, result.return_code, result.time_executed, result.output)
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)
def reproduce(self, target_path, input_path, arguments, max_time): """Reproduce a crash given an input. Args: target_path: Path to the fuzzer script or binary. input_path: Path to the reproducer input. arguments: Additional arguments needed for reproduction. max_time: Maximum allowed time for the reproduction. Returns: A ReproduceResult. """ # 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() args = _get_arguments(app_path, app_args) args.append(f'--testcase_path={input_path}') result = _run_with_interpreter_if_needed(fuzzer_path, args, max_time) return engine.ReproduceResult(result.command, result.return_code, result.time_executed, result.output)
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)
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. """ 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)
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)
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)
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)
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') 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.') ])
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)