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),
69 TARGETS = list(TARGET_INFO.keys())
70 ALL_TARGETS = TARGETS + ['all']
71 FUZZ_RNG_SEED_SIZE = 4
73 # Standard environment variables
74 CC = os.environ.get('CC', 'cc')
75 CXX = os.environ.get('CXX', 'c++')
76 CPPFLAGS = os.environ.get('CPPFLAGS', '')
77 CFLAGS = os.environ.get('CFLAGS', '-O3')
78 CXXFLAGS = os.environ.get('CXXFLAGS', CFLAGS)
79 LDFLAGS = os.environ.get('LDFLAGS', '')
80 MFLAGS = os.environ.get('MFLAGS', '-j')
81 THIRD_PARTY_SEQ_PROD_OBJ = os.environ.get('THIRD_PARTY_SEQ_PROD_OBJ', '')
83 # Fuzzing environment variables
84 LIB_FUZZING_ENGINE = os.environ.get('LIB_FUZZING_ENGINE', 'libregression.a')
85 AFL_FUZZ = os.environ.get('AFL_FUZZ', 'afl-fuzz')
86 DECODECORPUS = os.environ.get('DECODECORPUS',
87 abs_join(FUZZ_DIR, '..', 'decodecorpus'))
88 ZSTD = os.environ.get('ZSTD', abs_join(FUZZ_DIR, '..', '..', 'zstd'))
90 # Sanitizer environment variables
91 MSAN_EXTRA_CPPFLAGS = os.environ.get('MSAN_EXTRA_CPPFLAGS', '')
92 MSAN_EXTRA_CFLAGS = os.environ.get('MSAN_EXTRA_CFLAGS', '')
93 MSAN_EXTRA_CXXFLAGS = os.environ.get('MSAN_EXTRA_CXXFLAGS', '')
94 MSAN_EXTRA_LDFLAGS = os.environ.get('MSAN_EXTRA_LDFLAGS', '')
98 d = os.path.abspath(r)
99 if not os.path.isdir(d):
105 d = os.path.abspath(r)
106 if not os.path.isdir(d):
111 @contextlib.contextmanager
113 dirpath = tempfile.mkdtemp()
117 shutil.rmtree(dirpath, ignore_errors=True)
120 def parse_targets(in_targets):
122 for target in in_targets:
126 targets = targets.union(TARGETS)
127 elif target in TARGETS:
130 raise RuntimeError('{} is not a valid target'.format(target))
134 def targets_parser(args, description):
135 parser = argparse.ArgumentParser(prog=args.pop(0), description=description)
140 help='Fuzz target(s) to build {{{}}}'.format(', '.join(ALL_TARGETS)))
141 args, extra = parser.parse_known_args(args)
144 args.TARGET = parse_targets(args.TARGET)
149 def parse_env_flags(args, flags):
151 Look for flags set by environment variables.
153 san_flags = ','.join(re.findall('-fsanitize=((?:[a-z]+,?)+)', flags))
154 nosan_flags = ','.join(re.findall('-fno-sanitize=((?:[a-z]+,?)+)', flags))
156 def set_sanitizer(sanitizer, default, san, nosan):
157 if sanitizer in san and sanitizer in nosan:
158 raise RuntimeError('-fno-sanitize={s} and -fsanitize={s} passed'.
162 if sanitizer in nosan:
166 san = set(san_flags.split(','))
167 nosan = set(nosan_flags.split(','))
169 args.asan = set_sanitizer('address', args.asan, san, nosan)
170 args.msan = set_sanitizer('memory', args.msan, san, nosan)
171 args.ubsan = set_sanitizer('undefined', args.ubsan, san, nosan)
173 args.sanitize = args.asan or args.msan or args.ubsan
178 def compiler_version(cc, cxx):
180 Determines the compiler and version.
181 Only works for clang and gcc.
183 cc_version_bytes = subprocess.check_output([cc, "--version"])
184 cxx_version_bytes = subprocess.check_output([cxx, "--version"])
187 print("{} --version:\n{}".format(cc, cc_version_bytes.decode('ascii')))
188 if b'clang' in cc_version_bytes:
189 assert(b'clang' in cxx_version_bytes)
191 elif b'gcc' in cc_version_bytes or b'GCC' in cc_version_bytes:
192 assert(b'gcc' in cxx_version_bytes or b'g++' in cxx_version_bytes)
194 if compiler is not None:
195 version_regex = b'([0-9]+)\.([0-9]+)\.([0-9]+)'
196 version_match = re.search(version_regex, cc_version_bytes)
197 version = tuple(int(version_match.group(i)) for i in range(1, 4))
198 return compiler, version
201 def overflow_ubsan_flags(cc, cxx):
202 compiler, version = compiler_version(cc, cxx)
203 if compiler == 'gcc' and version < (8, 0, 0):
204 return ['-fno-sanitize=signed-integer-overflow']
205 if compiler == 'gcc' or (compiler == 'clang' and version >= (5, 0, 0)):
206 return ['-fno-sanitize=pointer-overflow']
210 def build_parser(args):
212 Cleans the repository and builds a fuzz target (or all).
213 Many flags default to environment variables (default says $X='y').
214 Options that aren't enabling features default to the correct values for
216 Enable sanitizers with --enable-*san.
217 For regression testing just build.
218 For libFuzzer set LIB_FUZZING_ENGINE and pass --enable-coverage.
219 For AFL set CC and CXX to AFL's compilers and set
220 LIB_FUZZING_ENGINE='libregression.a'.
222 parser = argparse.ArgumentParser(prog=args.pop(0), description=description)
224 '--lib-fuzzing-engine',
225 dest='lib_fuzzing_engine',
227 default=LIB_FUZZING_ENGINE,
228 help=('The fuzzing engine to use e.g. /path/to/libFuzzer.a '
229 "(default: $LIB_FUZZING_ENGINE='{})".format(LIB_FUZZING_ENGINE)))
231 fuzz_group = parser.add_mutually_exclusive_group()
232 fuzz_group.add_argument(
236 help='Enable coverage instrumentation (-fsanitize-coverage)')
237 fuzz_group.add_argument(
241 help=('Enable clang fuzzer (-fsanitize=fuzzer). When enabled '
242 'LIB_FUZZING_ENGINE is ignored')
246 '--enable-asan', dest='asan', action='store_true', help='Enable UBSAN')
253 '--enable-ubsan-pointer-overflow',
254 dest='ubsan_pointer_overflow',
256 help='Enable UBSAN pointer overflow check (known failure)')
258 '--enable-msan', dest='msan', action='store_true', help='Enable MSAN')
260 '--enable-msan-track-origins', dest='msan_track_origins',
261 action='store_true', help='Enable MSAN origin tracking')
263 '--msan-extra-cppflags',
264 dest='msan_extra_cppflags',
266 default=MSAN_EXTRA_CPPFLAGS,
267 help="Extra CPPFLAGS for MSAN (default: $MSAN_EXTRA_CPPFLAGS='{}')".
268 format(MSAN_EXTRA_CPPFLAGS))
270 '--msan-extra-cflags',
271 dest='msan_extra_cflags',
273 default=MSAN_EXTRA_CFLAGS,
274 help="Extra CFLAGS for MSAN (default: $MSAN_EXTRA_CFLAGS='{}')".format(
277 '--msan-extra-cxxflags',
278 dest='msan_extra_cxxflags',
280 default=MSAN_EXTRA_CXXFLAGS,
281 help="Extra CXXFLAGS for MSAN (default: $MSAN_EXTRA_CXXFLAGS='{}')".
282 format(MSAN_EXTRA_CXXFLAGS))
284 '--msan-extra-ldflags',
285 dest='msan_extra_ldflags',
287 default=MSAN_EXTRA_LDFLAGS,
288 help="Extra LDFLAGS for MSAN (default: $MSAN_EXTRA_LDFLAGS='{}')".
289 format(MSAN_EXTRA_LDFLAGS))
291 '--enable-sanitize-recover',
292 dest='sanitize_recover',
294 help='Non-fatal sanitizer errors where possible')
300 help='Set DEBUGLEVEL (default: 1)')
302 '--force-memory-access',
303 dest='memory_access',
306 help='Set MEM_FORCE_MEMORY_ACCESS (default: 0)')
308 '--fuzz-rng-seed-size',
309 dest='fuzz_rng_seed_size',
312 help='Set FUZZ_RNG_SEED_SIZE (default: 4)')
314 '--disable-fuzzing-mode',
316 action='store_false',
317 help='Do not define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION')
319 '--enable-stateful-fuzzing',
320 dest='stateful_fuzzing',
322 help='Reuse contexts between runs (makes reproduction impossible)')
325 dest='third_party_seq_prod_obj',
327 default=THIRD_PARTY_SEQ_PROD_OBJ,
328 help='Path to an object file with symbols for fuzzing your sequence producer plugin.')
334 help="CC (default: $CC='{}')".format(CC))
340 help="CXX (default: $CXX='{}')".format(CXX))
346 help="CPPFLAGS (default: $CPPFLAGS='{}')".format(CPPFLAGS))
352 help="CFLAGS (default: $CFLAGS='{}')".format(CFLAGS))
358 help="CXXFLAGS (default: $CXXFLAGS='{}')".format(CXXFLAGS))
364 help="LDFLAGS (default: $LDFLAGS='{}')".format(LDFLAGS))
370 help="Extra Make flags (default: $MFLAGS='{}')".format(MFLAGS))
375 help='Fuzz target(s) to build {{{}}}'.format(', '.join(ALL_TARGETS))
377 args = parser.parse_args(args)
378 args = parse_env_flags(args, ' '.join(
379 [args.cppflags, args.cflags, args.cxxflags, args.ldflags]))
381 # Check option sanity
382 if args.msan and (args.asan or args.ubsan):
383 raise RuntimeError('MSAN may not be used with any other sanitizers')
384 if args.msan_track_origins and not args.msan:
385 raise RuntimeError('--enable-msan-track-origins requires MSAN')
386 if args.ubsan_pointer_overflow and not args.ubsan:
387 raise RuntimeError('--enable-ubsan-pointer-overflow requires UBSAN')
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
413 '-DDEBUGLEVEL={}'.format(args.debug),
414 '-DMEM_FORCE_MEMORY_ACCESS={}'.format(args.memory_access),
415 '-DFUZZ_RNG_SEED_SIZE={}'.format(args.fuzz_rng_seed_size),
418 # Set flags for options
419 assert not (args.fuzzer and args.coverage)
422 '-fsanitize-coverage=trace-pc-guard,indirect-calls,trace-cmp'
425 common_flags += ['-fsanitize=fuzzer']
426 args.lib_fuzzing_engine = ''
428 mflags += ['LIB_FUZZING_ENGINE={}'.format(args.lib_fuzzing_engine)]
430 if args.sanitize_recover:
431 recover_flags = ['-fsanitize-recover=all']
433 recover_flags = ['-fno-sanitize-recover=all']
435 common_flags += recover_flags
438 msan_flags = ['-fsanitize=memory']
439 if args.msan_track_origins:
440 msan_flags += ['-fsanitize-memory-track-origins']
441 common_flags += msan_flags
442 # Append extra MSAN flags (it might require special setup)
443 cppflags += [args.msan_extra_cppflags]
444 cflags += [args.msan_extra_cflags]
445 cxxflags += [args.msan_extra_cxxflags]
446 ldflags += [args.msan_extra_ldflags]
449 common_flags += ['-fsanitize=address']
452 ubsan_flags = ['-fsanitize=undefined']
453 if not args.ubsan_pointer_overflow:
454 ubsan_flags += overflow_ubsan_flags(cc, cxx)
455 common_flags += ubsan_flags
457 if args.stateful_fuzzing:
458 cppflags += ['-DSTATEFUL_FUZZING']
460 if args.third_party_seq_prod_obj:
461 cppflags += ['-DFUZZ_THIRD_PARTY_SEQ_PROD']
462 mflags += ['THIRD_PARTY_SEQ_PROD_OBJ={}'.format(args.third_party_seq_prod_obj)]
464 if args.fuzzing_mode:
465 cppflags += ['-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION']
467 if args.lib_fuzzing_engine == 'libregression.a':
468 targets = ['libregression.a'] + targets
470 # Append the common flags
471 cflags += common_flags
472 cxxflags += common_flags
474 # Prepare the flags for Make
475 cc_str = "CC={}".format(cc)
476 cxx_str = "CXX={}".format(cxx)
477 cppflags_str = "CPPFLAGS={}".format(' '.join(cppflags))
478 cflags_str = "CFLAGS={}".format(' '.join(cflags))
479 cxxflags_str = "CXXFLAGS={}".format(' '.join(cxxflags))
480 ldflags_str = "LDFLAGS={}".format(' '.join(ldflags))
483 print('MFLAGS={}'.format(' '.join(mflags)))
492 clean_cmd = ['make', 'clean'] + mflags
493 print(' '.join(clean_cmd))
494 subprocess.check_call(clean_cmd)
504 print(' '.join(build_cmd))
505 subprocess.check_call(build_cmd)
509 def libfuzzer_parser(args):
511 Runs a libfuzzer binary.
512 Passes all extra arguments to libfuzzer.
513 The fuzzer should have been build with LIB_FUZZING_ENGINE pointing to
515 Generates output in the CORPORA directory, puts crashes in the ARTIFACT
516 directory, and takes extra input from the SEED directory.
517 To merge AFL's output pass the SEED as AFL's output directory and pass
520 parser = argparse.ArgumentParser(prog=args.pop(0), description=description)
524 help='Override the default corpora dir (default: {})'.format(
525 abs_join(CORPORA_DIR, 'TARGET')))
529 help='Override the default artifact dir (default: {})'.format(
530 abs_join(CORPORA_DIR, 'TARGET-crash')))
534 help='Override the default seed dir (default: {})'.format(
535 abs_join(CORPORA_DIR, 'TARGET-seed')))
539 help='Fuzz target(s) to build {{{}}}'.format(', '.join(TARGETS)))
540 args, extra = parser.parse_known_args(args)
543 if args.TARGET and args.TARGET not in TARGETS:
544 raise RuntimeError('{} is not a valid target'.format(args.TARGET))
549 def libfuzzer(target, corpora=None, artifact=None, seed=None, extra_args=None):
551 corpora = abs_join(CORPORA_DIR, target)
553 artifact = abs_join(CORPORA_DIR, '{}-crash'.format(target))
555 seed = abs_join(CORPORA_DIR, '{}-seed'.format(target))
556 if extra_args is None:
559 target = abs_join(FUZZ_DIR, target)
561 corpora = [create(corpora)]
562 artifact = create(artifact)
565 corpora += [artifact]
569 cmd = [target, '-artifact_prefix={}/'.format(artifact)]
570 cmd += corpora + extra_args
572 subprocess.check_call(cmd)
575 def libfuzzer_cmd(args):
577 args = libfuzzer_parser(args)
578 except Exception as e:
581 libfuzzer(args.TARGET, args.corpora, args.artifact, args.seed, args.extra)
585 def afl_parser(args):
587 Runs an afl-fuzz job.
588 Passes all extra arguments to afl-fuzz.
589 The fuzzer should have been built with CC/CXX set to the AFL compilers,
590 and with LIB_FUZZING_ENGINE='libregression.a'.
591 Takes input from CORPORA and writes output to OUTPUT.
592 Uses AFL_FUZZ as the binary (set from flag or environment variable).
594 parser = argparse.ArgumentParser(prog=args.pop(0), description=description)
598 help='Override the default corpora dir (default: {})'.format(
599 abs_join(CORPORA_DIR, 'TARGET')))
603 help='Override the default AFL output dir (default: {})'.format(
604 abs_join(CORPORA_DIR, 'TARGET-afl')))
609 help='AFL_FUZZ (default: $AFL_FUZZ={})'.format(AFL_FUZZ))
613 help='Fuzz target(s) to build {{{}}}'.format(', '.join(TARGETS)))
614 args, extra = parser.parse_known_args(args)
617 if args.TARGET and args.TARGET not in TARGETS:
618 raise RuntimeError('{} is not a valid target'.format(args.TARGET))
621 args.corpora = abs_join(CORPORA_DIR, args.TARGET)
623 args.output = abs_join(CORPORA_DIR, '{}-afl'.format(args.TARGET))
630 args = afl_parser(args)
631 except Exception as e:
634 target = abs_join(FUZZ_DIR, args.TARGET)
636 corpora = create(args.corpora)
637 output = create(args.output)
639 cmd = [args.afl_fuzz, '-i', corpora, '-o', output] + args.extra
640 cmd += [target, '@@']
646 def regression(args):
649 Runs one or more regression tests.
650 The fuzzer should have been built with
651 LIB_FUZZING_ENGINE='libregression.a'.
652 Takes input from CORPORA.
654 args = targets_parser(args, description)
655 except Exception as e:
658 for target in args.TARGET:
659 corpora = create(abs_join(CORPORA_DIR, target))
660 target = abs_join(FUZZ_DIR, target)
661 cmd = [target, corpora]
663 subprocess.check_call(cmd)
667 def gen_parser(args):
669 Generate a seed corpus appropriate for TARGET with data generated with
671 The fuzz inputs are prepended with a seed before the zstd data, so the
672 output of decodecorpus shouldn't be used directly.
673 Generates NUMBER samples prepended with FUZZ_RNG_SEED_SIZE random bytes and
674 puts the output in SEED.
675 DECODECORPUS is the decodecorpus binary, and must already be built.
677 parser = argparse.ArgumentParser(prog=args.pop(0), description=description)
683 help='Number of samples to generate')
688 help='Maximum sample size to generate')
692 help='Override the default seed dir (default: {})'.format(
693 abs_join(CORPORA_DIR, 'TARGET-seed')))
697 default=DECODECORPUS,
698 help="decodecorpus binary (default: $DECODECORPUS='{}')".format(
704 help="zstd binary (default: $ZSTD='{}')".format(ZSTD))
706 '--fuzz-rng-seed-size',
709 help="FUZZ_RNG_SEED_SIZE used for generate the samples (must match)"
714 help='Fuzz target(s) to build {{{}}}'.format(', '.join(TARGETS)))
715 args, extra = parser.parse_known_args(args)
718 if args.TARGET and args.TARGET not in TARGETS:
719 raise RuntimeError('{} is not a valid target'.format(args.TARGET))
722 args.seed = abs_join(CORPORA_DIR, '{}-seed'.format(args.TARGET))
724 if not os.path.isfile(args.decodecorpus):
725 raise RuntimeError("{} is not a file run 'make -C {} decodecorpus'".
726 format(args.decodecorpus, abs_join(FUZZ_DIR, '..')))
733 args = gen_parser(args)
734 except Exception as e:
738 seed = create(args.seed)
739 with tmpdir() as compressed, tmpdir() as decompressed, tmpdir() as dict:
740 info = TARGET_INFO[args.TARGET]
742 if info.input_type == InputType.DICTIONARY_DATA:
743 number = max(args.number, 1000)
748 '-n{}'.format(args.number),
749 '-p{}/'.format(compressed),
750 '-o{}'.format(decompressed),
753 if info.frame_type == FrameType.BLOCK:
756 '--max-block-size-log={}'.format(min(args.max_size_log, 17))
759 cmd += ['--max-content-size-log={}'.format(args.max_size_log)]
762 subprocess.check_call(cmd)
764 if info.input_type == InputType.RAW_DATA:
765 print('using decompressed data in {}'.format(decompressed))
766 samples = decompressed
767 elif info.input_type == InputType.COMPRESSED_DATA:
768 print('using compressed data in {}'.format(compressed))
771 assert info.input_type == InputType.DICTIONARY_DATA
772 print('making dictionary data from {}'.format(decompressed))
774 min_dict_size_log = 9
775 max_dict_size_log = max(min_dict_size_log + 1, args.max_size_log)
776 for dict_size_log in range(min_dict_size_log, max_dict_size_log):
777 dict_size = 1 << dict_size_log
782 '--maxdict={}'.format(dict_size),
783 '-o', abs_join(dict, '{}.zstd-dict'.format(dict_size))
786 subprocess.check_call(cmd)
788 # Copy the samples over and prepend the RNG seeds
789 for name in os.listdir(samples):
790 samplename = abs_join(samples, name)
791 outname = abs_join(seed, name)
792 with open(samplename, 'rb') as sample:
793 with open(outname, 'wb') as out:
795 chunk = sample.read(CHUNK_SIZE)
796 while len(chunk) > 0:
798 chunk = sample.read(CHUNK_SIZE)
805 Runs a libfuzzer fuzzer with -merge=1 to build a minimal corpus in
806 TARGET_seed_corpus. All extra args are passed to libfuzzer.
808 args = targets_parser(args, description)
809 except Exception as e:
813 for target in args.TARGET:
814 # Merge the corpus + anything else into the seed_corpus
815 corpus = abs_join(CORPORA_DIR, target)
816 seed_corpus = abs_join(CORPORA_DIR, "{}_seed_corpus".format(target))
817 extra_args = [corpus, "-merge=1"] + args.extra
818 libfuzzer(target, corpora=seed_corpus, extra_args=extra_args)
819 seeds = set(os.listdir(seed_corpus))
820 # Copy all crashes directly into the seed_corpus if not already present
821 crashes = abs_join(CORPORA_DIR, '{}-crash'.format(target))
822 for crash in os.listdir(crashes):
823 if crash not in seeds:
824 shutil.copy(abs_join(crashes, crash), seed_corpus)
831 Zips up the seed corpus.
833 args = targets_parser(args, description)
834 except Exception as e:
838 for target in args.TARGET:
839 # Zip the seed_corpus
840 seed_corpus = abs_join(CORPORA_DIR, "{}_seed_corpus".format(target))
841 zip_file = "{}.zip".format(seed_corpus)
842 cmd = ["zip", "-r", "-q", "-j", "-9", zip_file, "."]
844 subprocess.check_call(cmd, cwd=seed_corpus)
848 print("\n".join(TARGETS))
851 def short_help(args):
853 print("Usage: {} [OPTIONS] COMMAND [ARGS]...\n".format(name))
858 print("\tfuzzing helpers (select a command and pass -h for help)\n")
860 print("\t-h, --help\tPrint this message")
863 print("\tbuild\t\tBuild a fuzzer")
864 print("\tlibfuzzer\tRun a libFuzzer fuzzer")
865 print("\tafl\t\tRun an AFL fuzzer")
866 print("\tregression\tRun a regression test")
867 print("\tgen\t\tGenerate a seed corpus for a fuzzer")
868 print("\tminimize\tMinimize the test corpora")
869 print("\tzip\t\tZip the minimized corpora up")
870 print("\tlist\t\tList the available targets")
878 if args[1] == '-h' or args[1] == '--help' or args[1] == '-H':
881 command = args.pop(1)
882 args[0] = "{} {}".format(args[0], command)
883 if command == "build":
885 if command == "libfuzzer":
886 return libfuzzer_cmd(args)
887 if command == "regression":
888 return regression(args)
893 if command == "minimize":
894 return minimize(args)
897 if command == "list":
898 return list_cmd(args)
900 print("Error: No such command {} (pass -h for help)".format(command))
904 if __name__ == "__main__":