def test_reproduce(self): """Test reproduce.""" testcase_file_path = os.path.join(self.temp_dir, 'testcase') with open(testcase_file_path, 'wb') as f: f.write(b'EEE') self._setup_env(job_type='libfuzzer_asan_job') build_manager.setup_build() result = testcase_manager.engine_reproduce( libfuzzer_engine.LibFuzzerEngine(), 'test_fuzzer', testcase_file_path, [], 30) self.assertEqual([ os.path.join(environment.get_value('BUILD_DIR'), 'test_fuzzer'), '-runs=100', file_host.rebase_to_worker_root(testcase_file_path) ], result.command) self.assertEqual(result.return_code, libfuzzer_constants.TARGET_ERROR_EXITCODE) self.assertGreater(result.time_executed, 0) self.assertIn('Running 1 inputs 100 time(s) each', result.output) self.assertIn( 'AddressSanitizer: SEGV on unknown address 0x000000000000', result.output)
def test_exit_failure_logged(self): """Test that we log when libFuzzer's exit code indicates it ran into an error.""" test_helpers.patch(self, [ 'metrics.logs.log_error', ]) def mocked_log_error(*args, **kwargs): # pylint: disable=unused-argument self.assertIn(engine.ENGINE_ERROR_MESSAGE, args[0]) self.mock.log_error.side_effect = mocked_log_error _, corpus_path = setup_testcase_and_corpus('empty', 'corpus_with_some_files') target_path = engine_common.find_fuzzer_path(DATA_DIR, 'exit_fuzzer') engine_impl = engine.LibFuzzerEngine() options = engine_impl.prepare(corpus_path, target_path, DATA_DIR) options.extra_env['EXIT_FUZZER_CODE'] = '1' results = engine_impl.fuzz(target_path, options, TEMP_DIR, 10) self.assertEqual(1, self.mock.log_error.call_count) self.assertEqual(1, len(results.crashes)) self.assertEqual(fuzzer_utils.get_temp_dir(), os.path.dirname(results.crashes[0].input_path)) self.assertEqual(0, os.path.getsize(results.crashes[0].input_path))
def test_fuzzer_can_boot_and_run_with_corpus(self): """Tests running a single round of fuzzing on a Fuchsia target, using a toy fuzzer that should crash very quickly. Additionally, tests that pushing a corpus to the target works & produces an expanded corpus.""" environment.set_value("JOB_NAME", "libfuzzer_asan_fuchsia") environment.set_value("FUZZ_TARGET", "example_fuzzers/trap_fuzzer") build_manager.setup_build() _, corpus_path = setup_testcase_and_corpus("aaaa", "fuchsia_corpus") num_files_original = len([corpfile for corpfile in os.listdir(corpus_path)]) engine_impl = engine.LibFuzzerEngine() self.mock.get_fuzz_timeout.return_value = get_fuzz_timeout(20.0) options = engine_impl.prepare(corpus_path, "example_fuzzers/trap_fuzzer", DATA_DIR) results = engine_impl.fuzz("example_fuzzers/trap_fuzzer", options, TEMP_DIR, 20) # If we don't get a crash, something went wrong. self.assertIn("Test unit written to", results.logs) # Check that the command was invoked with a corpus argument. self.assertIn("data/corpus/new", results.command) # Check that new units were added to the corpus. num_files_new = len([ corpfile for corpfile in os.listdir(os.path.join(TEMP_DIR, "temp-1337/new")) ]) self.assertGreater(num_files_new, num_files_original)
def test_prepare(self): """Test prepare.""" engine_impl = engine.LibFuzzerEngine() options = engine_impl.prepare("/corpus_dir", "/path/target", "/path") self.assertEqual("/corpus_dir", options.corpus_dir) self.assertItemsEqual( [ "-max_len=31337", "-timeout=11", "-rss_limit_mb=2560", "-arg1", "-dict=/path/blah.dict", ], options.arguments, ) self.assertDictEqual({ "value_profile": 1, "corpus_subset": 20, "fork": 2 }, options.strategies) self.assertItemsEqual(["/new_corpus_dir", "/corpus_dir"], options.fuzz_corpus_dirs) self.assertDictEqual({"extra_env": "1"}, options.extra_env) self.assertFalse(options.use_dataflow_tracing) self.assertTrue(options.is_mutations_run) self.mock.unpack_seed_corpus_if_needed.assert_called_with( "/path/target", "/corpus_dir")
def test_single_testcase_crash(self): """Tests libfuzzer with a crashing testcase.""" testcase_path, _ = setup_testcase_and_corpus("crash", "empty_corpus") engine_impl = engine.LibFuzzerEngine() target_path = engine_common.find_fuzzer_path(ANDROID_DATA_DIR, "test_fuzzer") result = engine_impl.reproduce(target_path, testcase_path, ["-timeout=60", "-rss_limit_mb=2560"], 65) self.assertEqual( [ self.adb_path, "shell", self.hwasan_options, self.device_path(target_path), "-timeout=60", "-rss_limit_mb=2560", "-runs=100", self.device_path(testcase_path), ], result.command, ) self.assertIn( "ERROR: HWAddressSanitizer: SEGV on unknown address 0x000000000000", result.output, )
def test_fuzz_from_subset(self): """Tests fuzzing from corpus subset.""" self.mock.generate_weighted_strategy_pool.return_value = set_strategy_pool( [strategy.CORPUS_SUBSET_STRATEGY]) self.mock.get_fuzz_timeout.return_value = get_fuzz_timeout(5.0) _, corpus_path = setup_testcase_and_corpus("empty", "corpus_with_some_files") engine_impl = engine.LibFuzzerEngine() target_path = engine_common.find_fuzzer_path(DATA_DIR, "test_fuzzer") dict_path = target_path + ".dict" options = engine_impl.prepare(corpus_path, target_path, DATA_DIR) results = engine_impl.fuzz(target_path, options, TEMP_DIR, 10) self.compare_arguments( os.path.join(DATA_DIR, "test_fuzzer"), [ "-max_len=256", "-timeout=25", "-rss_limit_mb=2560", "-dict=" + dict_path, "-artifact_prefix=" + TEMP_DIR + "/", "-max_total_time=5", "-print_final_stats=1", ], [ os.path.join(TEMP_DIR, "temp-1337/new"), os.path.join(TEMP_DIR, "temp-1337/subset"), ], results.command, ) self.assert_has_stats(results.stats)
def test_fuzz_no_crash(self): """Tests fuzzing (no crash).""" self.mock.generate_weighted_strategy_pool.return_value = set_strategy_pool( [strategy.VALUE_PROFILE_STRATEGY]) self.mock.get_fuzz_timeout.return_value = get_fuzz_timeout(5.0) _, corpus_path = setup_testcase_and_corpus('empty', 'corpus') engine_impl = engine.LibFuzzerEngine() target_path = engine_common.find_fuzzer_path(DATA_DIR, 'test_fuzzer') options = engine_impl.prepare(corpus_path, target_path, DATA_DIR) results = engine_impl.fuzz(target_path, options, TEMP_DIR, 10) self.assert_has_stats(results.stats) self.compare_arguments(os.path.join(DATA_DIR, 'test_fuzzer'), [ '-max_len=256', '-timeout=25', '-rss_limit_mb=2048', '-use_value_profile=1', '-artifact_prefix=' + TEMP_DIR + '/', '-max_total_time=5', '-print_final_stats=1' ], [ os.path.join(TEMP_DIR, 'temp-1337/new'), os.path.join(TEMP_DIR, 'corpus') ], results.command) self.assertEqual(0, len(results.crashes)) # New items should've been added to the corpus. self.assertNotEqual(0, len(os.listdir(corpus_path)))
def test_qemu_logs_returned_on_error(self): """Test running against a qemu that has died""" test_helpers.patch(self, ["metrics.logs.log_warn"]) # Pass-through logs just so we can see what's going on (but moving from # log_warn to plain log to avoid creating a loop) self.mock.log_warn.side_effect = logs.log environment.set_value("FUZZ_TARGET", "example_fuzzers/overflow_fuzzer") environment.set_value("JOB_NAME", "libfuzzer_asan_fuchsia") build_manager.setup_build() testcase_path, _ = setup_testcase_and_corpus("fuchsia_crash", "empty_corpus") runner = libfuzzer.FuchsiaQemuLibFuzzerRunner("fake/fuzzer") # Check that it's up properly self.assertEqual(runner.device.ssh(["echo", "hello"]), 0) # Force shutdown runner.device.ssh(["dm", "shutdown"]) # Try to fuzz against the dead qemu to trigger automatic recovery behavior engine_impl = engine.LibFuzzerEngine() engine_impl.reproduce( "example_fuzzers/overflow_fuzzer", testcase_path, ["-timeout=25", "-rss_limit_mb=2560"], 30, ) # Check the logs for the shutdown sequence self.assertIn("Shutting down", self.mock.log_warn.call_args[0][0])
def test_qemu_logs_returned_on_error(self): """Test running against a qemu that has died""" test_helpers.patch(self, ['metrics.logs.log_warn']) # Pass-through logs just so we can see what's going on (but moving from # log_warn to plain log to avoid creating a loop) self.mock.log_warn.side_effect = logs.log environment.set_value('FUZZ_TARGET', 'example_fuzzers/overflow_fuzzer') environment.set_value('JOB_NAME', 'libfuzzer_asan_fuchsia') build_manager.setup_build() testcase_path, _ = setup_testcase_and_corpus('fuchsia_crash', 'empty_corpus') runner = libfuzzer.FuchsiaQemuLibFuzzerRunner('fake/fuzzer') # Check that it's up properly self.assertEqual(runner.device.ssh(['echo', 'hello']), 0) # Force shutdown runner.device.ssh(['dm', 'shutdown']) # Try to fuzz against the dead qemu to trigger automatic recovery behavior engine_impl = engine.LibFuzzerEngine() engine_impl.reproduce('example_fuzzers/overflow_fuzzer', testcase_path, ['-timeout=25', '-rss_limit_mb=2048'], 30) # Check the logs for the shutdown sequence self.assertIn('Shutting down', self.mock.log_warn.call_args[0][0])
def test_fuzz_from_subset(self): """Tests fuzzing from corpus subset.""" self.mock.generate_weighted_strategy_pool.return_value = set_strategy_pool( [strategy.CORPUS_SUBSET_STRATEGY]) self.mock.get_fuzz_timeout.return_value = get_fuzz_timeout(5.0) _, corpus_path = setup_testcase_and_corpus('empty', 'corpus_with_some_files') engine_impl = engine.LibFuzzerEngine() target_path = engine_common.find_fuzzer_path(DATA_DIR, 'test_fuzzer') dict_path = target_path + '.dict' options = engine_impl.prepare(corpus_path, target_path, DATA_DIR) results = engine_impl.fuzz(target_path, options, TEMP_DIR, 10) self.compare_arguments(os.path.join(DATA_DIR, 'test_fuzzer'), [ '-max_len=256', '-timeout=25', '-rss_limit_mb=2048', '-dict=' + dict_path, '-artifact_prefix=' + TEMP_DIR + '/', '-max_total_time=5', '-print_final_stats=1' ], [ os.path.join(TEMP_DIR, 'temp-1337/new'), os.path.join(TEMP_DIR, 'temp-1337/subset') ], results.command) self.assert_has_stats(results.stats)
def test_analyze_dict(self): """Tests recommended dictionary analysis.""" test_helpers.patch( self, [ "bot.fuzzers.dictionary_manager.DictionaryManager." "parse_recommended_dictionary_from_log_lines", "bot.fuzzers.dictionary_manager.DictionaryManager." "update_recommended_dictionary", ], ) self.mock.parse_recommended_dictionary_from_log_lines.return_value = {'"USELESS_0"', '"APPLE"', '"USELESS_1"', '"GINGER"', '"USELESS_2"', '"BEET"', '"USELESS_3"',} self.mock.get_fuzz_timeout.return_value = get_fuzz_timeout(5.0) _, corpus_path = setup_testcase_and_corpus("empty", "corpus_with_some_files") engine_impl = engine.LibFuzzerEngine() target_path = engine_common.find_fuzzer_path(DATA_DIR, "analyze_dict_fuzzer") options = engine_impl.prepare(corpus_path, target_path, DATA_DIR) engine_impl.fuzz(target_path, options, TEMP_DIR, 10) expected_recommended_dictionary = {'"APPLE"', '"GINGER"', '"BEET"'} self.assertIn( expected_recommended_dictionary, self.mock.update_recommended_dictionary.call_args[0], )
def test_fuzz_with_mutator_plugin(self): """Tests fuzzing with a mutator plugin.""" self.mock.get_fuzz_timeout.return_value = get_fuzz_timeout(5.0) os.environ["MUTATOR_PLUGINS_DIR"] = os.path.join(TEMP_DIR, "mutator-plugins") # TODO(metzman): Remove the old binary and switch the test to the new one. fuzz_target_name = "test_fuzzer_old" plugin_archive_name = "custom_mutator_plugin-libfuzzer_asan-test_fuzzer_old.zip" # Call before setting up the plugin since this call will erase the directory # the plugin is written to. _, corpus_path = setup_testcase_and_corpus("empty", "empty_corpus") plugin_archive_path = os.path.join(DATA_DIR, plugin_archive_name) self.mock.generate_weighted_strategy_pool.return_value = set_strategy_pool( [strategy.MUTATOR_PLUGIN_STRATEGY]) self.mock._get_mutator_plugins_from_bucket.return_value = [ # pylint: disable=protected-access plugin_archive_name ] self.mock._download_mutator_plugin_archive.return_value = ( # pylint: disable=protected-access plugin_archive_path) custom_mutator_print_string = "CUSTOM MUTATOR\n" try: target_path = engine_common.find_fuzzer_path(DATA_DIR, fuzz_target_name) engine_impl = engine.LibFuzzerEngine() options = engine_impl.prepare(corpus_path, target_path, DATA_DIR) results = engine_impl.fuzz(target_path, options, TEMP_DIR, 10) finally: shutil.rmtree(os.environ["MUTATOR_PLUGINS_DIR"]) # custom_mutator_print_string gets printed before the custom mutator mutates # a test case. Assert that the count is greater than 1 to ensure that the # function didn't crash on its first execution (after printing). self.assertGreater(results.logs.count(custom_mutator_print_string), 1)
def test_fuzz_from_subset(self): """Tests fuzzing from corpus subset.""" self.mock.generate_weighted_strategy_pool.return_value = set_strategy_pool( [strategy.CORPUS_SUBSET_STRATEGY]) self.mock.get_fuzz_timeout.return_value = get_fuzz_timeout(5.0) _, corpus_path = setup_testcase_and_corpus('empty', 'corpus_with_some_files') engine_impl = engine.LibFuzzerEngine() target_path = engine_common.find_fuzzer_path(ANDROID_DATA_DIR, 'test_fuzzer') dict_path = target_path + '.dict' options = engine_impl.prepare(corpus_path, target_path, ANDROID_DATA_DIR) results = engine_impl.fuzz(target_path, options, TEMP_DIR, 10) self.assertEqual([ self.adb_path, 'shell', self.hwasan_options, self.device_path(target_path), '-max_len=256', '-timeout=25', '-rss_limit_mb=2048', '-dict=' + self.device_path(dict_path), '-artifact_prefix=' + self.device_path(TEMP_DIR) + '/', '-max_total_time=5', '-print_final_stats=1', self.device_path(os.path.join(TEMP_DIR, 'temp-1337/new')), self.device_path(os.path.join(TEMP_DIR, 'temp-1337/subset')), ], results.command) self.assertTrue(android.adb.file_exists(self.device_path(dict_path))) self.assert_has_stats(results.stats)
def test_merge_reductions(self): """Tests that reduced testcases are merged back into the original corpus without deleting the larger version.""" self.mock.get_fuzz_timeout.return_value = get_fuzz_timeout(1.0) _, corpus_path = setup_testcase_and_corpus('empty', 'empty_corpus') fuzz_target_name = 'analyze_dict_fuzzer' test_helpers.patch(self, [ 'bot.fuzzers.libFuzzer.engine.LibFuzzerEngine._create_merge_corpus_dir', 'system.shell.get_directory_file_count', ]) self.mock.get_directory_file_count.side_effect = ( mock_get_directory_file_count) minimal_unit_contents = 'APPLE' minimal_unit_hash = '569bea285d70dda2218f89ef5454ea69fb5111ef' nonminimal_unit_contents = 'APPLEO' nonminimal_unit_hash = '540d9ba6239483d60cd7448a3202b96c90409186' def mocked_create_merge_directory(_): """A mocked version of create_merge_directory that adds some interesting files to the merge corpus and initial corpus.""" merge_directory_path = launcher.create_corpus_directory( 'merge-corpus') shell.create_directory(merge_directory_path, create_intermediates=True, recreate=True) # Write the minimal unit to the merge directory. minimal_unit_path = os.path.join(merge_directory_path, minimal_unit_hash) with open(minimal_unit_path, 'w+') as file_handle: file_handle.write(minimal_unit_contents) # Write the nonminimal unit to the corpus directory. nonminimal_unit_path = os.path.join(corpus_path, nonminimal_unit_hash) with open(nonminimal_unit_path, 'w+') as file_handle: file_handle.write(nonminimal_unit_contents) return merge_directory_path # pylint: disable=protected-access self.mock._create_merge_corpus_dir.side_effect = ( mocked_create_merge_directory) target_path = engine_common.find_fuzzer_path(DATA_DIR, fuzz_target_name) engine_impl = engine.LibFuzzerEngine() options = engine_impl.prepare(corpus_path, target_path, DATA_DIR) options.arguments.append('-runs=10') engine_impl.fuzz(target_path, options, TEMP_DIR, 10) # Verify that both the newly found minimal testcase and the nonminimal # testcase are in the corpus. self.assertIn(minimal_unit_hash, os.listdir(corpus_path)) self.assertIn(nonminimal_unit_hash, os.listdir(corpus_path))
def test_single_testcase_crash(self): """Tests launcher with a crashing testcase.""" testcase_path, _ = setup_testcase_and_corpus('crash', 'empty_corpus') engine_impl = engine.LibFuzzerEngine() target_path = engine_common.find_fuzzer_path(DATA_DIR, 'test_fuzzer') result = engine_impl.reproduce(target_path, testcase_path, [], 30) self.assertIn( 'ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000', result.output)
def test_merge_reductions(self): """Tests that reduced testcases are merged back into the original corpus without deleting the larger version.""" self.mock.get_fuzz_timeout.return_value = get_fuzz_timeout(1.0) _, corpus_path = setup_testcase_and_corpus("empty", "empty_corpus") fuzz_target_name = "analyze_dict_fuzzer" test_helpers.patch( self, [ "bot.fuzzers.libFuzzer.engine.LibFuzzerEngine._create_merge_corpus_dir", "system.shell.get_directory_file_count", ], ) self.mock.get_directory_file_count.side_effect = mock_get_directory_file_count minimal_unit_contents = "APPLE" minimal_unit_hash = "569bea285d70dda2218f89ef5454ea69fb5111ef" nonminimal_unit_contents = "APPLEO" nonminimal_unit_hash = "07aef0e305db0779f3b52ab4dad975a1b737c461" def mocked_create_merge_directory(_): """A mocked version of create_merge_directory that adds some interesting files to the merge corpus and initial corpus.""" merge_directory_path = libfuzzer.create_corpus_directory("merge-corpus") # Write the minimal unit to the new corpus directory. new_corpus_directory_path = libfuzzer.create_corpus_directory("new") minimal_unit_path = os.path.join(new_corpus_directory_path, minimal_unit_hash) with open(minimal_unit_path, "w+") as file_handle: file_handle.write(minimal_unit_contents) # Write the nonminimal unit to the corpus directory. nonminimal_unit_path = os.path.join(corpus_path, nonminimal_unit_hash) with open(nonminimal_unit_path, "w+") as file_handle: file_handle.write(nonminimal_unit_contents) return merge_directory_path # pylint: disable=protected-access self.mock._create_merge_corpus_dir.side_effect = mocked_create_merge_directory target_path = engine_common.find_fuzzer_path(DATA_DIR, fuzz_target_name) engine_impl = engine.LibFuzzerEngine() options = engine_impl.prepare(corpus_path, target_path, DATA_DIR) options.arguments.append("-runs=10") engine_impl.fuzz(target_path, options, TEMP_DIR, 10) # Verify that both the newly found minimal testcase and the nonminimal # testcase are in the corpus. self.assertIn(minimal_unit_hash, os.listdir(corpus_path)) self.assertIn(nonminimal_unit_hash, os.listdir(corpus_path))
def test_prepare_auto_add_dict(self): """Test prepare automatically adding dict argument.""" with open('/path/target.options', 'w') as f: f.write('[libfuzzer]\n' 'max_len=31337\n' 'timeout=11\n') self.fs.create_file('/path/target.dict') engine_impl = engine.LibFuzzerEngine() options = engine_impl.prepare('/corpus_dir', '/path/target', '/path') self.assertItemsEqual([ '-max_len=31337', '-timeout=11', '-rss_limit_mb=2048', '-arg1', '-dict=/path/target.dict' ], options.arguments)
def test_fuzzer_can_boot_and_run_reproducer(self): """Tests running a testcase that should cause a fast, predictable crash.""" build_manager.setup_fuchsia_build() testcase_path, _ = setup_testcase_and_corpus('fuchsia_crash', 'empty_corpus') engine_impl = engine.LibFuzzerEngine() result = engine_impl.reproduce('example_fuzzers/toy_fuzzer', testcase_path, ['-timeout=25', '-rss_limit_mb=2048'], 30) self.assertIn('ERROR: AddressSanitizer: heap-buffer-overflow on address', result.output) self.assertIn('Running: data/fuchsia_crash', result.output)
def test_target_not_found(self): """Test target not found.""" testcase_file_path = os.path.join(self.temp_dir, 'testcase') with open(testcase_file_path, 'wb') as f: f.write(b'EEE') self._setup_env(job_type='libfuzzer_asan_job') build_manager.setup_build() with self.assertRaises(testcase_manager.TargetNotFoundError): testcase_manager.engine_reproduce( libfuzzer_engine.LibFuzzerEngine(), 'does_not_exist', testcase_file_path, [], 30)
def test_prepare_invalid_dict(self): """Test prepare with an invalid dict path.""" with open('/path/target.options', 'w') as f: f.write('[libfuzzer]\n' 'max_len=31337\n' 'timeout=11\n' 'dict=not_exist.dict\n') engine_impl = engine.LibFuzzerEngine() options = engine_impl.prepare('/corpus_dir', '/path/target', '/path') self.assertItemsEqual( ['-max_len=31337', '-timeout=11', '-rss_limit_mb=2048', '-arg1'], options.arguments)
def test_single_testcase_crash(self): """Tests libfuzzer with a crashing testcase.""" testcase_path, _ = setup_testcase_and_corpus('crash', 'empty_corpus') engine_impl = engine.LibFuzzerEngine() target_path = engine_common.find_fuzzer_path(DATA_DIR, 'test_fuzzer') result = engine_impl.reproduce(target_path, testcase_path, ['-timeout=60', '-rss_limit_mb=2560'], 65) self.compare_arguments( os.path.join(DATA_DIR, 'test_fuzzer'), ['-timeout=60', '-rss_limit_mb=2560', '-runs=100'], [testcase_path], result.command) self.assertIn( 'ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000', result.output)
def test_prepare_invalid_dict(self): """Test prepare with an invalid dict path.""" with open("/path/target.options", "w") as f: f.write("[libfuzzer]\n" "max_len=31337\n" "timeout=11\n" "dict=not_exist.dict\n") engine_impl = engine.LibFuzzerEngine() options = engine_impl.prepare("/corpus_dir", "/path/target", "/path") self.assertItemsEqual( ["-max_len=31337", "-timeout=11", "-rss_limit_mb=2560", "-arg1"], options.arguments, )
def test_fuzzer_can_boot_and_run_reproducer(self): """Tests running a testcase that should cause a fast, predictable crash.""" environment.set_value('FUZZ_TARGET', 'example-fuzzers/overflow_fuzzer') environment.set_value('JOB_NAME', 'libfuzzer_asan_fuchsia') build_manager.setup_build() testcase_path, _ = setup_testcase_and_corpus('fuchsia_crash', 'empty_corpus') engine_impl = engine.LibFuzzerEngine() result = engine_impl.reproduce('example-fuzzers/overflow_fuzzer', testcase_path, ['-timeout=25', '-rss_limit_mb=2560'], 30) self.assertIn('ERROR: AddressSanitizer: heap-buffer-overflow on address', result.output) self.assertIn('Running: data/fuchsia_crash', result.output)
def test_minimize(self): """Tests minimize.""" testcase_path, _ = setup_testcase_and_corpus('aaaa', 'empty_corpus') minimize_output_path = os.path.join(TEMP_DIR, 'minimized_testcase') engine_impl = engine.LibFuzzerEngine() target_path = engine_common.find_fuzzer_path(ANDROID_DATA_DIR, 'crash_with_A_fuzzer') result = engine_impl.minimize_testcase(target_path, [], testcase_path, minimize_output_path, 30) self.assertTrue(result) self.assertTrue(os.path.exists(minimize_output_path)) with open(minimize_output_path) as f: result = f.read() self.assertEqual('A', result)
def test_cleanse(self): """Tests cleanse.""" testcase_path, _ = setup_testcase_and_corpus('aaaa', 'empty_corpus') cleanse_output_path = os.path.join(TEMP_DIR, 'cleansed_testcase') engine_impl = engine.LibFuzzerEngine() target_path = engine_common.find_fuzzer_path(ANDROID_DATA_DIR, 'crash_with_A_fuzzer') result = engine_impl.cleanse(target_path, [], testcase_path, cleanse_output_path, 30) self.assertTrue(result) self.assertTrue(os.path.exists(cleanse_output_path)) with open(cleanse_output_path) as f: result = f.read() self.assertFalse(all(c == 'A' for c in result))
def test_cleanse(self): """Tests cleanse.""" testcase_path, _ = setup_testcase_and_corpus("aaaa", "empty_corpus") cleanse_output_path = os.path.join(TEMP_DIR, "cleansed_testcase") engine_impl = engine.LibFuzzerEngine() target_path = engine_common.find_fuzzer_path(DATA_DIR, "crash_with_A_fuzzer") result = engine_impl.cleanse(target_path, [], testcase_path, cleanse_output_path, 120) self.assertTrue(result) self.assertTrue(os.path.exists(cleanse_output_path)) with open(cleanse_output_path) as f: result = f.read() self.assertFalse(all(c == "A" for c in result))
def test_minimize(self): """Tests minimize.""" testcase_path, _ = setup_testcase_and_corpus("aaaa", "empty_corpus") minimize_output_path = os.path.join(TEMP_DIR, "minimized_testcase") engine_impl = engine.LibFuzzerEngine() target_path = engine_common.find_fuzzer_path(DATA_DIR, "crash_with_A_fuzzer") result = engine_impl.minimize_testcase(target_path, [], testcase_path, minimize_output_path, 120) self.assertTrue(result) self.assertTrue(os.path.exists(minimize_output_path)) with open(minimize_output_path) as f: result = f.read() self.assertEqual("A", result)
def test_fuzz_crash(self): """Tests fuzzing (crash).""" self.mock.get_fuzz_timeout.return_value = get_fuzz_timeout(5.0) _, corpus_path = setup_testcase_and_corpus("empty", "corpus") engine_impl = engine.LibFuzzerEngine() target_path = engine_common.find_fuzzer_path(ANDROID_DATA_DIR, "always_crash_fuzzer") options = engine_impl.prepare(corpus_path, target_path, ANDROID_DATA_DIR) results = engine_impl.fuzz(target_path, options, TEMP_DIR, 10) self.assert_has_stats(results.stats) self.assertEqual( [ self.adb_path, "shell", self.hwasan_options, self.device_path(target_path), "-max_len=100", "-timeout=25", "-rss_limit_mb=2560", "-artifact_prefix=" + self.device_path(TEMP_DIR) + "/", "-max_total_time=5", "-print_final_stats=1", self.device_path(os.path.join(TEMP_DIR, "temp-1337/new")), self.device_path(os.path.join(TEMP_DIR, "corpus")), ], results.command, ) self.assertEqual(1, len(results.crashes)) self.assertTrue(os.path.exists(results.crashes[0].input_path)) self.assertEqual(TEMP_DIR, os.path.dirname(results.crashes[0].input_path)) self.assertEqual(results.logs, results.crashes[0].stacktrace) self.assertListEqual(["-rss_limit_mb=2560", "-timeout=60"], results.crashes[0].reproduce_args) self.assertIn( "Test unit written to {0}/crash-".format( self.device_path(self.crash_dir)), results.logs, ) self.assertIn( "ERROR: HWAddressSanitizer: SEGV on unknown address " "0x000000000000", results.logs, )
def test_reproduce(self): """Test reproduce.""" testcase_file_path = os.path.join(self.temp_dir, 'testcase') with open(testcase_file_path, 'wb') as f: f.write('EEE') self._setup_env(job_type='libfuzzer_asan_job') build_manager.setup_build() result = testcase_manager.engine_reproduce( libfuzzer_engine.LibFuzzerEngine(), 'test_fuzzer', testcase_file_path, [], 30) self.assertIn('Running 1 inputs 100 time(s) each', result.output) self.assertIn('AddressSanitizer: SEGV on unknown address 0x000000000000', result.output)
def test_exit_failure_logged(self): """Test that we log when libFuzzer's exit code indicates it ran into an error.""" test_helpers.patch(self, [ 'metrics.logs.log_error', ]) _, corpus_path = setup_testcase_and_corpus('empty', 'corpus_with_some_files') os.environ['EXIT_FUZZER_CODE'] = '1' target_path = engine_common.find_fuzzer_path(DATA_DIR, 'exit_fuzzer') engine_impl = engine.LibFuzzerEngine() options = engine_impl.prepare(corpus_path, target_path, DATA_DIR) engine_impl.fuzz(target_path, options, TEMP_DIR, 10) self.assertEqual(1, self.mock.log_error.call_count)