1803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar#!/usr/bin/env python 2803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 3803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar""" 4803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel DunbarThis is a generic fuzz testing tool, see --help for more information. 5803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar""" 6803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 7803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbarimport os 8803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbarimport sys 9803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbarimport random 10803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbarimport subprocess 11803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbarimport itertools 12803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 13803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbarclass TestGenerator: 14803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar def __init__(self, inputs, delete, insert, replace, 15803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar insert_strings, pick_input): 16803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar self.inputs = [(s, open(s).read()) for s in inputs] 17803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 18803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar self.delete = bool(delete) 19803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar self.insert = bool(insert) 20803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar self.replace = bool(replace) 21803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar self.pick_input = bool(pick_input) 22803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar self.insert_strings = list(insert_strings) 23803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 24803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar self.num_positions = sum([len(d) for _,d in self.inputs]) 25803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar self.num_insert_strings = len(insert_strings) 26803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar self.num_tests = ((delete + (insert + replace)*self.num_insert_strings) 27803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar * self.num_positions) 28803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar self.num_tests += 1 29803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 30803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar if self.pick_input: 31803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar self.num_tests *= self.num_positions 32803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 33803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar def position_to_source_index(self, position): 34803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar for i,(s,d) in enumerate(self.inputs): 35803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar n = len(d) 36803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar if position < n: 37803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar return (i,position) 38803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar position -= n 39803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar raise ValueError,'Invalid position.' 40803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 41803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar def get_test(self, index): 42803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar assert 0 <= index < self.num_tests 43803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 44803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar picked_position = None 45803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar if self.pick_input: 46803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar index,picked_position = divmod(index, self.num_positions) 47803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar picked_position = self.position_to_source_index(picked_position) 48803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 49803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar if index == 0: 50803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar return ('nothing', None, None, picked_position) 51803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 52803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar index -= 1 53803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar index,position = divmod(index, self.num_positions) 54803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar position = self.position_to_source_index(position) 55803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar if self.delete: 56803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar if index == 0: 57803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar return ('delete', position, None, picked_position) 58803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar index -= 1 59803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 60803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar index,insert_index = divmod(index, self.num_insert_strings) 61803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar insert_str = self.insert_strings[insert_index] 62803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar if self.insert: 63803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar if index == 0: 64803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar return ('insert', position, insert_str, picked_position) 65803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar index -= 1 66803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 67803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar assert self.replace 68803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar assert index == 0 69803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar return ('replace', position, insert_str, picked_position) 70803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 71803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbarclass TestApplication: 72803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar def __init__(self, tg, test): 73803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar self.tg = tg 74803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar self.test = test 75803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 76803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar def apply(self): 77803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar if self.test[0] == 'nothing': 78803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar pass 79803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar else: 80803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar i,j = self.test[1] 81803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar name,data = self.tg.inputs[i] 82803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar if self.test[0] == 'delete': 83803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar data = data[:j] + data[j+1:] 84803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar elif self.test[0] == 'insert': 85803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar data = data[:j] + self.test[2] + data[j:] 86803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar elif self.test[0] == 'replace': 87803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar data = data[:j] + self.test[2] + data[j+1:] 88803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar else: 89803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar raise ValueError,'Invalid test %r' % self.test 90803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar open(name,'wb').write(data) 91803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 92803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar def revert(self): 93803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar if self.test[0] != 'nothing': 94803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar i,j = self.test[1] 95803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar name,data = self.tg.inputs[i] 96803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar open(name,'wb').write(data) 97803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 98803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbardef quote(str): 99803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar return '"' + str + '"' 100803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 101803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbardef run_one_test(test_application, index, input_files, args): 102803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar test = test_application.test 103803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 104803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar # Interpolate arguments. 105803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar options = { 'index' : index, 106803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 'inputs' : ' '.join(quote(f) for f in input_files) } 107803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 108803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar # Add picked input interpolation arguments, if used. 109803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar if test[3] is not None: 110803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar pos = test[3][1] 111803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar options['picked_input'] = input_files[test[3][0]] 112803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar options['picked_input_pos'] = pos 113803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar # Compute the line and column. 114803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar file_data = test_application.tg.inputs[test[3][0]][1] 115803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar line = column = 1 116803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar for i in range(pos): 117803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar c = file_data[i] 118803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar if c == '\n': 119803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar line += 1 120803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar column = 1 121803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar else: 122803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar column += 1 123803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar options['picked_input_line'] = line 124803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar options['picked_input_col'] = column 125803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 126803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar test_args = [a % options for a in args] 127803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar if opts.verbose: 128803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar print '%s: note: executing %r' % (sys.argv[0], test_args) 129803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 130803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar stdout = None 131803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar stderr = None 132803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar if opts.log_dir: 133803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar stdout_log_path = os.path.join(opts.log_dir, '%s.out' % index) 134803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar stderr_log_path = os.path.join(opts.log_dir, '%s.err' % index) 135803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar stdout = open(stdout_log_path, 'wb') 136803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar stderr = open(stderr_log_path, 'wb') 137803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar else: 138803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar sys.stdout.flush() 139803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar p = subprocess.Popen(test_args, stdout=stdout, stderr=stderr) 140803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar p.communicate() 141803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar exit_code = p.wait() 142803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 143803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar test_result = (exit_code == opts.expected_exit_code or 144803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar exit_code in opts.extra_exit_codes) 145803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 146803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar if stdout is not None: 147803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar stdout.close() 148803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar stderr.close() 149803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 150803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar # Remove the logs for passes, unless logging all results. 151803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar if not opts.log_all and test_result: 152803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar os.remove(stdout_log_path) 153803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar os.remove(stderr_log_path) 154803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 155803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar if not test_result: 156803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar print 'FAIL: %d' % index 157803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar elif not opts.succinct: 158803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar print 'PASS: %d' % index 1592a6dc143b6da657ca0839acec1ac36e12cd1b295Argyrios Kyrtzidis return test_result 160803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 161803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbardef main(): 162803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar global opts 163803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar from optparse import OptionParser, OptionGroup 164803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar parser = OptionParser("""%prog [options] ... test command args ... 165803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 166803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar%prog is a tool for fuzzing inputs and testing them. 167803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 168803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel DunbarThe most basic usage is something like: 169803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 170803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar $ %prog --file foo.txt ./test.sh 171803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 172803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbarwhich will run a default list of fuzzing strategies on the input. For each 173803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbarfuzzed input, it will overwrite the input files (in place), run the test script, 174803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbarthen restore the files back to their original contents. 175803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 176803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel DunbarNOTE: You should make sure you have a backup copy of your inputs, in case 177803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbarsomething goes wrong!!! 178803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 179803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel DunbarYou can cause the fuzzing to not restore the original files with 180803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar'--no-revert'. Generally this is used with '--test <index>' to run one failing 181803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbartest and then leave the fuzzed inputs in place to examine the failure. 182803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 183803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel DunbarFor each fuzzed input, %prog will run the test command given on the command 184803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbarline. Each argument in the command is subject to string interpolation before 185803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbarbeing executed. The syntax is "%(VARIABLE)FORMAT" where FORMAT is a standard 1869df08bb7661779a1703d29681833b79e62336c22Daniel Dunbarprintf format, and VARIABLE is one of: 187803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 188803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 'index' - the test index being run 189803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 'inputs' - the full list of test inputs 190803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 'picked_input' - (with --pick-input) the selected input file 191803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 'picked_input_pos' - (with --pick-input) the selected input position 192803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 'picked_input_line' - (with --pick-input) the selected input line 193803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 'picked_input_col' - (with --pick-input) the selected input column 194803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 195803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel DunbarBy default, the script will run forever continually picking new tests to 196803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbarrun. You can limit the number of tests that are run with '--max-tests <number>', 197803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbarand you can run a particular test with '--test <index>'. 1982a6dc143b6da657ca0839acec1ac36e12cd1b295Argyrios Kyrtzidis 1992a6dc143b6da657ca0839acec1ac36e12cd1b295Argyrios KyrtzidisYou can specify '--stop-on-fail' to stop the script on the first failure 2002a6dc143b6da657ca0839acec1ac36e12cd1b295Argyrios Kyrtzidiswithout reverting the changes. 2012a6dc143b6da657ca0839acec1ac36e12cd1b295Argyrios Kyrtzidis 202803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar""") 203803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar parser.add_option("-v", "--verbose", help="Show more output", 204803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar action='store_true', dest="verbose", default=False) 205803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar parser.add_option("-s", "--succinct", help="Reduce amount of output", 206803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar action="store_true", dest="succinct", default=False) 207803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 208803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar group = OptionGroup(parser, "Test Execution") 209803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar group.add_option("", "--expected-exit-code", help="Set expected exit code", 210803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar type=int, dest="expected_exit_code", 211803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar default=0) 212803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar group.add_option("", "--extra-exit-code", 213803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar help="Set additional expected exit code", 214803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar type=int, action="append", dest="extra_exit_codes", 215803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar default=[]) 216803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar group.add_option("", "--log-dir", 217803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar help="Capture test logs to an output directory", 218803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar type=str, dest="log_dir", 219803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar default=None) 220803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar group.add_option("", "--log-all", 221803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar help="Log all outputs (not just failures)", 222803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar action="store_true", dest="log_all", default=False) 223803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar parser.add_option_group(group) 224803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 225803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar group = OptionGroup(parser, "Input Files") 226803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar group.add_option("", "--file", metavar="PATH", 227803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar help="Add an input file to fuzz", 228803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar type=str, action="append", dest="input_files", default=[]) 229803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar group.add_option("", "--filelist", metavar="LIST", 230803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar help="Add a list of inputs files to fuzz (one per line)", 23100f1f27664c0fcb4b536aa5835094ad09b1a5371Argyrios Kyrtzidis type=str, action="append", dest="filelists", default=[]) 232803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar parser.add_option_group(group) 233803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 234803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar group = OptionGroup(parser, "Fuzz Options") 235803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar group.add_option("", "--replacement-chars", dest="replacement_chars", 236803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar help="Characters to insert/replace", 237803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar default="0{}[]<>\;@#$^%& ") 238803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar group.add_option("", "--replacement-string", dest="replacement_strings", 239803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar action="append", help="Add a replacement string to use", 240803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar default=[]) 241c53a844e22c5a635c64ede7e1ac31fb035e52701Daniel Dunbar group.add_option("", "--replacement-list", dest="replacement_lists", 242c53a844e22c5a635c64ede7e1ac31fb035e52701Daniel Dunbar help="Add a list of replacement strings (one per line)", 243c53a844e22c5a635c64ede7e1ac31fb035e52701Daniel Dunbar action="append", default=[]) 244803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar group.add_option("", "--no-delete", help="Don't delete characters", 245803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar action='store_false', dest="enable_delete", default=True) 246803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar group.add_option("", "--no-insert", help="Don't insert strings", 247803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar action='store_false', dest="enable_insert", default=True) 248803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar group.add_option("", "--no-replace", help="Don't replace strings", 249803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar action='store_false', dest="enable_replace", default=True) 250803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar group.add_option("", "--no-revert", help="Don't revert changes", 251803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar action='store_false', dest="revert", default=True) 2522a6dc143b6da657ca0839acec1ac36e12cd1b295Argyrios Kyrtzidis group.add_option("", "--stop-on-fail", help="Stop on first failure", 2532a6dc143b6da657ca0839acec1ac36e12cd1b295Argyrios Kyrtzidis action='store_true', dest="stop_on_fail", default=False) 254803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar parser.add_option_group(group) 255803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 256803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar group = OptionGroup(parser, "Test Selection") 257803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar group.add_option("", "--test", help="Run a particular test", 258803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar type=int, dest="test", default=None, metavar="INDEX") 259803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar group.add_option("", "--max-tests", help="Maximum number of tests", 2603cb8e2f5e9ce7677ea01adf5746a6b529fae934bArgyrios Kyrtzidis type=int, dest="max_tests", default=None, metavar="COUNT") 261803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar group.add_option("", "--pick-input", 262803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar help="Randomly select an input byte as well as fuzzing", 263803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar action='store_true', dest="pick_input", default=False) 264803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar parser.add_option_group(group) 265803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 266803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar parser.disable_interspersed_args() 267803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 268803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar (opts, args) = parser.parse_args() 269803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 270803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar if not args: 271803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar parser.error("Invalid number of arguments") 272803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 273803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar # Collect the list of inputs. 274803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar input_files = list(opts.input_files) 275803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar for filelist in opts.filelists: 276803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar f = open(filelist) 277803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar try: 278803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar for ln in f: 279803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar ln = ln.strip() 280803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar if ln: 281803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar input_files.append(ln) 282803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar finally: 283803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar f.close() 284803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar input_files.sort() 285803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 286803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar if not input_files: 287803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar parser.error("No input files!") 288803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 289803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar print '%s: note: fuzzing %d files.' % (sys.argv[0], len(input_files)) 290803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 291803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar # Make sure the log directory exists if used. 292803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar if opts.log_dir: 293803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar if not os.path.exists(opts.log_dir): 294803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar try: 295803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar os.mkdir(opts.log_dir) 296803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar except OSError: 297803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar print "%s: error: log directory couldn't be created!" % ( 298803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar sys.argv[0],) 299803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar raise SystemExit,1 300803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 301803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar # Get the list if insert/replacement strings. 302803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar replacements = list(opts.replacement_chars) 303803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar replacements.extend(opts.replacement_strings) 304c53a844e22c5a635c64ede7e1ac31fb035e52701Daniel Dunbar for replacement_list in opts.replacement_lists: 305c53a844e22c5a635c64ede7e1ac31fb035e52701Daniel Dunbar f = open(replacement_list) 306c53a844e22c5a635c64ede7e1ac31fb035e52701Daniel Dunbar try: 307c53a844e22c5a635c64ede7e1ac31fb035e52701Daniel Dunbar for ln in f: 308c53a844e22c5a635c64ede7e1ac31fb035e52701Daniel Dunbar ln = ln[:-1] 309c53a844e22c5a635c64ede7e1ac31fb035e52701Daniel Dunbar if ln: 310c53a844e22c5a635c64ede7e1ac31fb035e52701Daniel Dunbar replacements.append(ln) 311c53a844e22c5a635c64ede7e1ac31fb035e52701Daniel Dunbar finally: 312c53a844e22c5a635c64ede7e1ac31fb035e52701Daniel Dunbar f.close() 313c53a844e22c5a635c64ede7e1ac31fb035e52701Daniel Dunbar 314c53a844e22c5a635c64ede7e1ac31fb035e52701Daniel Dunbar # Unique and order the replacement list. 315c53a844e22c5a635c64ede7e1ac31fb035e52701Daniel Dunbar replacements = list(set(replacements)) 316c53a844e22c5a635c64ede7e1ac31fb035e52701Daniel Dunbar replacements.sort() 317803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 318803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar # Create the test generator. 319803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar tg = TestGenerator(input_files, opts.enable_delete, opts.enable_insert, 320803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar opts.enable_replace, replacements, opts.pick_input) 321803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 322803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar print '%s: note: %d input bytes.' % (sys.argv[0], tg.num_positions) 323803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar print '%s: note: %d total tests.' % (sys.argv[0], tg.num_tests) 324803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar if opts.test is not None: 325803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar it = [opts.test] 326803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar elif opts.max_tests is not None: 327803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar it = itertools.imap(random.randrange, 328803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar itertools.repeat(tg.num_tests, opts.max_tests)) 329803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar else: 330803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar it = itertools.imap(random.randrange, itertools.repeat(tg.num_tests)) 331803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar for test in it: 332803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar t = tg.get_test(test) 333803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 334803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar if opts.verbose: 335803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar print '%s: note: running test %d: %r' % (sys.argv[0], test, t) 336803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar ta = TestApplication(tg, t) 337803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar try: 338803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar ta.apply() 3392a6dc143b6da657ca0839acec1ac36e12cd1b295Argyrios Kyrtzidis test_result = run_one_test(ta, test, input_files, args) 3402a6dc143b6da657ca0839acec1ac36e12cd1b295Argyrios Kyrtzidis if not test_result and opts.stop_on_fail: 3412a6dc143b6da657ca0839acec1ac36e12cd1b295Argyrios Kyrtzidis opts.revert = False 3422a6dc143b6da657ca0839acec1ac36e12cd1b295Argyrios Kyrtzidis sys.exit(1) 343803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar finally: 344803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar if opts.revert: 345803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar ta.revert() 346803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 347803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar sys.stdout.flush() 348803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar 349803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbarif __name__ == '__main__': 350803588053d79b35a1c829fd00c09dae5cd4a2c7dDaniel Dunbar main() 351