From f460c03c8a4d035b2f857d23351545408d4afaa6 Mon Sep 17 00:00:00 2001 From: Oliver Chang Date: Wed, 27 Oct 2021 12:24:26 +1100 Subject: [PATCH] cifuzz: Fix artifacts uploading issues. (#6646) --- infra/cifuzz/fuzz_target.py | 70 +++++++++++++++++++++----------- infra/cifuzz/fuzz_target_test.py | 20 +++++++++ infra/cifuzz/run_fuzzers.py | 18 -------- infra/cifuzz/run_fuzzers_test.py | 20 --------- 4 files changed, 67 insertions(+), 61 deletions(-) diff --git a/infra/cifuzz/fuzz_target.py b/infra/cifuzz/fuzz_target.py index c42d160cc..8c62a64ab 100644 --- a/infra/cifuzz/fuzz_target.py +++ b/infra/cifuzz/fuzz_target.py @@ -17,12 +17,14 @@ import logging import os import shutil import stat +import tempfile import clusterfuzz.environment import clusterfuzz.fuzz import config_utils import logs +import stack_parser logs.init() @@ -106,6 +108,24 @@ class FuzzTarget: # pylint: disable=too-many-instance-attributes self.latest_corpus_path) return self.latest_corpus_path + def _target_artifact_path(self): + """Target artifact path.""" + artifact_path = os.path.join(self.workspace.artifacts, self.target_name, + self.config.sanitizer) + os.makedirs(artifact_path, exist_ok=True) + return artifact_path + + def _save_crash(self, crash): + """Add stacktraces to crashes.""" + target_reproducer_path = os.path.join(self._target_artifact_path(), + os.path.basename(crash.input_path)) + shutil.copy(crash.input_path, target_reproducer_path) + + bug_summary_artifact_path = target_reproducer_path + '.summary' + stack_parser.parse_fuzzer_output(crash.stacktrace, + bug_summary_artifact_path) + return target_reproducer_path + def prune(self): """Prunes the corpus and returns the result.""" self._download_corpus() @@ -117,7 +137,7 @@ class FuzzTarget: # pylint: disable=too-many-instance-attributes result = engine_impl.minimize_corpus(self.target_path, [], [self.latest_corpus_path], self.pruned_corpus_path, - self.workspace.artifacts, + self._target_artifact_path(), self.duration) return FuzzResult(None, result.logs, self.pruned_corpus_path) @@ -134,32 +154,36 @@ class FuzzTarget: # pylint: disable=too-many-instance-attributes corpus_path = self.latest_corpus_path logging.info('Starting fuzzing') - with clusterfuzz.environment.Environment(config_utils.DEFAULT_ENGINE, - self.config.sanitizer, - self.target_path, - interactive=True) as env: - engine_impl = clusterfuzz.fuzz.get_engine(config_utils.DEFAULT_ENGINE) - options = engine_impl.prepare(corpus_path, env.target_path, env.build_dir) - options.merge_back_new_testcases = False - options.analyze_dictionary = False - options.arguments.extend(LIBFUZZER_OPTIONS) + with tempfile.TemporaryDirectory() as artifacts_dir: + with clusterfuzz.environment.Environment(config_utils.DEFAULT_ENGINE, + self.config.sanitizer, + self.target_path, + interactive=True) as env: + engine_impl = clusterfuzz.fuzz.get_engine(config_utils.DEFAULT_ENGINE) + options = engine_impl.prepare(corpus_path, env.target_path, + env.build_dir) + options.merge_back_new_testcases = False + options.analyze_dictionary = False + options.arguments.extend(LIBFUZZER_OPTIONS) - result = engine_impl.fuzz(self.target_path, options, - self.workspace.artifacts, self.duration) + result = engine_impl.fuzz(self.target_path, options, artifacts_dir, + self.duration) - # Libfuzzer timeout was reached. - if not result.crashes: - logging.info('Fuzzer %s finished with no crashes discovered.', - self.target_name) - return FuzzResult(None, None, self.latest_corpus_path) + # Libfuzzer timeout was reached. + if not result.crashes: + logging.info('Fuzzer %s finished with no crashes discovered.', + self.target_name) + return FuzzResult(None, None, self.latest_corpus_path) - # Only report first crash. - crash = result.crashes[0] - logging.info('Fuzzer: %s. Detected bug.', self.target_name) + # Only report first crash. + crash = result.crashes[0] + logging.info('Fuzzer: %s. Detected bug:\n%s', self.target_name, + crash.stacktrace) - if self.is_crash_reportable(crash.input_path): - # We found a bug in the fuzz target and we will report it. - return FuzzResult(crash.input_path, result.logs, self.latest_corpus_path) + if self.is_crash_reportable(crash.input_path): + # We found a bug in the fuzz target and we will report it. + saved_path = self._save_crash(crash) + return FuzzResult(saved_path, result.logs, self.latest_corpus_path) # We found a bug but we won't report it. return FuzzResult(None, None, self.latest_corpus_path) diff --git a/infra/cifuzz/fuzz_target_test.py b/infra/cifuzz/fuzz_target_test.py index ecea6fbbf..33d58a497 100644 --- a/infra/cifuzz/fuzz_target_test.py +++ b/infra/cifuzz/fuzz_target_test.py @@ -220,5 +220,25 @@ class IsCrashReportableTest(fake_filesystem_unittest.TestCase): 'introduced.') +class FuzzTest(fake_filesystem_unittest.TestCase): + """Fuzz test.""" + + def setUp(self): + """Sets up example fuzz target to test is_reproducible method.""" + self.setUpPyfakefs() + deployment = _create_deployment() + config = deployment.config + workspace = deployment.workspace + self.fuzz_target = fuzz_target.FuzzTarget('/path/fuzz-target', 10, + workspace, deployment, config) + + def test_get_fuzz_target_artifact(self): + """Tests that get_fuzz_target_artifact works as intended.""" + # pylint: disable=protected-access + fuzz_target_artifact = self.fuzz_target._target_artifact_path() + self.assertEqual('/workspace/out/artifacts/fuzz-target/address', + fuzz_target_artifact) + + if __name__ == '__main__': unittest.main() diff --git a/infra/cifuzz/run_fuzzers.py b/infra/cifuzz/run_fuzzers.py index f59ef4f89..ef468b9c6 100644 --- a/infra/cifuzz/run_fuzzers.py +++ b/infra/cifuzz/run_fuzzers.py @@ -15,14 +15,12 @@ import enum import logging import os -import shutil import sys import time import clusterfuzz_deployment import fuzz_target import generate_coverage_report -import stack_parser import workspace_utils # pylint: disable=wrong-import-position,import-error @@ -106,13 +104,6 @@ class BaseFuzzTargetRunner: bug is found.""" raise NotImplementedError('Child class must implement method.') - def get_fuzz_target_artifact(self, target, artifact_name): - """Returns the path of a fuzzing artifact named |artifact_name| for - |fuzz_target|.""" - artifact_name = (f'{target.target_name}-{self.config.sanitizer}-' - f'{artifact_name}') - return os.path.join(self.workspace.artifacts, artifact_name) - def create_fuzz_target_obj(self, target_path, run_seconds): """Returns a fuzz target object.""" return fuzz_target.FuzzTarget(target_path, run_seconds, self.workspace, @@ -149,15 +140,6 @@ class BaseFuzzTargetRunner: target.target_name) continue - # TODO(metzman): Do this with filestore. - testcase_artifact_path = self.get_fuzz_target_artifact( - target, os.path.basename(result.testcase)) - shutil.move(result.testcase, testcase_artifact_path) - bug_summary_artifact_path = self.get_fuzz_target_artifact( - target, 'bug-summary.txt') - stack_parser.parse_fuzzer_output(result.stacktrace, - bug_summary_artifact_path) - bug_found = True if self.quit_on_bug_found: logging.info('Bug found. Stopping fuzzing.') diff --git a/infra/cifuzz/run_fuzzers_test.py b/infra/cifuzz/run_fuzzers_test.py index 1e561c2bb..4f65a8bfd 100644 --- a/infra/cifuzz/run_fuzzers_test.py +++ b/infra/cifuzz/run_fuzzers_test.py @@ -211,24 +211,6 @@ class BaseFuzzTargetRunnerTest(unittest.TestCase): out_path) self._test_initialize_fail(expected_error_args, workspace=tmp_dir) - def test_get_fuzz_target_artifact(self): - """Tests that get_fuzz_target_artifact works as intended.""" - with tempfile.TemporaryDirectory() as tmp_dir: - runner = self._create_runner(workspace=tmp_dir) - crashes_dir = 'crashes-dir' - runner.crashes_dir = crashes_dir - artifact_name = 'artifact-name' - target = mock.MagicMock() - target_name = 'target_name' - target.target_name = target_name - - fuzz_target_artifact = runner.get_fuzz_target_artifact( - target, artifact_name) - expected_fuzz_target_artifact = os.path.join( - tmp_dir, 'out', 'artifacts', 'target_name-address-artifact-name') - - self.assertEqual(fuzz_target_artifact, expected_fuzz_target_artifact) - class CiFuzzTargetRunnerTest(fake_filesystem_unittest.TestCase): """Tests that CiFuzzTargetRunner works as intended.""" @@ -264,8 +246,6 @@ class CiFuzzTargetRunnerTest(fake_filesystem_unittest.TestCase): magic_mock.target_name = 'target1' mock_create_fuzz_target_obj.return_value = magic_mock self.assertTrue(runner.run_fuzz_targets()) - self.assertIn('target1-address-testcase', - os.listdir(runner.workspace.artifacts)) self.assertEqual(mock_run_fuzz_target.call_count, 1)