def __init__(self, input_directory, target_path, fuzzing_strategies): """Inits AflFuzzInputDirectory. Args: input_directory: Directory passed to afl-fuzz containing corpus. target_path: Path to the fuzzer executable. Used to find seed corpus. fuzzing_strategies: fuzzing strategies to use. """ self.input_directory = input_directory self.strategies = fuzzing_strategies # We only need to use this when a temporary input directory is made. # (ie: when there is an oversized testcase in the input). self.original_input_directory = None engine_common.unpack_seed_corpus_if_needed( target_path, self.input_directory, max_bytes=constants.MAX_FILE_BYTES) # Ensure there is a usable testcase in the input directory. This is needed # because locally (and possibly for new fuzzers on CF) the dummy file is not # always in the input directory, which prevents AFL from running. if not list_full_file_paths(self.input_directory): write_dummy_file(self.input_directory) # Make a new corpus without oversized inputs if needed. self.create_new_if_needed() # Decide if we should skip AFL's deterministic steps. self.skip_deterministic = True self._decide_skip_deterministic()
def run(self, initial_corpus_path, minimized_corpus_path, bad_units_path): """Run corpus pruning. Output result to directory.""" if not shell.get_directory_file_count(initial_corpus_path): # Empty corpus, nothing to do. return # Set memory tool options and fuzzer arguments. engine_common.unpack_seed_corpus_if_needed(self.runner.target_path, initial_corpus_path, force_unpack=True) environment.reset_current_memory_tool_options(redzone_size=MIN_REDZONE, leaks=True) self.runner.process_sanitizer_options() additional_args = self.runner.get_libfuzzer_flags() # Execute fuzzer with arguments for corpus pruning. logs.log("Running merge...") try: result = self.runner.minimize_corpus( additional_args, [initial_corpus_path], minimized_corpus_path, bad_units_path, CORPUS_PRUNING_TIMEOUT, ) except engine.TimeoutError as e: raise CorpusPruningException( "Corpus pruning timed out while minimizing corpus\n" + e.message) except engine.Error as e: raise CorpusPruningException( "Corpus pruning failed to minimize corpus\n" + e.message) symbolized_output = stack_symbolizer.symbolize_stacktrace(result.logs) # Sanity check that there are files in minimized corpus after merging. if not shell.get_directory_file_count(minimized_corpus_path): raise CorpusPruningException( "Corpus pruning failed to minimize corpus\n" + symbolized_output) logs.log("Corpus merge finished successfully.", output=symbolized_output)
def get_corpus_directories(main_corpus_directory, new_testcases_directory, fuzzer_path, fuzzing_strategies, strategy_pool, minijail_chroot=None, allow_corpus_subset=True): """Return a list of corpus directories to be passed to the fuzzer binary for fuzzing.""" corpus_directories = [] corpus_directories.append(new_testcases_directory) # Check for seed corpus and add it into corpus directory. engine_common.unpack_seed_corpus_if_needed(fuzzer_path, main_corpus_directory) # Pick a few testcases from our corpus to use as the initial corpus. subset_size = engine_common.random_choice( engine_common.CORPUS_SUBSET_NUM_TESTCASES) if (allow_corpus_subset and strategy_pool.do_strategy(strategy.CORPUS_SUBSET_STRATEGY) and shell.get_directory_file_count(main_corpus_directory) > subset_size): # Copy |subset_size| testcases into 'subset' directory. corpus_subset_directory = create_corpus_directory('subset') copy_from_corpus(corpus_subset_directory, main_corpus_directory, subset_size) corpus_directories.append(corpus_subset_directory) fuzzing_strategies.append(strategy.CORPUS_SUBSET_STRATEGY.name + '_' + str(subset_size)) if minijail_chroot: bind_corpus_dirs(minijail_chroot, [main_corpus_directory]) else: # Regular fuzzing with the full main corpus directory. corpus_directories.append(main_corpus_directory) if minijail_chroot: bind_corpus_dirs(minijail_chroot, corpus_directories) return corpus_directories
def run(self, initial_corpus_path, minimized_corpus_path, bad_units_path): """Run corpus pruning. Output result to directory.""" if not shell.get_directory_file_count(initial_corpus_path): # Empty corpus, nothing to do. return # Set memory tool options and fuzzer arguments. engine_common.unpack_seed_corpus_if_needed(self.runner.fuzzer_path, initial_corpus_path, force_unpack=True) environment.reset_current_memory_tool_options(redzone_size=MIN_REDZONE, leaks=True) self.runner.process_sanitizer_options() additional_args = self.runner.get_libfuzzer_flags() # Execute fuzzer with arguments for corpus pruning. logs.log('Running merge...') result = self.runner.merge( [minimized_corpus_path, initial_corpus_path], CORPUS_PRUNING_TIMEOUT, artifact_prefix=bad_units_path, tmp_dir=self.context.merge_tmp_dir, additional_args=additional_args) # Sanity check that we didn't time out. symbolized_output = stack_symbolizer.symbolize_stacktrace( result.output) if result.timed_out: raise CorpusPruningException( 'Corpus pruning timed out while merging corpus: %s.' % symbolized_output) # Sanity check that we didn't error out and there are files in minimized # corpus after merging. if (result.return_code or not shell.get_directory_file_count(minimized_corpus_path)): raise CorpusPruningException( 'Corpus pruning failed to merge corpus: %s.' % symbolized_output) logs.log('Corpus merge finished successfully.', output=symbolized_output)
def prepare(self, corpus_dir, target_path, _): """Prepare for a fuzzing session, by generating options. Returns a FuzzOptions object. Args: corpus_dir: The main corpus directory. target_path: Path to the target. build_dir: Path to the build directory. Returns: A FuzzOptions object. """ arguments = fuzzer.get_arguments(target_path) strategy_pool = strategy_selection.generate_weighted_strategy_pool( strategy_list=strategy.LIBFUZZER_STRATEGY_LIST, use_generator=True, engine_name=self.name) strategy_info = launcher.pick_strategies(strategy_pool, target_path, corpus_dir, arguments) arguments.extend(strategy_info.arguments) # Check for seed corpus and add it into corpus directory. engine_common.unpack_seed_corpus_if_needed(target_path, corpus_dir) # Pick a few testcases from our corpus to use as the initial corpus. subset_size = engine_common.random_choice( engine_common.CORPUS_SUBSET_NUM_TESTCASES) if (not strategy_info.use_dataflow_tracing and strategy_pool.do_strategy(strategy.CORPUS_SUBSET_STRATEGY) and shell.get_directory_file_count(corpus_dir) > subset_size): # Copy |subset_size| testcases into 'subset' directory. corpus_subset_dir = self._create_temp_corpus_dir('subset') launcher.copy_from_corpus(corpus_subset_dir, corpus_dir, subset_size) strategy_info.fuzzing_strategies.append( strategy.CORPUS_SUBSET_STRATEGY.name + '_' + str(subset_size)) strategy_info.additional_corpus_dirs.append(corpus_subset_dir) else: strategy_info.additional_corpus_dirs.append(corpus_dir) # Check dict argument to make sure that it's valid. dict_argument = fuzzer_utils.extract_argument(arguments, constants.DICT_FLAG, remove=False) if dict_argument and not os.path.exists(dict_argument): logs.log_error('Invalid dict %s for %s.' % (dict_argument, target_path)) fuzzer_utils.extract_argument(arguments, constants.DICT_FLAG) # If there's no dict argument, check for %target_binary_name%.dict file. if (not fuzzer_utils.extract_argument( arguments, constants.DICT_FLAG, remove=False)): default_dict_path = dictionary_manager.get_default_dictionary_path( target_path) if os.path.exists(default_dict_path): arguments.append(constants.DICT_FLAG + default_dict_path) return LibFuzzerOptions(corpus_dir, arguments, strategy_info.fuzzing_strategies, strategy_info.additional_corpus_dirs, strategy_info.extra_env, strategy_info.use_dataflow_tracing, strategy_info.is_mutations_run)
def _unpack_seed_corpus_if_needed(self, *args, **kwargs): return engine_common.unpack_seed_corpus_if_needed( self.FUZZ_TARGET_PATH, self.CORPUS_DIRECTORY, *args, **kwargs)