def set_sanitizer_options(fuzzer_path): """Sets sanitizer options based on .options file overrides and what this script requires.""" engine_common.process_sanitizer_options_overrides(fuzzer_path) sanitizer_options_var = environment.get_current_memory_tool_var() sanitizer_options = environment.get_memory_tool_options( sanitizer_options_var, {}) sanitizer_options['exitcode'] = constants.TARGET_ERROR_EXITCODE environment.set_memory_tool_options(sanitizer_options_var, sanitizer_options)
def run(self, input_directory, output_directory, no_of_files): """Run the fuzzer to generate testcases.""" fuzzer_binary_name, fuzzer_path = self._get_fuzzer_binary_name_and_path( ) project_qualified_name = data_types.fuzz_target_project_qualified_name( utils.current_project(), fuzzer_binary_name) arguments = self.generate_arguments(fuzzer_path) corpus_directory = get_corpus_directory(input_directory, project_qualified_name) # Create fuzz testcases. for i in range(no_of_files): # Contents of testcase file don't matter at this point. Need to create # something non-null so that it is not ignored. testcase_file_path = os.path.join( output_directory, '%s%d' % (testcase_manager.FUZZ_PREFIX, i)) utils.write_data_to_file(' ', testcase_file_path) # Write the flags file containing command line for running launcher # script. flags_file_path = os.path.join( output_directory, '%s%d' % (testcase_manager.FLAGS_PREFIX, i)) flags = ['%TESTCASE%', fuzzer_binary_name] if arguments: flags.append(arguments) flags_file_content = ' '.join(flags) utils.write_data_to_file(flags_file_content, flags_file_path) output = 'Generated %d testcase for fuzzer %s.\n' % ( no_of_files, fuzzer_binary_name) output += 'metadata::fuzzer_binary_name: %s\n' % fuzzer_binary_name issue_owners = engine_common.get_issue_owners(fuzzer_path) if issue_owners: output += 'metadata::issue_owners: %s\n' % ','.join(issue_owners) issue_labels = engine_common.get_issue_labels(fuzzer_path) if issue_labels: output += 'metadata::issue_labels: %s\n' % ','.join(issue_labels) issue_components = engine_common.get_issue_components(fuzzer_path) if issue_components: output += 'metadata::issue_components: %s\n' % ','.join( issue_components) # Update *SAN_OPTIONS in current environment from .options file. This # environment is used in fuzz task later for deriving the environment # string in |get_environment_settings_as_string| and embedding this as # part of stacktrace. engine_common.process_sanitizer_options_overrides(fuzzer_path) return BuiltinFuzzerResult(output=output, corpus_directory=corpus_directory)
def test_sanitizer_options_changed(self, options_name, section_name): """Test that sanitizer options set in .options file are added to the environment variable.""" environment.set_value(options_name, 'a=1:b=2:c=1') self.fs.create_file( self.fuzz_target_options_file, contents='[{section_name}]\nc=3:d=4'.format(section_name=section_name)) engine_common.process_sanitizer_options_overrides(self.fuzz_target) self.assertEqual('a=1:b=2:c=3:d=4', environment.get_value(options_name))
def test_sanitizer_options_not_changed_unrelated_section( self, options_name, section_name): """Test that sanitizer options are not changed when provided an unrelated sanitizer section name.""" environment.set_value(options_name, 'a=1:b=2:c=1') self.fs.create_file(self.fuzz_target_options_file, contents='[{section_name}]\nc=3:d=4'.format( section_name=section_name)) engine_common.process_sanitizer_options_overrides(self.fuzz_target) self.assertEqual('a=1:b=2:c=1', environment.get_value(options_name))
def test_options_file(self): """Test *SAN_OPTIONS set from .options file.""" os.environ['ASAN_OPTIONS'] = self.DUMMY_OPTION os.environ['MSAN_OPTIONS'] = self.DUMMY_OPTION self._create_file( self.TARGET_OPTIONS_PATH, contents='[asan]\nfake_option=1\n[msan]\nfake_option=2') engine_common.process_sanitizer_options_overrides(self.TARGET_PATH) launcher.set_additional_sanitizer_options_for_afl_fuzz() self.assert_sanitizer_opts_set('ASAN_OPTIONS', self.DUMMY_OPTION, 'fake_option=1') self.assert_sanitizer_opts_set('MSAN_OPTIONS', self.DUMMY_OPTION, 'fake_option=2')
def run(self, input_directory, output_directory, no_of_files): """Run the fuzzer to generate testcases.""" build_directory = environment.get_value('BUILD_DIR') if not build_directory: raise BuiltinFuzzerException( 'BUILD_DIR environment variable is not set.') fuzzers = fuzzers_utils.get_fuzz_targets(build_directory) if not fuzzers: raise BuiltinFuzzerException( 'No fuzzer binaries found in |BUILD_DIR| directory.') fuzzer_binary_name = environment.get_value('FUZZ_TARGET') if fuzzer_binary_name: fuzzer_path = _get_fuzzer_path(fuzzers, fuzzer_binary_name) else: fuzzer_path = random.SystemRandom().choice(fuzzers) fuzzer_binary_name = os.path.basename(fuzzer_path) project_qualified_name = data_types.fuzz_target_project_qualified_name( utils.current_project(), fuzzer_binary_name) corpus_directory = os.path.join(input_directory, project_qualified_name) if environment.is_trusted_host(): from bot.untrusted_runner import file_host corpus_directory = file_host.rebase_to_worker_root( corpus_directory) arguments = self.generate_arguments(fuzzer_path) # Create corpus directory if it does not exist already. if environment.is_trusted_host(): from bot.untrusted_runner import file_host file_host.create_directory(corpus_directory, create_intermediates=True) else: if not os.path.exists(corpus_directory): os.mkdir(corpus_directory) # Create fuzz testcases. for i in range(no_of_files): # Contents of testcase file don't matter at this point. Need to create # something non-null so that it is not ignored. testcase_file_path = os.path.join(output_directory, '%s%d' % (tests.FUZZ_PREFIX, i)) utils.write_data_to_file(' ', testcase_file_path) # Write the flags file containing command line for running launcher # script. flags_file_path = os.path.join(output_directory, '%s%d' % (tests.FLAGS_PREFIX, i)) flags = ['%TESTCASE%', fuzzer_binary_name] if arguments: flags.append(arguments) flags_file_content = ' '.join(flags) utils.write_data_to_file(flags_file_content, flags_file_path) output = 'Generated %d testcase for fuzzer %s.\n' % ( no_of_files, fuzzer_binary_name) output += 'metadata::fuzzer_binary_name: %s\n' % fuzzer_binary_name issue_owners = engine_common.get_issue_owners(fuzzer_path) if issue_owners: output += 'metadata::issue_owners: %s\n' % ','.join(issue_owners) issue_labels = engine_common.get_issue_labels(fuzzer_path) if issue_labels: output += 'metadata::issue_labels: %s\n' % ','.join(issue_labels) # Update *SAN_OPTIONS in current environment from .options file. This # environment is used in fuzz task later for deriving the environment # string in |get_environment_settings_as_string| and embedding this as # part of stacktrace. engine_common.process_sanitizer_options_overrides(fuzzer_path) return BuiltinFuzzerResult(output=output, corpus_directory=corpus_directory)
def test_sanitizer_options_not_changed_no_options_file(self, options_name): """Test that sanitizer options are not changed and no exception occurs when .options file is not provided.""" environment.set_value(options_name, 'a=1:b=2:c=1') engine_common.process_sanitizer_options_overrides(self.fuzz_target) self.assertEqual('a=1:b=2:c=1', environment.get_value(options_name))
def main(argv): """Run afl as specified by argv.""" atexit.register(fuzzer_utils.cleanup) # Initialize variables. _, testcase_file_path, target_name = argv[:3] input_directory = environment.get_value('FUZZ_CORPUS_DIR') fuzzer_name = data_types.fuzz_target_project_qualified_name( utils.current_project(), target_name) # Initialize log handler. logs.configure( 'run_fuzzer', { 'fuzzer': fuzzer_name, 'engine': 'afl', 'job_name': environment.get_value('JOB_NAME') }) build_directory = environment.get_value('BUILD_DIR') fuzzer_path = engine_common.find_fuzzer_path(build_directory, target_name) if not fuzzer_path: # This is an expected case when doing regression testing with old builds # that do not have that fuzz target. It can also happen when a host sends a # message to an untrusted worker that just restarted and lost information on # build directory. logs.log_warn('Could not find fuzz target %s.' % target_name) return # Install signal handler. signal.signal(signal.SIGTERM, engine_common.signal_term_handler) # Set up temp dir. engine_common.recreate_directory(fuzzer_utils.get_temp_dir()) config = AflConfig.from_target_path(fuzzer_path) runner = AflRunner(fuzzer_path, config, testcase_file_path, input_directory) # Add *SAN_OPTIONS overrides from .options file. engine_common.process_sanitizer_options_overrides(fuzzer_path) # If we don't have a corpus, then that means this is not a fuzzing run. if not input_directory: load_testcase_if_exists(runner, testcase_file_path) return # Make sure afl won't exit because of bad sanitizer options. set_additional_sanitizer_options_for_afl_fuzz() # Execute afl-fuzz on the fuzzing target. fuzz_result = runner.fuzz() # Print info for the fuzzer logs. command = fuzz_result.command print('Command: {0}\n' 'Bot: {1}\n' 'Time ran: {2}\n').format(engine_common.get_command_quoted(command), BOT_NAME, fuzz_result.time_executed) print fuzz_result.output runner.strategies.print_strategies() if fuzz_result.return_code: # If AFL returned a non-zero return code quit now without getting stats, # since they would be meaningless. print runner.fuzzer_stderr return stats_getter = stats.StatsGetter(runner.afl_output.stats_path, config.dict_path) try: new_units_generated, new_units_added, corpus_size = ( runner.libfuzzerize_corpus()) stats_getter.set_stats(fuzz_result.time_executed, new_units_generated, new_units_added, corpus_size, runner.strategies, runner.fuzzer_stderr, fuzz_result.output) engine_common.dump_big_query_data(stats_getter.stats, testcase_file_path, AFL_PREFIX, fuzzer_name, command) finally: print runner.fuzzer_stderr # Whenever new units are added to corpus, record the stats to make them # easily searchable in stackdriver. if new_units_added: logs.log('New units added to corpus: %d.' % new_units_added, stats=stats_getter.stats)