diff --git a/src/python/bot/fuzzers/afl/constants.py b/src/python/bot/fuzzers/afl/constants.py index ff194880c3..c12ed60214 100644 --- a/src/python/bot/fuzzers/afl/constants.py +++ b/src/python/bot/fuzzers/afl/constants.py @@ -24,10 +24,10 @@ OUTPUT_FLAG = '-o' -SKIP_DETERMINISTIC_FLAG = '-d' - TIMEOUT_FLAG = '-t' +INSTANCE_ID_FLAG = '-S' + # AFL environment variables. SKIP_CRASHES_ENV_VAR = 'AFL_SKIP_CRASHES' @@ -61,3 +61,5 @@ MAX_MEMORY_LIMIT = 'none' CORE_PATTERN_FILE_PATH = '/proc/sys/kernel/core_pattern' + +DEFAULT_INSTANCE_ID = 'default' diff --git a/src/python/bot/fuzzers/afl/launcher.py b/src/python/bot/fuzzers/afl/launcher.py index 2639263157..53faed23c5 100755 --- a/src/python/bot/fuzzers/afl/launcher.py +++ b/src/python/bot/fuzzers/afl/launcher.py @@ -180,10 +180,15 @@ def is_testcase(cls, path): return (os.path.isfile(path) and bool(re.match(cls.TESTCASE_REGEX, os.path.basename(path)))) + @property + def instance_directory(self): + """Returns afl-fuzz's instance directory.""" + return os.path.join(self.output_directory, constants.DEFAULT_INSTANCE_ID) + @property def queue(self): """Returns afl-fuzz's queue directory.""" - return os.path.join(self.output_directory, 'queue') + return os.path.join(self.instance_directory, 'queue') def is_new_testcase(self, path): """Determine if |path| is a new unit.""" @@ -207,7 +212,7 @@ def copy_crash_if_needed(self, testcase_path): created by run.py). """ crash_paths = list_full_file_paths( - os.path.join(self.output_directory, 'crashes')) + os.path.join(self.instance_directory, 'crashes')) for crash_path in crash_paths: # AFL puts a README.txt file in the crashes directory. Just ignore it. @@ -232,7 +237,7 @@ def remove_hang_in_queue(self, hang_filename): @property def stats_path(self): """Returns the path of AFL's stats file: "fuzzer_stats".""" - return os.path.join(self.output_directory, 'fuzzer_stats') + return os.path.join(self.instance_directory, 'fuzzer_stats') class FuzzingStrategies(object): @@ -421,10 +426,6 @@ def __init__(self, input_directory, target_path, fuzzing_strategies): # 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 restore_if_needed(self): """Restore the original input directory if self.original_input_directory is set. Used to by merge() to get rid of the temporary input directory if it @@ -438,20 +439,6 @@ def restore_if_needed(self): self.input_directory = self.original_input_directory self.original_input_directory = None - def _decide_skip_deterministic(self): - """AFL typically uses deterministic steps to mutate files in the input - directory. It is only useful to do these steps once on a corpus. Thus skip - the deterministic steps if there are more than a trivial number of inputs - in the initial corpus.""" - # Don't get tricked into thinking we have a new corpus when subsetting. - if (self.strategies.use_corpus_subset and - self.strategies.corpus_subset_size < self.MIN_INPUTS_FOR_SKIP): - return - inputs = os.listdir(self.input_directory) - # If the corpus is small, don't worry about skipping. - if len(inputs) < self.MIN_INPUTS_FOR_SKIP: - self.skip_deterministic = False - def create_new_if_needed(self): """Checks if any inputs are too large for AFL. If not then does nothing. Otherwise creates a temporary input directory and copies the non-oversized @@ -799,6 +786,7 @@ def generate_afl_args(self, afl_output = self.afl_output.output_directory afl_args = [ + constants.INSTANCE_ID_FLAG + constants.DEFAULT_INSTANCE_ID, constants.INPUT_FLAG + afl_input, constants.OUTPUT_FLAG + afl_output, constants.MEMORY_LIMIT_FLAG + str(mem_limit) ] @@ -1032,17 +1020,6 @@ def fuzz(self): self._fuzz_args = self.generate_afl_args() - # Enable AFL's 'quick & dirty mode' which disable deterministic steps if - # we have already done them. See - # https://github.com/mcarpenter/afl/blob/master/docs/README for more - # details. - # TODO(metzman): Decide if we want to ensure that the deterministic stage - # finishes before terminating if we don't skip since it will never get a - # chance to run again if terminated early. This is only conceivable for - # fuzzers with large seed corpora and short timeouts. - if self.afl_input.skip_deterministic: - self._fuzz_args.insert(0, constants.SKIP_DETERMINISTIC_FLAG) - self.do_offline_mutations() # Decide if we want to use fast cal based on the size of the input @@ -1111,7 +1088,7 @@ def merge_corpus(self): # Remove arguments for afl-fuzz. self.remove_arg(showmap_args, constants.INPUT_FLAG) self.remove_arg(showmap_args, constants.DICT_FLAG) - self.remove_arg(showmap_args, constants.SKIP_DETERMINISTIC_FLAG) + self.remove_arg(showmap_args, constants.INSTANCE_ID_FLAG) # Replace -o argument. if environment.get_value('USE_MINIJAIL'): diff --git a/src/python/tests/core/bot/fuzzers/afl/afl_launcher_integration_test.py b/src/python/tests/core/bot/fuzzers/afl/afl_launcher_integration_test.py index 351a233091..f4957de050 100644 --- a/src/python/tests/core/bot/fuzzers/afl/afl_launcher_integration_test.py +++ b/src/python/tests/core/bot/fuzzers/afl/afl_launcher_integration_test.py @@ -269,7 +269,7 @@ def test_fuzz_no_crash(self, mock_get_timeout): testcase_path = setup_testcase_and_corpus('empty', 'corpus', fuzz=True) output = run_launcher(testcase_path, 'test_fuzzer') self.assertIn( - 'Command: {0}/afl-fuzz -i{1}/corpus ' + 'Command: {0}/afl-fuzz -Sdefault -i{1}/corpus ' '-o{1}/temp-1337/afl_output_dir -mnone ' '{0}/test_fuzzer 2147483647'.format(DATA_DIRECTORY, TEMP_DIRECTORY), output) @@ -284,7 +284,7 @@ def test_fuzz_input_crash(self, mock_get_timeout): testcase_path = setup_testcase_and_corpus('empty', 'corpus', fuzz=True) output = run_launcher(testcase_path, 'always_crash_fuzzer') self.assertIn( - 'Command: {0}/afl-fuzz -i{1}/corpus ' + 'Command: {0}/afl-fuzz -Sdefault -i{1}/corpus ' '-o{1}/temp-1337/afl_output_dir -mnone ' '{0}/always_crash_fuzzer 2147483647'.format(DATA_DIRECTORY, TEMP_DIRECTORY), output) @@ -307,7 +307,7 @@ def test_fuzz_crash(self, mock_get_timeout): output = run_launcher(testcase_path, 'easy_crash_fuzzer') self.assertIn( - 'Command: {0}/afl-fuzz -i{1}/corpus ' + 'Command: {0}/afl-fuzz -Sdefault -i{1}/corpus ' '-o{1}/temp-1337/afl_output_dir -mnone ' '{0}/easy_crash_fuzzer 2147483647'.format(DATA_DIRECTORY, TEMP_DIRECTORY), output) @@ -334,7 +334,7 @@ def test_fuzz_merge(self, mock_get_timeout): f.write('A' * 256) output = run_launcher(testcase_path, 'test_fuzzer') self.assertIn( - 'Command: {0}/afl-fuzz -d -i{1}/redundant_corpus ' + 'Command: {0}/afl-fuzz -Sdefault -i{1}/redundant_corpus ' '-o{1}/temp-1337/afl_output_dir -mnone ' '{0}/test_fuzzer 2147483647'.format(DATA_DIRECTORY, TEMP_DIRECTORY), output) @@ -400,7 +400,7 @@ def test_fuzz_no_crash(self, mock_get_timeout): testcase_path = setup_testcase_and_corpus('empty', 'corpus', fuzz=True) output = run_launcher(testcase_path, 'test_fuzzer') self.assertIn( - 'Command: {0}/afl-fuzz -i/corpus ' + 'Command: {0}/afl-fuzz -Sdefault -i/corpus ' '-o/afl_output_dir -mnone {0}/test_fuzzer ' '2147483647'.format(DATA_DIRECTORY), output) @@ -419,7 +419,7 @@ def test_fuzz_crash(self, mock_get_timeout): output = run_launcher(testcase_path, 'easy_crash_fuzzer') self.assertIn( - 'Command: {0}/afl-fuzz -i/corpus ' + 'Command: {0}/afl-fuzz -Sdefault -i/corpus ' '-o/afl_output_dir -mnone ' '{0}/easy_crash_fuzzer 2147483647'.format(DATA_DIRECTORY), output) @@ -445,7 +445,7 @@ def test_fuzz_merge(self, mock_get_timeout): output = run_launcher(testcase_path, 'test_fuzzer') self.assertIn( - 'Command: {0}/afl-fuzz -d -i/redundant_corpus ' + 'Command: {0}/afl-fuzz -Sdefault -i/redundant_corpus ' '-o/afl_output_dir -mnone ' '{0}/test_fuzzer 2147483647'.format(DATA_DIRECTORY), output) diff --git a/src/python/tests/core/bot/fuzzers/afl/afl_launcher_test.py b/src/python/tests/core/bot/fuzzers/afl/afl_launcher_test.py index 924d00e602..616139e9d7 100644 --- a/src/python/tests/core/bot/fuzzers/afl/afl_launcher_test.py +++ b/src/python/tests/core/bot/fuzzers/afl/afl_launcher_test.py @@ -38,7 +38,7 @@ class LauncherTestBase(fake_filesystem_unittest.TestCase): TARGET_OPTIONS_PATH = TARGET_PATH + '.options' TEMP_DIR = '/tmp' OUTPUT_DIR = '/tmp/afl_output_dir' - CRASHES_DIR = '/tmp/afl_output_dir/crashes' + CRASHES_DIR = '/tmp/afl_output_dir/default/crashes' DEFAULT_INPUT_DIR_CONTENTS = [fuzzer.AFL_DUMMY_INPUT] DATA_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data') @@ -212,31 +212,6 @@ def _create_oversized_file(self, directory=None, filename=None): file_contents = 'a' * (1024 * 1024) self._create_file(filename, directory=directory, contents=file_contents) - def test__decide_skip_deterministic(self): - """Test the AflFuzzInputDirectory._decide_skip_deterministic() method.""" - - # Test that skip_deterministic is set to False when there is no real - # corpus. - afl_input = self._new_afl_input() - self.assertFalse(afl_input.skip_deterministic) - - # Test that skip_deterministic is set to False when there are a small number - # of files in the corpus. - num_files = 3 - for file_num in range(num_files): - self._create_file('file' + str(file_num)) - - afl_input = self._new_afl_input() - self.assertFalse(afl_input.skip_deterministic) - - # Test that skip_deterministic is set to False when the "small number" of - # files permitted for determnisitic steps is exceeded. - for file_num in range(num_files, afl_input.MIN_INPUTS_FOR_SKIP): - self._create_file('file' + str(file_num)) - - afl_input = self._new_afl_input() - self.assertTrue(afl_input.skip_deterministic) - def test_create_new_if_needed(self): """Test that the AflFuzzInputDirectory.create_new_if_needed() method works as expected for oversized inputs and normal sized inputs.""" @@ -303,7 +278,7 @@ def test_corpus_subset(self): class AflFuzzOutputDirectoryTest(LauncherTestBase): """Test the launcher.AflFuzzOutputDirectory class.""" - QUEUE_DIR = '/tmp/afl_output_dir/queue' + QUEUE_DIR = '/tmp/afl_output_dir/default/queue' # Note that this is just a file that is part of the input corpus. # It isn't an AFL testcase. @@ -318,8 +293,8 @@ def setUp(self): super().setUp() # Creates self.OUTPUT_DIR. self.afl_output = launcher.AflFuzzOutputDirectory() - os.mkdir(self.CRASHES_DIR) - os.mkdir(self.QUEUE_DIR) + os.makedirs(self.CRASHES_DIR) + os.makedirs(self.QUEUE_DIR) self.input_testcase_path, input_testcase_obj = self._create_file( self.INPUT_TESTCASE_FILENAME)