3 # ################################################################
4 # Copyright (c) Meta Platforms, Inc. and affiliates.
7 # This source code is licensed under both the BSD-style license (found in the
8 # LICENSE file in the root directory of this source tree) and the GPLv2 (found
9 # in the COPYING file in the root directory of this source tree).
10 # You may select, at your option, one of the above-listed licenses.
11 # ##########################################################################
25 return os.path.abspath(os.path.join(a, *p))
28 class InputType(object):
34 class FrameType(object):
39 class TargetInfo(object):
40 def __init__(self, input_type, frame_type=FrameType.ZSTD):
41 self.input_type = input_type
42 self.frame_type = frame_type
46 FUZZ_DIR = os.path.abspath(os.path.dirname(__file__))
47 CORPORA_DIR = abs_join(FUZZ_DIR, 'corpora')
49 'simple_round_trip': TargetInfo(InputType.RAW_DATA),
50 'stream_round_trip': TargetInfo(InputType.RAW_DATA),
51 'block_round_trip': TargetInfo(InputType.RAW_DATA, FrameType.BLOCK),
52 'simple_decompress': TargetInfo(InputType.COMPRESSED_DATA),
53 'stream_decompress': TargetInfo(InputType.COMPRESSED_DATA),
54 'block_decompress': TargetInfo(InputType.COMPRESSED_DATA, FrameType.BLOCK),
55 'dictionary_round_trip': TargetInfo(InputType.RAW_DATA),
56 'dictionary_decompress': TargetInfo(InputType.COMPRESSED_DATA),
57 'zstd_frame_info': TargetInfo(InputType.COMPRESSED_DATA),
58 'simple_compress': TargetInfo(InputType.RAW_DATA),
59 'dictionary_loader': TargetInfo(InputType.DICTIONARY_DATA),
60 'raw_dictionary_round_trip': TargetInfo(InputType.RAW_DATA),
61 'dictionary_stream_round_trip': TargetInfo(InputType.RAW_DATA),
62 'decompress_dstSize_tooSmall': TargetInfo(InputType.RAW_DATA),
63 'fse_read_ncount': TargetInfo(InputType.RAW_DATA),
64 'sequence_compression_api': TargetInfo(InputType.RAW_DATA),
65 'seekable_roundtrip': TargetInfo(InputType.RAW_DATA),
66 'huf_round_trip': TargetInfo(InputType.RAW_DATA),
67 'huf_decompress': TargetInfo(InputType.RAW_DATA),
68 'decompress_cross_format': TargetInfo(InputType.RAW_DATA),
69 'generate_sequences': TargetInfo(InputType.RAW_DATA),
71 TARGETS = list(TARGET_INFO.keys())
72 ALL_TARGETS = TARGETS + ['all']
73 FUZZ_RNG_SEED_SIZE = 4
75 # Standard environment variables
76 CC = os.environ.get('CC', 'cc')
77 CXX = os.environ.get('CXX', 'c++')
78 CPPFLAGS = os.environ.get('CPPFLAGS', '')
79 CFLAGS = os.environ.get('CFLAGS', '-O3')
80 CXXFLAGS = os.environ.get('CXXFLAGS', CFLAGS)
81 LDFLAGS = os.environ.get('LDFLAGS', '')
82 MFLAGS = os.environ.get('MFLAGS', '-j')
83 THIRD_PARTY_SEQ_PROD_OBJ = os.environ.get('THIRD_PARTY_SEQ_PROD_OBJ', '')
85 # Fuzzing environment variables
86 LIB_FUZZING_ENGINE = os.environ.get('LIB_FUZZING_ENGINE', 'libregression.a')
87 AFL_FUZZ = os.environ.get('AFL_FUZZ', 'afl-fuzz')
88 DECODECORPUS = os.environ.get('DECODECORPUS',
89 abs_join(FUZZ_DIR, '..', 'decodecorpus'))
90 ZSTD = os.environ.get('ZSTD', abs_join(FUZZ_DIR, '..', '..', 'zstd'))
92 # Sanitizer environment variables
93 MSAN_EXTRA_CPPFLAGS = os.environ.get('MSAN_EXTRA_CPPFLAGS', '')
94 MSAN_EXTRA_CFLAGS = os.environ.get('MSAN_EXTRA_CFLAGS', '')
95 MSAN_EXTRA_CXXFLAGS = os.environ.get('MSAN_EXTRA_CXXFLAGS', '')
96 MSAN_EXTRA_LDFLAGS = os.environ.get('MSAN_EXTRA_LDFLAGS', '')
100 d = os.path.abspath(r)
101 if not os.path.isdir(d):
107 d = os.path.abspath(r)
108 if not os.path.isdir(d):
113 @contextlib.contextmanager
115 dirpath = tempfile.mkdtemp()
119 shutil.rmtree(dirpath, ignore_errors=True)
122 def parse_targets(in_targets):
124 for target in in_targets:
128 targets = targets.union(TARGETS)
129 elif target in TARGETS:
132 raise RuntimeError('{} is not a valid target'.format(target))
136 def targets_parser(args, description):
137 parser = argparse.ArgumentParser(prog=args.pop(0), description=description)
142 help='Fuzz target(s) to build {{{}}}'.format(', '.join(ALL_TARGETS)))
143 args, extra = parser.parse_known_args(args)
146 args.TARGET = parse_targets(args.TARGET)
151 def parse_env_flags(args, flags):
153 Look for flags set by environment variables.
155 san_flags = ','.join(re.findall('-fsanitize=((?:[a-z]+,?)+)', flags))
156 nosan_flags = ','.join(re.findall('-fno-sanitize=((?:[a-z]+,?)+)', flags))
158 def set_sanitizer(sanitizer, default, san, nosan):
159 if sanitizer in san and sanitizer in nosan:
160 raise RuntimeError('-fno-sanitize={s} and -fsanitize={s} passed'.
164 if sanitizer in nosan:
168 san = set(san_flags.split(','))
169 nosan = set(nosan_flags.split(','))
171 args.asan = set_sanitizer('address', args.asan, san, nosan)
172 args.msan = set_sanitizer('memory', args.msan, san, nosan)
173 args.ubsan = set_sanitizer('undefined', args.ubsan, san, nosan)
175 args.sanitize = args.asan or args.msan or args.ubsan
180 def compiler_version(cc, cxx):
182 Determines the compiler and version.
183 Only works for clang and gcc.
185 cc_version_bytes = subprocess.check_output([cc, "--version"])
186 cxx_version_bytes = subprocess.check_output([cxx, "--version"])
189 print("{} --version:\n{}".format(cc, cc_version_bytes.decode('ascii')))
190 if b'clang' in cc_version_bytes:
191 assert(b'clang' in cxx_version_bytes)
193 elif b'gcc' in cc_version_bytes or b'GCC' in cc_version_bytes:
194 assert(b'gcc' in cxx_version_bytes or b'g++' in cxx_version_bytes)
196 if compiler is not None:
197 version_regex = b'([0-9]+)\.([0-9]+)\.([0-9]+)'
198 version_match = re.search(version_regex, cc_version_bytes)
199 version = tuple(int(version_match.group(i)) for i in range(1, 4))
200 return compiler, version
203 def overflow_ubsan_flags(cc, cxx):
204 compiler, version = compiler_version(cc, cxx)
205 if compiler == 'gcc' and version < (8, 0, 0):
206 return ['-fno-sanitize=signed-integer-overflow']
207 if compiler == 'gcc' or (compiler == 'clang' and version >= (5, 0, 0)):
208 return ['-fno-sanitize=pointer-overflow']
212 def build_parser(args):
214 Cleans the repository and builds a fuzz target (or all).
215 Many flags default to environment variables (default says $X='y').
216 Options that aren't enabling features default to the correct values for
218 Enable sanitizers with --enable-*san.
219 For regression testing just build.
220 For libFuzzer set LIB_FUZZING_ENGINE and pass --enable-coverage.
221 For AFL set CC and CXX to AFL's compilers and set
222 LIB_FUZZING_ENGINE='libregression.a'.
224 parser = argparse.ArgumentParser(prog=args.pop(0), description=description)
226 '--lib-fuzzing-engine',
227 dest='lib_fuzzing_engine',
229 default=LIB_FUZZING_ENGINE,
230 help=('The fuzzing engine to use e.g. /path/to/libFuzzer.a '
231 "(default: $LIB_FUZZING_ENGINE='{})".format(LIB_FUZZING_ENGINE)))
233 fuzz_group = parser.add_mutually_exclusive_group()
234 fuzz_group.add_argument(
238 help='Enable coverage instrumentation (-fsanitize-coverage)')
239 fuzz_group.add_argument(
243 help=('Enable clang fuzzer (-fsanitize=fuzzer). When enabled '
244 'LIB_FUZZING_ENGINE is ignored')
248 '--enable-asan', dest='asan', action='store_true', help='Enable UBSAN')
255 '--disable-ubsan-pointer-overflow',
256 dest='ubsan_pointer_overflow',
257 action='store_false',
258 help='Disable UBSAN pointer overflow check (known failure)')
260 '--enable-msan', dest='msan', action='store_true', help='Enable MSAN')
262 '--enable-msan-track-origins', dest='msan_track_origins',
263 action='store_true', help='Enable MSAN origin tracking')
265 '--msan-extra-cppflags',
266 dest='msan_extra_cppflags',
268 default=MSAN_EXTRA_CPPFLAGS,
269 help="Extra CPPFLAGS for MSAN (default: $MSAN_EXTRA_CPPFLAGS='{}')".
270 format(MSAN_EXTRA_CPPFLAGS))
272 '--msan-extra-cflags',
273 dest='msan_extra_cflags',
275 default=MSAN_EXTRA_CFLAGS,
276 help="Extra CFLAGS for MSAN (default: $MSAN_EXTRA_CFLAGS='{}')".format(
279 '--msan-extra-cxxflags',
280 dest='msan_extra_cxxflags',
282 default=MSAN_EXTRA_CXXFLAGS,
283 help="Extra CXXFLAGS for MSAN (default: $MSAN_EXTRA_CXXFLAGS='{}')".
284 format(MSAN_EXTRA_CXXFLAGS))
286 '--msan-extra-ldflags',
287 dest='msan_extra_ldflags',
289 default=MSAN_EXTRA_LDFLAGS,
290 help="Extra LDFLAGS for MSAN (default: $MSAN_EXTRA_LDFLAGS='{}')".
291 format(MSAN_EXTRA_LDFLAGS))
293 '--enable-sanitize-recover',
294 dest='sanitize_recover',
296 help='Non-fatal sanitizer errors where possible')
302 help='Set DEBUGLEVEL (default: 1)')
304 '--force-memory-access',
305 dest='memory_access',
308 help='Set MEM_FORCE_MEMORY_ACCESS (default: 0)')
310 '--fuzz-rng-seed-size',
311 dest='fuzz_rng_seed_size',
314 help='Set FUZZ_RNG_SEED_SIZE (default: 4)')
316 '--disable-fuzzing-mode',
318 action='store_false',
319 help='Do not define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION')
321 '--enable-stateful-fuzzing',
322 dest='stateful_fuzzing',
324 help='Reuse contexts between runs (makes reproduction impossible)')
327 dest='third_party_seq_prod_obj',
329 default=THIRD_PARTY_SEQ_PROD_OBJ,
330 help='Path to an object file with symbols for fuzzing your sequence producer plugin.')
336 help="CC (default: $CC='{}')".format(CC))
342 help="CXX (default: $CXX='{}')".format(CXX))
348 help="CPPFLAGS (default: $CPPFLAGS='{}')".format(CPPFLAGS))
354 help="CFLAGS (default: $CFLAGS='{}')".format(CFLAGS))
360 help="CXXFLAGS (default: $CXXFLAGS='{}')".format(CXXFLAGS))
366 help="LDFLAGS (default: $LDFLAGS='{}')".format(LDFLAGS))
372 help="Extra Make flags (default: $MFLAGS='{}')".format(MFLAGS))
377 help='Fuzz target(s) to build {{{}}}'.format(', '.join(ALL_TARGETS))
379 args = parser.parse_args(args)
380 args = parse_env_flags(args, ' '.join(
381 [args.cppflags, args.cflags, args.cxxflags, args.ldflags]))
383 # Check option sanity
384 if args.msan and (args.asan or args.ubsan):
385 raise RuntimeError('MSAN may not be used with any other sanitizers')
386 if args.msan_track_origins and not args.msan:
387 raise RuntimeError('--enable-msan-track-origins requires MSAN')
388 if args.sanitize_recover and not args.sanitize:
389 raise RuntimeError('--enable-sanitize-recover but no sanitizers used')
396 args = build_parser(args)
397 except Exception as e:
400 # The compilation flags we are setting
401 targets = args.TARGET
404 cppflags = shlex.split(args.cppflags)
405 cflags = shlex.split(args.cflags)
406 ldflags = shlex.split(args.ldflags)
407 cxxflags = shlex.split(args.cxxflags)
408 mflags = shlex.split(args.mflags)
409 # Flags to be added to both cflags and cxxflags
412 '-Wno-error=declaration-after-statement',
413 '-Wno-error=c++-compat',
414 '-Wno-error=deprecated' # C files are sometimes compiled with CXX
418 '-DDEBUGLEVEL={}'.format(args.debug),
419 '-DMEM_FORCE_MEMORY_ACCESS={}'.format(args.memory_access),
420 '-DFUZZ_RNG_SEED_SIZE={}'.format(args.fuzz_rng_seed_size),
423 # Set flags for options
424 assert not (args.fuzzer and args.coverage)
427 '-fsanitize-coverage=trace-pc-guard,indirect-calls,trace-cmp'
430 common_flags += ['-fsanitize=fuzzer']
431 args.lib_fuzzing_engine = ''
433 mflags += ['LIB_FUZZING_ENGINE={}'.format(args.lib_fuzzing_engine)]
435 if args.sanitize_recover:
436 recover_flags = ['-fsanitize-recover=all']
438 recover_flags = ['-fno-sanitize-recover=all']
440 common_flags += recover_flags
443 msan_flags = ['-fsanitize=memory']
444 if args.msan_track_origins:
445 msan_flags += ['-fsanitize-memory-track-origins']
446 common_flags += msan_flags
447 # Append extra MSAN flags (it might require special setup)
448 cppflags += [args.msan_extra_cppflags]
449 cflags += [args.msan_extra_cflags]
450 cxxflags += [args.msan_extra_cxxflags]
451 ldflags += [args.msan_extra_ldflags]
454 common_flags += ['-fsanitize=address']
457 ubsan_flags = ['-fsanitize=undefined']
458 if not args.ubsan_pointer_overflow:
459 ubsan_flags += overflow_ubsan_flags(cc, cxx)
460 common_flags += ubsan_flags
462 if args.stateful_fuzzing:
463 cppflags += ['-DSTATEFUL_FUZZING']
465 if args.third_party_seq_prod_obj:
466 cppflags += ['-DFUZZ_THIRD_PARTY_SEQ_PROD']
467 mflags += ['THIRD_PARTY_SEQ_PROD_OBJ={}'.format(args.third_party_seq_prod_obj)]
469 if args.fuzzing_mode:
470 cppflags += ['-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION']
472 if args.lib_fuzzing_engine == 'libregression.a':
473 targets = ['libregression.a'] + targets
475 # Append the common flags
476 cflags += common_flags
477 cxxflags += common_flags
479 # Prepare the flags for Make
480 cc_str = "CC={}".format(cc)
481 cxx_str = "CXX={}".format(cxx)
482 cppflags_str = "CPPFLAGS={}".format(' '.join(cppflags))
483 cflags_str = "CFLAGS={}".format(' '.join(cflags))
484 cxxflags_str = "CXXFLAGS={}".format(' '.join(cxxflags))
485 ldflags_str = "LDFLAGS={}".format(' '.join(ldflags))
488 print('MFLAGS={}'.format(' '.join(mflags)))
497 clean_cmd = ['make', 'clean'] + mflags
498 print(' '.join(clean_cmd))
499 subprocess.check_call(clean_cmd)
510 print(' '.join(build_cmd))
511 subprocess.check_call(build_cmd)
515 def libfuzzer_parser(args):
517 Runs a libfuzzer binary.
518 Passes all extra arguments to libfuzzer.
519 The fuzzer should have been build with LIB_FUZZING_ENGINE pointing to
521 Generates output in the CORPORA directory, puts crashes in the ARTIFACT
522 directory, and takes extra input from the SEED directory.
523 To merge AFL's output pass the SEED as AFL's output directory and pass
526 parser = argparse.ArgumentParser(prog=args.pop(0), description=description)
530 help='Override the default corpora dir (default: {})'.format(
531 abs_join(CORPORA_DIR, 'TARGET')))
535 help='Override the default artifact dir (default: {})'.format(
536 abs_join(CORPORA_DIR, 'TARGET-crash')))
540 help='Override the default seed dir (default: {})'.format(
541 abs_join(CORPORA_DIR, 'TARGET-seed')))
545 help='Fuzz target(s) to build {{{}}}'.format(', '.join(TARGETS)))
546 args, extra = parser.parse_known_args(args)
549 if args.TARGET and args.TARGET not in TARGETS:
550 raise RuntimeError('{} is not a valid target'.format(args.TARGET))
555 def libfuzzer(target, corpora=None, artifact=None, seed=None, extra_args=None):
557 corpora = abs_join(CORPORA_DIR, target)
559 artifact = abs_join(CORPORA_DIR, '{}-crash'.format(target))
561 seed = abs_join(CORPORA_DIR, '{}-seed'.format(target))
562 if extra_args is None:
565 target = abs_join(FUZZ_DIR, target)
567 corpora = [create(corpora)]
568 artifact = create(artifact)
571 corpora += [artifact]
575 cmd = [target, '-artifact_prefix={}/'.format(artifact)]
576 cmd += corpora + extra_args
578 subprocess.check_call(cmd)
581 def libfuzzer_cmd(args):
583 args = libfuzzer_parser(args)
584 except Exception as e:
587 libfuzzer(args.TARGET, args.corpora, args.artifact, args.seed, args.extra)
591 def afl_parser(args):
593 Runs an afl-fuzz job.
594 Passes all extra arguments to afl-fuzz.
595 The fuzzer should have been built with CC/CXX set to the AFL compilers,
596 and with LIB_FUZZING_ENGINE='libregression.a'.
597 Takes input from CORPORA and writes output to OUTPUT.
598 Uses AFL_FUZZ as the binary (set from flag or environment variable).
600 parser = argparse.ArgumentParser(prog=args.pop(0), description=description)
604 help='Override the default corpora dir (default: {})'.format(
605 abs_join(CORPORA_DIR, 'TARGET')))
609 help='Override the default AFL output dir (default: {})'.format(
610 abs_join(CORPORA_DIR, 'TARGET-afl')))
615 help='AFL_FUZZ (default: $AFL_FUZZ={})'.format(AFL_FUZZ))
619 help='Fuzz target(s) to build {{{}}}'.format(', '.join(TARGETS)))
620 args, extra = parser.parse_known_args(args)
623 if args.TARGET and args.TARGET not in TARGETS:
624 raise RuntimeError('{} is not a valid target'.format(args.TARGET))
627 args.corpora = abs_join(CORPORA_DIR, args.TARGET)
629 args.output = abs_join(CORPORA_DIR, '{}-afl'.format(args.TARGET))
636 args = afl_parser(args)
637 except Exception as e:
640 target = abs_join(FUZZ_DIR, args.TARGET)
642 corpora = create(args.corpora)
643 output = create(args.output)
645 cmd = [args.afl_fuzz, '-i', corpora, '-o', output] + args.extra
646 cmd += [target, '@@']
652 def regression(args):
655 Runs one or more regression tests.
656 The fuzzer should have been built with
657 LIB_FUZZING_ENGINE='libregression.a'.
658 Takes input from CORPORA.
660 args = targets_parser(args, description)
661 except Exception as e:
664 for target in args.TARGET:
665 corpora = create(abs_join(CORPORA_DIR, target))
666 target = abs_join(FUZZ_DIR, target)
667 cmd = [target, corpora]
669 subprocess.check_call(cmd)
673 def gen_parser(args):
675 Generate a seed corpus appropriate for TARGET with data generated with
677 The fuzz inputs are prepended with a seed before the zstd data, so the
678 output of decodecorpus shouldn't be used directly.
679 Generates NUMBER samples prepended with FUZZ_RNG_SEED_SIZE random bytes and
680 puts the output in SEED.
681 DECODECORPUS is the decodecorpus binary, and must already be built.
683 parser = argparse.ArgumentParser(prog=args.pop(0), description=description)
689 help='Number of samples to generate')
694 help='Maximum sample size to generate')
698 help='Override the default seed dir (default: {})'.format(
699 abs_join(CORPORA_DIR, 'TARGET-seed')))
703 default=DECODECORPUS,
704 help="decodecorpus binary (default: $DECODECORPUS='{}')".format(
710 help="zstd binary (default: $ZSTD='{}')".format(ZSTD))
712 '--fuzz-rng-seed-size',
715 help="FUZZ_RNG_SEED_SIZE used for generate the samples (must match)"
720 help='Fuzz target(s) to build {{{}}}'.format(', '.join(TARGETS)))
721 args, extra = parser.parse_known_args(args)
724 if args.TARGET and args.TARGET not in TARGETS:
725 raise RuntimeError('{} is not a valid target'.format(args.TARGET))
728 args.seed = abs_join(CORPORA_DIR, '{}-seed'.format(args.TARGET))
730 if not os.path.isfile(args.decodecorpus):
731 raise RuntimeError("{} is not a file run 'make -C {} decodecorpus'".
732 format(args.decodecorpus, abs_join(FUZZ_DIR, '..')))
739 args = gen_parser(args)
740 except Exception as e:
744 seed = create(args.seed)
745 with tmpdir() as compressed, tmpdir() as decompressed, tmpdir() as dict:
746 info = TARGET_INFO[args.TARGET]
748 if info.input_type == InputType.DICTIONARY_DATA:
749 number = max(args.number, 1000)
754 '-n{}'.format(args.number),
755 '-p{}/'.format(compressed),
756 '-o{}'.format(decompressed),
759 if info.frame_type == FrameType.BLOCK:
762 '--max-block-size-log={}'.format(min(args.max_size_log, 17))
765 cmd += ['--max-content-size-log={}'.format(args.max_size_log)]
768 subprocess.check_call(cmd)
770 if info.input_type == InputType.RAW_DATA:
771 print('using decompressed data in {}'.format(decompressed))
772 samples = decompressed
773 elif info.input_type == InputType.COMPRESSED_DATA:
774 print('using compressed data in {}'.format(compressed))
777 assert info.input_type == InputType.DICTIONARY_DATA
778 print('making dictionary data from {}'.format(decompressed))
780 min_dict_size_log = 9
781 max_dict_size_log = max(min_dict_size_log + 1, args.max_size_log)
782 for dict_size_log in range(min_dict_size_log, max_dict_size_log):
783 dict_size = 1 << dict_size_log
788 '--maxdict={}'.format(dict_size),
789 '-o', abs_join(dict, '{}.zstd-dict'.format(dict_size))
792 subprocess.check_call(cmd)
794 # Copy the samples over and prepend the RNG seeds
795 for name in os.listdir(samples):
796 samplename = abs_join(samples, name)
797 outname = abs_join(seed, name)
798 with open(samplename, 'rb') as sample:
799 with open(outname, 'wb') as out:
801 chunk = sample.read(CHUNK_SIZE)
802 while len(chunk) > 0:
804 chunk = sample.read(CHUNK_SIZE)
811 Runs a libfuzzer fuzzer with -merge=1 to build a minimal corpus in
812 TARGET_seed_corpus. All extra args are passed to libfuzzer.
814 args = targets_parser(args, description)
815 except Exception as e:
819 for target in args.TARGET:
820 # Merge the corpus + anything else into the seed_corpus
821 corpus = abs_join(CORPORA_DIR, target)
822 seed_corpus = abs_join(CORPORA_DIR, "{}_seed_corpus".format(target))
823 extra_args = [corpus, "-merge=1"] + args.extra
824 libfuzzer(target, corpora=seed_corpus, extra_args=extra_args)
825 seeds = set(os.listdir(seed_corpus))
826 # Copy all crashes directly into the seed_corpus if not already present
827 crashes = abs_join(CORPORA_DIR, '{}-crash'.format(target))
828 for crash in os.listdir(crashes):
829 if crash not in seeds:
830 shutil.copy(abs_join(crashes, crash), seed_corpus)
837 Zips up the seed corpus.
839 args = targets_parser(args, description)
840 except Exception as e:
844 for target in args.TARGET:
845 # Zip the seed_corpus
846 seed_corpus = abs_join(CORPORA_DIR, "{}_seed_corpus".format(target))
847 zip_file = "{}.zip".format(seed_corpus)
848 cmd = ["zip", "-r", "-q", "-j", "-9", zip_file, "."]
850 subprocess.check_call(cmd, cwd=seed_corpus)
854 print("\n".join(TARGETS))
857 def short_help(args):
859 print("Usage: {} [OPTIONS] COMMAND [ARGS]...\n".format(name))
864 print("\tfuzzing helpers (select a command and pass -h for help)\n")
866 print("\t-h, --help\tPrint this message")
869 print("\tbuild\t\tBuild a fuzzer")
870 print("\tlibfuzzer\tRun a libFuzzer fuzzer")
871 print("\tafl\t\tRun an AFL fuzzer")
872 print("\tregression\tRun a regression test")
873 print("\tgen\t\tGenerate a seed corpus for a fuzzer")
874 print("\tminimize\tMinimize the test corpora")
875 print("\tzip\t\tZip the minimized corpora up")
876 print("\tlist\t\tList the available targets")
884 if args[1] == '-h' or args[1] == '--help' or args[1] == '-H':
887 command = args.pop(1)
888 args[0] = "{} {}".format(args[0], command)
889 if command == "build":
891 if command == "libfuzzer":
892 return libfuzzer_cmd(args)
893 if command == "regression":
894 return regression(args)
899 if command == "minimize":
900 return minimize(args)
903 if command == "list":
904 return list_cmd(args)
906 print("Error: No such command {} (pass -h for help)".format(command))
910 if __name__ == "__main__":