diff --git a/src/python/bot/fuzzers/libFuzzer/constants.py b/src/python/bot/fuzzers/libFuzzer/constants.py index 51b15de1645..fd1d43246ea 100644 --- a/src/python/bot/fuzzers/libFuzzer/constants.py +++ b/src/python/bot/fuzzers/libFuzzer/constants.py @@ -22,6 +22,8 @@ # libFuzzer flags. ARTIFACT_PREFIX_FLAG = '-artifact_prefix=' +COLLECT_DATA_FLOW_FLAG = '-collect_data_flow=' + DICT_FLAG = '-dict=' FORK_FLAG = '-fork=' diff --git a/src/python/bot/fuzzers/libFuzzer/launcher.py b/src/python/bot/fuzzers/libFuzzer/launcher.py index 42af51e5b47..02cb52edcde 100755 --- a/src/python/bot/fuzzers/libFuzzer/launcher.py +++ b/src/python/bot/fuzzers/libFuzzer/launcher.py @@ -61,6 +61,9 @@ # Maximum length of a random chosen length for `-max_len`. MAX_VALUE_FOR_MAX_LENGTH = 10000 +# Probability of doing DFT-based fuzzing (depends on DFSan build presence). +DATAFLOW_TRACING_PROBABILITY = 0.25 + # Number of radamsa mutations. RADAMSA_MUTATIONS = 2000 @@ -429,17 +432,14 @@ def get_radamsa_path(): def remove_fuzzing_arguments(arguments): """Remove arguments used during fuzzing.""" - # Remove un-needed dictionary argument. - fuzzer_utils.extract_argument(arguments, constants.DICT_FLAG) - - # 'max_len' option may shrink the testcase if its size greater than 'max_len'. - fuzzer_utils.extract_argument(arguments, constants.MAX_LEN_FLAG) - - # Remove custom '-runs' argument. - fuzzer_utils.extract_argument(arguments, constants.RUNS_FLAG) - - # Remove `-fork' argument since it overrides '-merge' argument. - fuzzer_utils.extract_argument(arguments, constants.FORK_FLAG) + for argument in [ + constants.DICT_FLAG, # User for fuzzing only. + constants.MAX_LEN_FLAG, # This may shrink the testcases. + constants.RUNS_FLAG, # Make sure we don't have any '-runs' argument. + constants.FORK_FLAG, # It overrides `-merge` argument. + constants.COLLECT_DATA_FLOW_FLAG, # Used for fuzzing only. + ]: + fuzzer_utils.extract_argument(arguments, argument) def load_testcase_if_exists(fuzzer_runner, @@ -832,7 +832,20 @@ def main(argv): arguments.append(constants.VALUE_PROFILE_ARGUMENT) fuzzing_strategies.append(strategy.VALUE_PROFILE_STRATEGY.name) - if strategy_pool.do_strategy(strategy.FORK_STRATEGY): + # Depends on the presense of DFSan instrumented build. + dataflow_build_dir = environment.get_value('DATAFLOW_BUILD_DIR') + use_dataflow_tracing = ( + dataflow_build_dir and + strategy_pool.do_strategy(strategy.DATAFLOW_TRACING_STRATEGY)) + if use_dataflow_tracing: + dataflow_binary_path = os.path.join(dataflow_build_dir, + os.path.basename(fuzzer_path)) + arguments.append( + '%s%s' % (constants.COLLECT_DATA_FLOW_FLAG, dataflow_binary_path)) + fuzzing_strategies.append(strategy.DATAFLOW_TRACING_STRATEGY.name) + + # DataFlow Tracing requires fork mode, always use it with DFT strategy. + if use_dataflow_tracing or strategy_pool.do_strategy(strategy.FORK_STRATEGY): max_fuzz_threads = environment.get_value('MAX_FUZZ_THREADS', 1) num_fuzz_processes = max(1, multiprocessing.cpu_count() // max_fuzz_threads) arguments.append('%s%d' % (constants.FORK_FLAG, num_fuzz_processes)) diff --git a/src/python/bot/fuzzers/libFuzzer/stats.py b/src/python/bot/fuzzers/libFuzzer/stats.py index 69b690f2385..30a3513f330 100755 --- a/src/python/bot/fuzzers/libFuzzer/stats.py +++ b/src/python/bot/fuzzers/libFuzzer/stats.py @@ -110,6 +110,11 @@ def calculate_log_lines(log_lines): return other_lines_count, libfuzzer_lines_count, ignored_lines_count +def strategy_column_name(strategy_name): + """Convert the strategy name into stats column name.""" + return 'strategy_%s' % strategy_name + + def parse_fuzzing_strategies(log_lines, strategies): """Extract stats for fuzzing strategies used.""" if not strategies: @@ -130,27 +135,19 @@ def parse_line_for_strategy_prefix(line, strategy_name): try: strategy_value = int(line[len(strategy_prefix):]) - stats['strategy_' + strategy_name] = strategy_value + stats[strategy_column_name(strategy_name)] = strategy_value except (IndexError, ValueError) as e: logs.log_error('Failed to parse strategy "%s":\n%s\n' % (line, str(e))) - for line in strategies: - parse_line_for_strategy_prefix(line, strategy.CORPUS_SUBSET_STRATEGY.name) - parse_line_for_strategy_prefix(line, strategy.FORK_STRATEGY.name) + # These strategies are used with different values specified in the prefix. + for strategy_type in strategy.strategies_with_prefix_value: + for line in strategies: + parse_line_for_strategy_prefix(line, strategy_type.name) # Other strategies are either ON or OFF, without arbitrary values. - if strategy.CORPUS_MUTATION_RADAMSA_STRATEGY.name in strategies: - stats['strategy_corpus_mutations_radamsa'] = 1 - if strategy.CORPUS_MUTATION_ML_RNN_STRATEGY.name in strategies: - stats['strategy_corpus_mutations_ml_rnn'] = 1 - if strategy.MUTATOR_PLUGIN_STRATEGY.name in strategies: - stats['strategy_mutator_plugin'] = 1 - if strategy.RANDOM_MAX_LENGTH_STRATEGY.name in strategies: - stats['strategy_random_max_len'] = 1 - if strategy.RECOMMENDED_DICTIONARY_STRATEGY.name in strategies: - stats['strategy_recommended_dict'] = 1 - if strategy.VALUE_PROFILE_STRATEGY.name in strategies: - stats['strategy_value_profile'] = 1 + for strategy_type in strategy.strategies_with_boolean_value: + if strategy_type.name in strategies: + stats[strategy_column_name(strategy_type.name)] = 1 return stats @@ -183,17 +180,13 @@ def parse_performance_features(log_lines, strategies, arguments): 'slow_unit_count': 0, 'slow_units_count': 0, 'startup_crash_count': 1, - 'strategy_corpus_mutations_radamsa': 0, - 'strategy_corpus_mutations_ml_rnn': 0, - 'strategy_corpus_subset': 0, - 'strategy_fork': 0, - 'strategy_mutator_plugin': 0, - 'strategy_random_max_len': 0, - 'strategy_recommended_dict': 0, - 'strategy_value_profile': 0, 'timeout_count': 0, } + # Initialize all strategy stats as disabled by default. + for strategy_type in strategy.strategy_list: + stats[strategy_column_name(strategy_type.name)] = 0 + # Process fuzzing strategies used. stats.update(parse_fuzzing_strategies(log_lines, strategies)) diff --git a/src/python/bot/fuzzers/libFuzzer/strategy_selection.py b/src/python/bot/fuzzers/libFuzzer/strategy_selection.py index 2c53e21cdbc..03266d459d4 100644 --- a/src/python/bot/fuzzers/libFuzzer/strategy_selection.py +++ b/src/python/bot/fuzzers/libFuzzer/strategy_selection.py @@ -82,16 +82,15 @@ def generate_default_strategy_pool(): # Decide whether or not to include remaining strategies. if engine_common.do_corpus_subset(): pool.add_strategy(strategy.CORPUS_SUBSET_STRATEGY) - if do_strategy(strategy.RANDOM_MAX_LENGTH_STRATEGY): - pool.add_strategy(strategy.RANDOM_MAX_LENGTH_STRATEGY) - if do_strategy(strategy.RECOMMENDED_DICTIONARY_STRATEGY): - pool.add_strategy(strategy.RECOMMENDED_DICTIONARY_STRATEGY) - if do_strategy(strategy.VALUE_PROFILE_STRATEGY): - pool.add_strategy(strategy.VALUE_PROFILE_STRATEGY) - if do_strategy(strategy.FORK_STRATEGY): - pool.add_strategy(strategy.FORK_STRATEGY) - if do_strategy(strategy.MUTATOR_PLUGIN_STRATEGY): - pool.add_strategy(strategy.MUTATOR_PLUGIN_STRATEGY) + + for value in [ + strategy.RANDOM_MAX_LENGTH_STRATEGY, + strategy.RECOMMENDED_DICTIONARY_STRATEGY, strategy.VALUE_PROFILE_STRATEGY, + strategy.FORK_STRATEGY, strategy.MUTATOR_PLUGIN_STRATEGY + ]: + if do_strategy(value): + pool.add_strategy(value) + return pool @@ -117,9 +116,12 @@ def generate_weighted_strategy_pool(): if strategy_tuple.name in chosen_strategies: pool.add_strategy(strategy_tuple) - # We consider mutator plugin separately as it is only supported by a small - # number of fuzz targets and should be used heavily when available. - if do_strategy(strategy.MUTATOR_PLUGIN_STRATEGY): - pool.add_strategy(strategy.MUTATOR_PLUGIN_STRATEGY) + # We consider certain strategies separately as those are only supported by a + # small number of fuzz targets and should be used heavily when available. + for value in [ + strategy.DATAFLOW_TRACING_STRATEGY, strategy.MUTATOR_PLUGIN_STRATEGY + ]: + if do_strategy(value): + pool.add_strategy(value) return pool diff --git a/src/python/bot/fuzzers/libfuzzer.py b/src/python/bot/fuzzers/libfuzzer.py index 8fe15cf3431..f3c6b4ad8a5 100644 --- a/src/python/bot/fuzzers/libfuzzer.py +++ b/src/python/bot/fuzzers/libfuzzer.py @@ -550,6 +550,7 @@ def get_runner(fuzzer_path, temp_dir=None): """Get a libfuzzer runner.""" use_minijail = environment.get_value('USE_MINIJAIL') build_dir = environment.get_value('BUILD_DIR') + dataflow_build_dir = environment.get_value('DATAFLOW_BUILD_DIR') if use_minijail: # Set up chroot and runner. if environment.is_chromeos_system_job(): @@ -564,6 +565,10 @@ def get_runner(fuzzer_path, temp_dir=None): minijail_chroot.add_binding( minijail.ChrootBinding(build_dir, build_dir, False)) + if dataflow_build_dir: + minijail_chroot.add_binding( + minijail.ChrootBinding(dataflow_build_dir, dataflow_build_dir, False)) + # Also bind the build dir to /out to make it easier to hardcode references # to data files. minijail_chroot.add_binding( diff --git a/src/python/bot/fuzzers/strategy.py b/src/python/bot/fuzzers/strategy.py index 70970a044d9..ae36c837344 100755 --- a/src/python/bot/fuzzers/strategy.py +++ b/src/python/bot/fuzzers/strategy.py @@ -26,6 +26,7 @@ name='corpus_mutations_radamsa', probability=0.15) CORPUS_MUTATION_ML_RNN_STRATEGY = Strategy( name='corpus_mutations_ml_rnn', probability=0.50) +DATAFLOW_TRACING_STRATEGY = Strategy(name='dataflow_tracing', probability=0.25) CORPUS_SUBSET_STRATEGY = Strategy(name='corpus_subset', probability=0.50) FORK_STRATEGY = Strategy(name='fork', probability=0.50) MUTATOR_PLUGIN_STRATEGY = Strategy(name='mutator_plugin', probability=0.50) @@ -36,7 +37,26 @@ strategy_list = [ CORPUS_MUTATION_RADAMSA_STRATEGY, CORPUS_MUTATION_ML_RNN_STRATEGY, - CORPUS_SUBSET_STRATEGY, FORK_STRATEGY, MUTATOR_PLUGIN_STRATEGY, - RANDOM_MAX_LENGTH_STRATEGY, RECOMMENDED_DICTIONARY_STRATEGY, - VALUE_PROFILE_STRATEGY + DATAFLOW_TRACING_STRATEGY, CORPUS_SUBSET_STRATEGY, FORK_STRATEGY, + MUTATOR_PLUGIN_STRATEGY, RANDOM_MAX_LENGTH_STRATEGY, + RECOMMENDED_DICTIONARY_STRATEGY, VALUE_PROFILE_STRATEGY ] + +strategies_with_prefix_value = [ + CORPUS_SUBSET_STRATEGY, + FORK_STRATEGY, +] + +strategies_with_boolean_value = [ + CORPUS_MUTATION_RADAMSA_STRATEGY, + CORPUS_MUTATION_ML_RNN_STRATEGY, + DATAFLOW_TRACING_STRATEGY, + MUTATOR_PLUGIN_STRATEGY, + RANDOM_MAX_LENGTH_STRATEGY, + RECOMMENDED_DICTIONARY_STRATEGY, + VALUE_PROFILE_STRATEGY, +] + +# To ensure that all strategies present in |strategy_list| are parsed for stats. +assert (set(strategy_list) == set(strategies_with_prefix_value + + strategies_with_boolean_value)) diff --git a/src/python/tests/core/bot/fuzzers/libFuzzer/libfuzzer_launcher_test.py b/src/python/tests/core/bot/fuzzers/libFuzzer/libfuzzer_launcher_test.py index 0f9cef1e670..64c132a8f41 100644 --- a/src/python/tests/core/bot/fuzzers/libFuzzer/libfuzzer_launcher_test.py +++ b/src/python/tests/core/bot/fuzzers/libFuzzer/libfuzzer_launcher_test.py @@ -228,7 +228,8 @@ def setUp(self): self.mock.default_project_name.return_value = 'default-proj' self.mock.getpid.return_value = 1337 - self.mock.generate_weighted_strategy_pool.return_value = set_strategy_pool() + self.mock.generate_weighted_strategy_pool.return_value = set_strategy_pool( + [strategy.DATAFLOW_TRACING_STRATEGY]) @mock.patch('google_cloud_utils.storage.exists', lambda x: None) @mock.patch('google_cloud_utils.storage.read_data', lambda x: None) @@ -401,6 +402,8 @@ def test_basic_fuzz(self, mock_stdout): 0, 'startup_crash_count': 0, + 'strategy_dataflow_tracing': + 0, 'strategy_corpus_mutations_radamsa': 1, 'strategy_corpus_mutations_ml_rnn': @@ -598,6 +601,8 @@ def test_basic_fuzz_with_custom_options(self, mock_stdout): 0, 'slowest_unit_time_sec': 0, + 'strategy_dataflow_tracing': + 0, 'startup_crash_count': 0, 'strategy_corpus_mutations_radamsa': @@ -694,6 +699,7 @@ def test_parse_log_and_stats_no_crash(self): 'slow_units_count': 0, 'slowest_unit_time_sec': 0, 'startup_crash_count': 0, + 'strategy_dataflow_tracing': 0, 'strategy_corpus_mutations_radamsa': 1, 'strategy_corpus_mutations_ml_rnn': 0, 'strategy_corpus_subset': 50, @@ -745,6 +751,7 @@ def test_parse_log_and_stats_crash(self): 'slow_units_count': 0, 'slowest_unit_time_sec': 0, 'startup_crash_count': 0, + 'strategy_dataflow_tracing': 0, 'strategy_corpus_mutations_radamsa': 0, 'strategy_corpus_mutations_ml_rnn': 0, 'strategy_corpus_subset': 0, @@ -790,6 +797,7 @@ def test_parse_log_and_stats_startup_crash(self): 'slow_unit_count': 0, 'slow_units_count': 0, 'startup_crash_count': 1, + 'strategy_dataflow_tracing': 0, 'strategy_corpus_mutations_radamsa': 0, 'strategy_corpus_mutations_ml_rnn': 0, 'strategy_corpus_subset': 0, @@ -840,6 +848,7 @@ def test_parse_log_and_stats_corpus_crash(self): 'slow_units_count': 0, 'slowest_unit_time_sec': 0, 'startup_crash_count': 0, + 'strategy_dataflow_tracing': 0, 'strategy_corpus_mutations_radamsa': 0, 'strategy_corpus_mutations_ml_rnn': 0, 'strategy_corpus_subset': 0, @@ -890,6 +899,7 @@ def test_parse_log_and_stats_corpus_crash_with_corpus_subset(self): 'slow_units_count': 0, 'slowest_unit_time_sec': 0, 'startup_crash_count': 0, + 'strategy_dataflow_tracing': 0, 'strategy_corpus_mutations_radamsa': 0, 'strategy_corpus_mutations_ml_rnn': 0, 'strategy_corpus_subset': 1, @@ -940,6 +950,7 @@ def test_parse_log_and_stats_oom(self): 'slow_units_count': 0, 'slowest_unit_time_sec': 0, 'startup_crash_count': 0, + 'strategy_dataflow_tracing': 0, 'strategy_corpus_mutations_radamsa': 0, 'strategy_corpus_mutations_ml_rnn': 0, 'strategy_corpus_subset': 0, @@ -990,6 +1001,7 @@ def test_parse_log_and_stats_oom_in_seed_corpus(self): 'slow_units_count': 0, 'slowest_unit_time_sec': 0, 'startup_crash_count': 0, + 'strategy_dataflow_tracing': 0, 'strategy_corpus_mutations_radamsa': 0, 'strategy_corpus_mutations_ml_rnn': 1, 'strategy_corpus_subset': 0, @@ -1039,6 +1051,7 @@ def test_parse_log_and_stats_from_corrupted_output(self): 'slow_units_count': 0, 'slowest_unit_time_sec': 0, 'startup_crash_count': 0, + 'strategy_dataflow_tracing': 0, 'strategy_corpus_mutations_radamsa': 0, 'strategy_corpus_mutations_ml_rnn': 0, 'strategy_corpus_subset': 0, @@ -1089,6 +1102,7 @@ def test_parse_log_and_stats_timeout(self): 'slow_units_count': 4, 'slowest_unit_time_sec': 19, 'startup_crash_count': 0, + 'strategy_dataflow_tracing': 0, 'strategy_corpus_mutations_radamsa': 1, 'strategy_corpus_mutations_ml_rnn': 0, 'strategy_corpus_subset': 0, @@ -1297,6 +1311,8 @@ def test_oom_crash(self, mock_stdout): 0, 'startup_crash_count': 0, + 'strategy_dataflow_tracing': + 0, 'strategy_corpus_mutations_radamsa': 0, 'strategy_corpus_mutations_ml_rnn': @@ -1589,8 +1605,6 @@ def test_fuzz_from_subset_minijail(self, mock_tempfile, _): '/main_corpus_dir', ]]) - del os.environ['USE_MINIJAIL'] - @mock.patch('bot.fuzzers.libFuzzer.launcher.add_recommended_dictionary', lambda x, y, z: False) @mock.patch('sys.stdout', new_callable=test_utils.MockStdout) @@ -1708,8 +1722,6 @@ def test_fuzz_from_subset_without_enough_corpus_minijail( '/main_corpus_dir', ]]) - del os.environ['USE_MINIJAIL'] - @mock.patch('bot.fuzzers.libFuzzer.launcher.add_recommended_dictionary', lambda x, y, z: False) @mock.patch('bot.fuzzers.libFuzzer.launcher.' @@ -1810,6 +1822,343 @@ def test_fuzz_with_mutations_using_ml_rnn(self, mock_execute, *_): '/fake/inputs-disk/temp-1337/mutations', ]]) + def _dataflow_tracing_test_setup(self): + """Common setup for the tests checking dataflow tracing strategy support.""" + self.mock.generate_weighted_strategy_pool.return_value = set_strategy_pool( + [strategy.DATAFLOW_TRACING_STRATEGY]) + + self.fs.CreateDirectory('/fake/corpus_dir') + self.fs.CreateFile('/fake/testcase_basic') + self.fs.CreateFile('/fake/dfsan_build/fake_fuzzer') + + os.environ['FUZZ_CORPUS_DIR'] = '/fake/corpus_dir' + os.environ['DATAFLOW_BUILD_DIR'] = '/fake/dfsan_build' + + @mock.patch('bot.fuzzers.libFuzzer.launcher.add_recommended_dictionary', + lambda x, y, z: False) + @mock.patch('multiprocessing.cpu_count', return_value=1) + @mock.patch('sys.stdout', new_callable=test_utils.MockStdout) + def test_fuzz_with_dataflow_tracing(self, mock_stdout, *_): + """Tests fuzzing with dataflow tracing.""" + self._dataflow_tracing_test_setup() + new_units_added = 11 + with mock.patch( + 'subprocess.Popen', + create_mock_popen(self.no_crash_output, + '/fake/inputs-disk/temp-1337/new', '/fake/corpus_dir', + new_units_added)) as mock_popen: + launcher.main([ + 'launcher.py', + '/fake/testcase_basic', + 'fake_fuzzer', + '-max_len=80', + ]) + + self.assertEqual(mock_popen.commands, [[ + '/fake/build_dir/fake_fuzzer', '-max_len=80', '-rss_limit_mb=2048', + '-timeout=25', '-collect_data_flow=/fake/dfsan_build/fake_fuzzer', + '-fork=1', '-artifact_prefix=/fake/', '-max_total_time=2650', + '-print_final_stats=1', '/fake/inputs-disk/temp-1337/new', + '/fake/corpus_dir' + ], [ + '/fake/build_dir/fake_fuzzer', + '-rss_limit_mb=2048', + '-timeout=25', + '-merge=1', + '/fake/inputs-disk/temp-1337/merge-corpus', + '/fake/inputs-disk/temp-1337/new', + '/fake/corpus_dir', + ]]) + + # Check new testcases added. + self.assertEqual( + sorted(os.listdir('/fake/corpus_dir')), + sorted(mock_popen.testcases_written)) + + for corpus_file in os.listdir('/fake/corpus_dir'): + with open(os.path.join('/fake/corpus_dir', corpus_file)) as f: + self.assertEqual(f.read(), corpus_file) + + # Check stats. + stats_data = fuzzer_stats.TestcaseRun.read_from_disk( + '/fake/testcase_basic') + self.assertDictEqual( + stats_data.data, { + u'actual_duration': + 0, + u'average_exec_per_sec': + 97, + u'bad_instrumentation': + 0, + 'build_revision': + 1337, + u'command': [ + u'/fake/build_dir/fake_fuzzer', + u'-max_len=80', + u'-rss_limit_mb=2048', + u'-timeout=25', + u'-collect_data_flow=/fake/dfsan_build/fake_fuzzer', + u'-fork=1', + u'-artifact_prefix=/fake/', + u'-max_total_time=2650', + u'-print_final_stats=1', + u'/fake/inputs-disk/temp-1337/new', + u'/fake/corpus_dir', + ], + u'corpus_crash_count': + 0, + u'crash_count': + 0, + u'corpus_size': + 11, + u'dict_used': + 1, + u'edge_coverage': + 1769, + u'edges_total': + 398408, + u'feature_coverage': + 4958, + u'initial_edge_coverage': + 1769, + u'initial_feature_coverage': + 4958, + u'expected_duration': + 2650, + 'fuzzer': + u'libfuzzer_fake_fuzzer', + u'fuzzing_time_percent': + 0.0, + 'job': + u'job_name', + 'kind': + u'TestcaseRun', + u'leak_count': + 0, + u'log_lines_from_engine': + 65, + u'log_lines_ignored': + 8, + u'log_lines_unwanted': + 0, + u'manual_dict_size': + 0, + u'max_len': + 80, + u'merge_edge_coverage': + 1769, + u'new_edges': + 0, + u'new_features': + 0, + u'new_units_added': + 11, + u'new_units_generated': + 55, + u'number_of_executed_units': + 258724, + u'oom_count': + 0, + u'peak_rss_mb': + 103, + u'recommended_dict_size': + 0, + u'slow_unit_count': + 0, + u'slow_units_count': + 0, + u'slowest_unit_time_sec': + 0, + u'startup_crash_count': + 0, + u'strategy_dataflow_tracing': + 1, + u'strategy_corpus_mutations_radamsa': + 0, + u'strategy_corpus_mutations_ml_rnn': + 0, + u'strategy_corpus_subset': + 0, + u'strategy_fork': + 1, + u'strategy_mutator_plugin': + 0, + u'strategy_random_max_len': + 0, + u'strategy_recommended_dict': + 0, + u'strategy_value_profile': + 0, + u'timeout_count': + 0, + u'timeout_limit': + 25, + 'timestamp': + 1337.0 + }) + + # Output printed. + self.assertIn(self.no_crash_output, mock_stdout.getvalue()) + expected_command = ( + 'Command: /fake/build_dir/fake_fuzzer ' + '-max_len=80 -rss_limit_mb=2048 -timeout=25 ' + '-collect_data_flow=/fake/dfsan_build/fake_fuzzer -fork=1 ' + '-artifact_prefix=/fake/ -max_total_time=2650 -print_final_stats=1 ' + '/fake/inputs-disk/temp-1337/new /fake/corpus_dir') + self.assertIn(expected_command, mock_stdout.getvalue()) + self.assertIn('Bot: test-bot', mock_stdout.getvalue()) + self.assertIn('Time ran:', mock_stdout.getvalue()) + + @mock.patch('bot.fuzzers.libFuzzer.launcher.add_recommended_dictionary', + lambda x, y, z: False) + @mock.patch('multiprocessing.cpu_count', return_value=1) + @mock.patch('sys.stdout', new_callable=test_utils.MockStdout) + @mock.patch('system.minijail.tempfile.NamedTemporaryFile') + def test_fuzz_with_dataflow_tracing_minijail(self, mock_tempfile, *_): + """Tests fuzzing with dataflow tracing inside minijail.""" + self._dataflow_tracing_test_setup() + + os.environ['USE_MINIJAIL'] = 'True' + + mock_tempfile.return_value.__enter__.return_value.name = '/tmppath' + mock_tempfile.return_value.name = '/tmpfile' + + new_units_added = 11 + with mock.patch( + 'subprocess.Popen', + create_mock_popen(self.no_crash_output, + '/fake/inputs-disk/temp-1337/new', '/fake/corpus_dir', + new_units_added)) as mock_popen: + launcher.main([ + 'launcher.py', + '/fake/testcase_basic', + 'fake_fuzzer', + '-max_len=80', + ]) + + self.assertEqual(mock_popen.commands, [[ + 'sudo', + '-S', + 'mknod', + '-m', + '666', + '/fake/inputs-disk/temp-1337/CHROOT/dev/null', + 'c', + '1', + '3', + ], [ + 'sudo', '-S', 'mknod', '-m', '666', + '/fake/inputs-disk/temp-1337/CHROOT/dev/random', 'c', '1', '8' + ], [ + 'sudo', + '-S', + 'mknod', + '-m', + '666', + '/fake/inputs-disk/temp-1337/CHROOT/dev/urandom', + 'c', + '1', + '9', + ], [ + '/fake_root/resources/platform/{}/minijail0'.format( + environment.platform().lower()), + '-f', + '/tmpfile', + '-U', + '-m', + '0 1000 1', + '-T', + 'static', + '-c', + '0', + '-n', + '-v', + '-p', + '-l', + '-I', + '-k', + 'proc,/proc,proc,1', + '-P', + '/fake/inputs-disk/temp-1337/CHROOT', + '-b', + '/fake/inputs-disk/temp-1337/TEMP,/tmp,1', + '-b', + '/lib,/lib,0', + '-b', + '/lib64,/lib64,0', + '-b', + '/usr/lib,/usr/lib,0', + '-b', + '/fake/build_dir,/fake/build_dir,0', + '-b', + '/fake/dfsan_build,/fake/dfsan_build,0', + '-b', + '/fake/build_dir,/out,0', + '-b', + '/fake/inputs-disk/temp-1337/new,/new,1', + '-b', + '/fake/corpus_dir,/corpus_dir,1', + '/fake/build_dir/fake_fuzzer', + '-max_len=80', + '-rss_limit_mb=2048', + '-timeout=25', + '-collect_data_flow=/fake/dfsan_build/fake_fuzzer', + '-fork=1', + '-artifact_prefix=/', + '-max_total_time=2650', + '-print_final_stats=1', + '/new', + '/corpus_dir', + ], [ + '/fake_root/resources/platform/{}/minijail0'.format( + environment.platform().lower()), + '-f', + '/tmpfile', + '-U', + '-m', + '0 1000 1', + '-T', + 'static', + '-c', + '0', + '-n', + '-v', + '-p', + '-l', + '-I', + '-k', + 'proc,/proc,proc,1', + '-P', + '/fake/inputs-disk/temp-1337/CHROOT', + '-b', + '/fake/inputs-disk/temp-1337/TEMP,/tmp,1', + '-b', + '/lib,/lib,0', + '-b', + '/lib64,/lib64,0', + '-b', + '/usr/lib,/usr/lib,0', + '-b', + '/fake/build_dir,/fake/build_dir,0', + '-b', + '/fake/dfsan_build,/fake/dfsan_build,0', + '-b', + '/fake/build_dir,/out,0', + '-b', + '/fake/inputs-disk/temp-1337/new,/new,1', + '-b', + '/fake/corpus_dir,/corpus_dir,1', + '-b', + '/fake/inputs-disk/temp-1337/merge-corpus,/merge-corpus,1', + '/fake/build_dir/fake_fuzzer', + '-rss_limit_mb=2048', + '-timeout=25', + '-merge=1', + '/merge-corpus', + '/new', + '/corpus_dir', + ]]) + @mock.patch('bot.fuzzers.libFuzzer.launcher.add_recommended_dictionary', lambda x, y, z: False) @mock.patch('multiprocessing.cpu_count', return_value=2) @@ -1953,6 +2302,8 @@ def test_fuzz_with_fork_mode(self, mock_stdout, *_): 0, u'strategy_corpus_subset': 0, + u'strategy_dataflow_tracing': + 0, u'strategy_fork': 2, u'strategy_mutator_plugin':