--- /dev/null
+#! /usr/bin/env python
+
+# ################################################################
+# Copyright (c) 2016-present, Facebook, Inc.
+# All rights reserved.
+#
+# This source code is licensed under both the BSD-style license (found in the
+# LICENSE file in the root directory of this source tree) and the GPLv2 (found
+# in the COPYING file in the root directory of this source tree).
+# ##########################################################################
+
+import argparse
+import contextlib
+import os
+import re
+import shutil
+import subprocess
+import sys
+import tempfile
+
+
+def abs_join(a, *p):
+ return os.path.abspath(os.path.join(a, *p))
+
+
+# Constants
+FUZZ_DIR = os.path.abspath(os.path.dirname(__file__))
+CORPORA_DIR = abs_join(FUZZ_DIR, 'corpora')
+TARGETS = [
+ 'simple_round_trip',
+ 'stream_round_trip',
+ 'block_round_trip',
+ 'simple_decompress',
+ 'stream_decompress',
+ 'block_decompress',
+]
+ALL_TARGETS = TARGETS + ['all']
+FUZZ_RNG_SEED_SIZE = 4
+
+# Standard environment variables
+CC = os.environ.get('CC', 'cc')
+CXX = os.environ.get('CXX', 'c++')
+CPPFLAGS = os.environ.get('CPPFLAGS', '')
+CFLAGS = os.environ.get('CFLAGS', '-O3')
+CXXFLAGS = os.environ.get('CXXFLAGS', CFLAGS)
+LDFLAGS = os.environ.get('LDFLAGS', '')
+MFLAGS = os.environ.get('MFLAGS', '-j')
+
+# Fuzzing environment variables
+LIB_FUZZING_ENGINE = os.environ.get('LIB_FUZZING_ENGINE', 'libregression.a')
+AFL_FUZZ = os.environ.get('AFL_FUZZ', 'afl-fuzz')
+DECODECORPUS = os.environ.get('DECODECORPUS',
+ abs_join(FUZZ_DIR, '..', 'decodecorpus'))
+
+# Sanitizer environment variables
+MSAN_EXTRA_CPPFLAGS = os.environ.get('MSAN_EXTRA_CPPFLAGS', '')
+MSAN_EXTRA_CFLAGS = os.environ.get('MSAN_EXTRA_CFLAGS', '')
+MSAN_EXTRA_CXXFLAGS = os.environ.get('MSAN_EXTRA_CXXFLAGS', '')
+MSAN_EXTRA_LDFLAGS = os.environ.get('MSAN_EXTRA_LDFLAGS', '')
+
+
+def create(r):
+ d = os.path.abspath(r)
+ if not os.path.isdir(d):
+ os.mkdir(d)
+ return d
+
+
+def check(r):
+ d = os.path.abspath(r)
+ if not os.path.isdir(d):
+ return None
+ return d
+
+
+@contextlib.contextmanager
+def tmpdir():
+ dirpath = tempfile.mkdtemp()
+ try:
+ yield dirpath
+ finally:
+ shutil.rmtree(dirpath, ignore_errors=True)
+
+
+def parse_env_flags(args, flags):
+ """
+ Look for flags set by environment variables.
+ """
+ flags = ' '.join(flags)
+ san_flags = ','.join(re.findall('-fsanitize=((?:[a-z]+,?)+)', flags))
+ nosan_flags = ','.join(re.findall('-fno-sanitize=((?:[a-z]+,?)+)', flags))
+
+ def set_sanitizer(sanitizer, default, san, nosan):
+ if sanitizer in san and sanitizer in nosan:
+ raise RuntimeError('-fno-sanitize={s} and -fsanitize={s} passed'.
+ format(s=sanitizer))
+ if sanitizer in san:
+ return True
+ if sanitizer in nosan:
+ return False
+ return default
+
+ san = set(san_flags.split(','))
+ nosan = set(nosan_flags.split(','))
+
+ args.asan = set_sanitizer('address', args.asan, san, nosan)
+ args.msan = set_sanitizer('memory', args.msan, san, nosan)
+ args.ubsan = set_sanitizer('undefined', args.ubsan, san, nosan)
+
+ args.sanitize = args.asan or args.msan or args.ubsan
+
+ return args
+
+
+def build_parser(args):
+ description = """
+ Cleans the repository and builds a fuzz target (or all).
+ Many flags default to environment variables (default says $X='y').
+ Options that aren't enabling features default to the correct values for
+ zstd.
+ Enable sanitizers with --enable-*san.
+ For regression testing just build.
+ For libFuzzer set LIB_FUZZING_ENGINE and pass --enable-coverage.
+ For AFL set CC and CXX to AFL's compilers and set
+ LIB_FUZZING_ENGINE='libregression.a'.
+ """
+ parser = argparse.ArgumentParser(prog=args.pop(0), description=description)
+ parser.add_argument(
+ '--lib-fuzzing-engine',
+ dest='lib_fuzzing_engine',
+ type=str,
+ default=LIB_FUZZING_ENGINE,
+ help=('The fuzzing engine to use e.g. /path/to/libFuzzer.a '
+ "(default: $LIB_FUZZING_ENGINE='{})".format(LIB_FUZZING_ENGINE)))
+ parser.add_argument(
+ '--enable-coverage',
+ dest='coverage',
+ action='store_true',
+ help='Enable coverage instrumentation (-fsanitize-coverage)')
+ parser.add_argument(
+ '--enable-asan', dest='asan', action='store_true', help='Enable UBSAN')
+ parser.add_argument(
+ '--enable-ubsan',
+ dest='ubsan',
+ action='store_true',
+ help='Enable UBSAN')
+ parser.add_argument(
+ '--enable-ubsan-pointer-overflow',
+ dest='ubsan_pointer_overflow',
+ action='store_true',
+ help='Enable UBSAN pointer overflow check (known failure)')
+ parser.add_argument(
+ '--enable-msan', dest='msan', action='store_true', help='Enable MSAN')
+ parser.add_argument(
+ '--enable-msan-track-origins', dest='msan_track_origins',
+ action='store_true', help='Enable MSAN origin tracking')
+ parser.add_argument(
+ '--msan-extra-cppflags',
+ dest='msan_extra_cppflags',
+ type=str,
+ default=MSAN_EXTRA_CPPFLAGS,
+ help="Extra CPPFLAGS for MSAN (default: $MSAN_EXTRA_CPPFLAGS='{}')".
+ format(MSAN_EXTRA_CPPFLAGS))
+ parser.add_argument(
+ '--msan-extra-cflags',
+ dest='msan_extra_cflags',
+ type=str,
+ default=MSAN_EXTRA_CFLAGS,
+ help="Extra CFLAGS for MSAN (default: $MSAN_EXTRA_CFLAGS='{}')".format(
+ MSAN_EXTRA_CFLAGS))
+ parser.add_argument(
+ '--msan-extra-cxxflags',
+ dest='msan_extra_cxxflags',
+ type=str,
+ default=MSAN_EXTRA_CXXFLAGS,
+ help="Extra CXXFLAGS for MSAN (default: $MSAN_EXTRA_CXXFLAGS='{}')".
+ format(MSAN_EXTRA_CXXFLAGS))
+ parser.add_argument(
+ '--msan-extra-ldflags',
+ dest='msan_extra_ldflags',
+ type=str,
+ default=MSAN_EXTRA_LDFLAGS,
+ help="Extra LDFLAGS for MSAN (default: $MSAN_EXTRA_LDFLAGS='{}')".
+ format(MSAN_EXTRA_LDFLAGS))
+ parser.add_argument(
+ '--enable-sanitize-recover',
+ dest='sanitize_recover',
+ action='store_true',
+ help='Non-fatal sanitizer errors where possible')
+ parser.add_argument(
+ '--debug',
+ dest='debug',
+ type=int,
+ default=1,
+ help='Set ZSTD_DEBUG (default: 1)')
+ parser.add_argument(
+ '--force-memory-access',
+ dest='memory_access',
+ type=int,
+ default=0,
+ help='Set MEM_FORCE_MEMORY_ACCESS (default: 0)')
+ parser.add_argument(
+ '--fuzz-rng-seed-size',
+ dest='fuzz_rng_seed_size',
+ type=int,
+ default=4,
+ help='Set FUZZ_RNG_SEED_SIZE (default: 4)')
+ parser.add_argument(
+ '--disable-fuzzing-mode',
+ dest='fuzzing_mode',
+ action='store_false',
+ help='Do not define FUZZING_BUILD_MORE_UNSAFE_FOR_PRODUCTION')
+ parser.add_argument(
+ '--enable-stateful-fuzzing',
+ dest='stateful_fuzzing',
+ action='store_true',
+ help='Reuse contexts between runs (makes reproduction impossible)')
+ parser.add_argument(
+ '--cc',
+ dest='cc',
+ type=str,
+ default=CC,
+ help="CC (default: $CC='{}')".format(CC))
+ parser.add_argument(
+ '--cxx',
+ dest='cxx',
+ type=str,
+ default=CXX,
+ help="CXX (default: $CXX='{}')".format(CXX))
+ parser.add_argument(
+ '--cppflags',
+ dest='cppflags',
+ type=str,
+ default=CPPFLAGS,
+ help="CPPFLAGS (default: $CPPFLAGS='{}')".format(CPPFLAGS))
+ parser.add_argument(
+ '--cflags',
+ dest='cflags',
+ type=str,
+ default=CFLAGS,
+ help="CFLAGS (default: $CFLAGS='{}')".format(CFLAGS))
+ parser.add_argument(
+ '--cxxflags',
+ dest='cxxflags',
+ type=str,
+ default=CXXFLAGS,
+ help="CXXFLAGS (default: $CXXFLAGS='{}')".format(CXXFLAGS))
+ parser.add_argument(
+ '--ldflags',
+ dest='ldflags',
+ type=str,
+ default=LDFLAGS,
+ help="LDFLAGS (default: $LDFLAGS='{}')".format(LDFLAGS))
+ parser.add_argument(
+ '--mflags',
+ dest='mflags',
+ type=str,
+ default=MFLAGS,
+ help="Extra Make flags (default: $MFLAGS='{}')".format(MFLAGS))
+ parser.add_argument(
+ 'TARGET',
+ nargs='*',
+ type=str,
+ help='Fuzz target(s) to build {{{}}}'.format(', '.join(ALL_TARGETS))
+ )
+ args = parser.parse_args(args)
+ args = parse_env_flags(args, ' '.join(
+ [args.cppflags, args.cflags, args.cxxflags, args.ldflags]))
+
+ # Check option sanitiy
+ if args.msan and (args.asan or args.ubsan):
+ raise RuntimeError('MSAN may not be used with any other sanitizers')
+ if args.msan_track_origins and not args.msan:
+ raise RuntimeError('--enable-msan-track-origins requires MSAN')
+ if args.ubsan_pointer_overflow and not args.ubsan:
+ raise RuntimeError('--enable-ubsan-pointer-overlow requires UBSAN')
+ if args.sanitize_recover and not args.sanitize:
+ raise RuntimeError('--enable-sanitize-recover but no sanitizers used')
+
+ return args
+
+
+def build(args):
+ try:
+ args = build_parser(args)
+ except Exception as e:
+ print(e)
+ return 1
+ # The compilation flags we are setting
+ targets = args.TARGET
+ cc = args.cc
+ cxx = args.cxx
+ cppflags = [args.cppflags]
+ cflags = [args.cflags]
+ ldflags = [args.ldflags]
+ cxxflags = [args.cxxflags]
+ mflags = [args.mflags] if args.mflags else []
+ # Flags to be added to both cflags and cxxflags
+ common_flags = []
+
+ cppflags += [
+ '-DZSTD_DEBUG={}'.format(args.debug),
+ '-DMEM_FORCE_MEMORY_ACCESS={}'.format(args.memory_access),
+ '-DFUZZ_RNG_SEED_SIZE={}'.format(args.fuzz_rng_seed_size),
+ ]
+
+ mflags += ['LIB_FUZZING_ENGINE={}'.format(args.lib_fuzzing_engine)]
+
+ # Set flags for options
+ if args.coverage:
+ common_flags += [
+ '-fsanitize-coverage=trace-pc-guard,indirect-calls,trace-cmp'
+ ]
+
+ if args.sanitize_recover:
+ recover_flags = ['-fsanitize-recover=all']
+ else:
+ recover_flags = ['-fno-sanitize-recover=all']
+ if args.sanitize:
+ common_flags += recover_flags
+
+ if args.msan:
+ msan_flags = ['-fsanitize=memory']
+ if args.msan_track_origins:
+ msan_flags += ['-fsanitize-memory-track-origins']
+ common_flags += msan_flags
+ # Append extra MSAN flags (it might require special setup)
+ cppflags += [args.msan_extra_cppflags]
+ cflags += [args.msan_extra_cflags]
+ cxxflags += [args.msan_extra_cxxflags]
+ ldflags += [args.msan_extra_ldflags]
+
+ if args.asan:
+ common_flags += ['-fsanitize=address']
+
+ if args.ubsan:
+ ubsan_flags = ['-fsanitize=undefined']
+ if not args.ubsan_pointer_overflow:
+ ubsan_flags += ['-fno-sanitize=pointer-overflow']
+ common_flags += ubsan_flags
+
+ if args.stateful_fuzzing:
+ cppflags += ['-DSTATEFUL_FUZZING']
+
+ if args.fuzzing_mode:
+ cppflags += ['-DFUZZING_BUILD_MORE_UNSAFE_FOR_PRODUCTION']
+
+ if args.lib_fuzzing_engine == 'libregression.a':
+ targets = ['libregression.a'] + targets
+
+ # Append the common flags
+ cflags += common_flags
+ cxxflags += common_flags
+
+ # Prepare the flags for Make
+ cc_str = "CC={}".format(cc)
+ cxx_str = "CXX={}".format(cxx)
+ cppflags_str = "CPPFLAGS={}".format(' '.join(cppflags))
+ cflags_str = "CFLAGS={}".format(' '.join(cflags))
+ cxxflags_str = "CXXFLAGS={}".format(' '.join(cxxflags))
+ ldflags_str = "LDFLAGS={}".format(' '.join(ldflags))
+
+ # Print the flags
+ print('MFLAGS={}'.format(' '.join(mflags)))
+ print(cc_str)
+ print(cxx_str)
+ print(cppflags_str)
+ print(cflags_str)
+ print(cxxflags_str)
+ print(ldflags_str)
+
+ # Clean and build
+ clean_cmd = ['make', 'clean'] + mflags
+ print(' '.join(clean_cmd))
+ subprocess.check_call(clean_cmd)
+ build_cmd = [
+ 'make',
+ cc_str,
+ cxx_str,
+ cppflags_str,
+ cflags_str,
+ cxxflags_str,
+ ldflags_str,
+ ] + mflags + targets
+ print(' '.join(build_cmd))
+ subprocess.check_call(build_cmd)
+ return 0
+
+
+def libfuzzer_parser(args):
+ description = """
+ Runs a libfuzzer binary.
+ Passes all extra arguments to libfuzzer.
+ The fuzzer should have been build with LIB_FUZZING_ENGINE pointing to
+ libFuzzer.a.
+ Generates output in the CORPORA directory, puts crashes in the ARTIFACT
+ directory, and takes extra input from the SEED directory.
+ To merge AFL's output pass the SEED as AFL's output directory and pass
+ '-merge=1'.
+ """
+ parser = argparse.ArgumentParser(prog=args.pop(0), description=description)
+ parser.add_argument(
+ '--corpora',
+ type=str,
+ help='Override the default corpora dir (default: {})'.format(
+ abs_join(CORPORA_DIR, 'TARGET')))
+ parser.add_argument(
+ '--artifact',
+ type=str,
+ help='Override the default artifact dir (default: {})'.format(
+ abs_join(CORPORA_DIR, 'TARGET-crash')))
+ parser.add_argument(
+ '--seed',
+ type=str,
+ help='Override the default seed dir (default: {})'.format(
+ abs_join(CORPORA_DIR, 'TARGET-seed')))
+ parser.add_argument(
+ 'TARGET',
+ type=str,
+ help='Fuzz target(s) to build {{{}}}'.format(', '.join(TARGETS)))
+ args, extra = parser.parse_known_args(args)
+ args.extra = extra
+
+ if args.TARGET and args.TARGET not in TARGETS:
+ raise RuntimeError('{} is not a valid target'.format(args.TARGET))
+
+ if not args.corpora:
+ args.corpora = abs_join(CORPORA_DIR, args.TARGET)
+ if not args.artifact:
+ args.artifact = abs_join(CORPORA_DIR, '{}-crash'.format(args.TARGET))
+ if not args.seed:
+ args.seed = abs_join(CORPORA_DIR, '{}-seed'.format(args.TARGET))
+
+ return args
+
+
+def libfuzzer(args):
+ try:
+ args = libfuzzer_parser(args)
+ except Exception as e:
+ print(e)
+ return 1
+ target = abs_join(FUZZ_DIR, args.TARGET)
+
+ corpora = [create(args.corpora)]
+ artifact = create(args.artifact)
+ seed = check(args.seed)
+
+ corpora += [artifact]
+ if seed is not None:
+ corpora += [seed]
+
+ cmd = [target, '-artifact_prefix={}/'.format(artifact)]
+ cmd += corpora + args.extra
+ print(' '.join(cmd))
+ subprocess.call(cmd)
+ return 0
+
+
+def afl_parser(args):
+ description = """
+ Runs an afl-fuzz job.
+ Passes all extra arguments to afl-fuzz.
+ The fuzzer should have been built with CC/CXX set to the AFL compilers,
+ and with LIB_FUZZING_ENGINE='libregression.a'.
+ Takes input from CORPORA and writes output to OUTPUT.
+ Uses AFL_FUZZ as the binary (set from flag or environment variable).
+ """
+ parser = argparse.ArgumentParser(prog=args.pop(0), description=description)
+ parser.add_argument(
+ '--corpora',
+ type=str,
+ help='Override the default corpora dir (default: {})'.format(
+ abs_join(CORPORA_DIR, 'TARGET')))
+ parser.add_argument(
+ '--output',
+ type=str,
+ help='Override the default AFL output dir (default: {})'.format(
+ abs_join(CORPORA_DIR, 'TARGET-afl')))
+ parser.add_argument(
+ '--afl-fuzz',
+ type=str,
+ default=AFL_FUZZ,
+ help='AFL_FUZZ (default: $AFL_FUZZ={})'.format(AFL_FUZZ))
+ parser.add_argument(
+ 'TARGET',
+ type=str,
+ help='Fuzz target(s) to build {{{}}}'.format(', '.join(TARGETS)))
+ args, extra = parser.parse_known_args(args)
+ args.extra = extra
+
+ if args.TARGET and args.TARGET not in TARGETS:
+ raise RuntimeError('{} is not a valid target'.format(args.TARGET))
+
+ if not args.corpora:
+ args.corpora = abs_join(CORPORA_DIR, args.TARGET)
+ if not args.output:
+ args.output = abs_join(CORPORA_DIR, '{}-afl'.format(args.TARGET))
+
+ return args
+
+
+def afl(args):
+ try:
+ args = afl_parser(args)
+ except Exception as e:
+ print(e)
+ return 1
+ target = abs_join(FUZZ_DIR, args.TARGET)
+
+ corpora = create(args.corpora)
+ output = create(args.output)
+
+ cmd = [args.afl_fuzz, '-i', corpora, '-o', output] + args.extra
+ cmd += [target, '@@']
+ print(' '.join(cmd))
+ subprocess.call(cmd)
+ return 0
+
+
+def regression_parser(args):
+ description = """
+ Runs one or more regression tests.
+ The fuzzer should have been built with with
+ LIB_FUZZING_ENGINE='libregression.a'.
+ Takes input from CORPORA.
+ """
+ parser = argparse.ArgumentParser(prog=args.pop(0), description=description)
+ parser.add_argument(
+ 'TARGET',
+ nargs='*',
+ type=str,
+ help='Fuzz target(s) to build {{{}}}'.format(', '.join(ALL_TARGETS)))
+ args = parser.parse_args(args)
+
+ targets = set()
+ for target in args.TARGET:
+ if not target:
+ continue
+ if target == 'all':
+ targets = targets.union(TARGETS)
+ elif target in TARGETS:
+ targets = targets.add(target)
+ else:
+ raise RuntimeError('{} is not a valid target'.format(target))
+ args.TARGET = list(targets)
+
+ return args
+
+
+def regression(args):
+ try:
+ args = regression_parser(args)
+ except Exception as e:
+ print(e)
+ return 1
+ for target in args.TARGET:
+ corpora = create(abs_join(CORPORA_DIR, target))
+ target = abs_join(FUZZ_DIR, target)
+ cmd = [target, corpora]
+ print(' '.join(cmd))
+ subprocess.check_call(cmd)
+ return 0
+
+
+def gen_parser(args):
+ description = """
+ Generate a seed corpus appropiate for TARGET with data generated with
+ decodecorpus.
+ The fuzz inputs are prepended with a seed before the zstd data, so the
+ output of decodecorpus shouldn't be used directly.
+ Generates NUMBER samples prepended with FUZZ_RNG_SEED_SIZE random bytes and
+ puts the output in SEED.
+ DECODECORPUS is the decodecorpus binary, and must already be built.
+ """
+ parser = argparse.ArgumentParser(prog=args.pop(0), description=description)
+ parser.add_argument(
+ '--number',
+ '-n',
+ type=int,
+ default=100,
+ help='Number of samples to generate')
+ parser.add_argument(
+ '--max-size-log',
+ type=int,
+ default=13,
+ help='Maximum sample size to generate')
+ parser.add_argument(
+ '--seed',
+ type=str,
+ help='Override the default seed dir (default: {})'.format(
+ abs_join(CORPORA_DIR, 'TARGET-seed')))
+ parser.add_argument(
+ '--decodecorpus',
+ type=str,
+ default=DECODECORPUS,
+ help="decodecorpus binary (default: $DECODECORPUS='{}')".format(
+ DECODECORPUS))
+ parser.add_argument(
+ '--fuzz-rng-seed-size',
+ type=int,
+ default=4,
+ help="FUZZ_RNG_SEED_SIZE used for generate the samples (must match)"
+ )
+ parser.add_argument(
+ 'TARGET',
+ type=str,
+ help='Fuzz target(s) to build {{{}}}'.format(', '.join(TARGETS)))
+ args, extra = parser.parse_known_args(args)
+ args.extra = extra
+
+ if args.TARGET and args.TARGET not in TARGETS:
+ raise RuntimeError('{} is not a valid target'.format(args.TARGET))
+
+ if not args.seed:
+ args.seed = abs_join(CORPORA_DIR, '{}-seed'.format(args.TARGET))
+
+ if not os.path.isfile(args.decodecorpus):
+ raise RuntimeError("{} is not a file run 'make -C {} decodecorpus'".
+ format(args.decodecorpus, abs_join(FUZZ_DIR, '..')))
+
+ return args
+
+
+def gen(args):
+ try:
+ args = gen_parser(args)
+ except Exception as e:
+ print(e)
+ return 1
+
+ seed = create(args.seed)
+ with tmpdir() as compressed:
+ with tmpdir() as decompressed:
+ cmd = [
+ args.decodecorpus,
+ '-n{}'.format(args.number),
+ '--max-content-size-log={}'.format(args.max_size_log),
+ '-p{}/'.format(compressed),
+ '-o{}'.format(decompressed),
+ ]
+
+ if 'block_' in args.TARGET:
+ cmd += ['--gen-blocks']
+
+ print(' '.join(cmd))
+ subprocess.check_call(cmd)
+
+ if '_round_trip' in args.TARGET:
+ print('using decompressed data in {}'.format(decompressed))
+ samples = decompressed
+ elif '_decompress' in args.TARGET:
+ print('using compressed data in {}'.format(compressed))
+ samples = compressed
+
+ # Copy the samples over and prepend the RNG seeds
+ for name in os.listdir(samples):
+ samplename = abs_join(samples, name)
+ outname = abs_join(seed, name)
+ rng_seed = os.urandom(args.fuzz_rng_seed_size)
+ with open(samplename, 'rb') as sample:
+ with open(outname, 'wb') as out:
+ out.write(rng_seed)
+ CHUNK_SIZE = 131072
+ chunk = sample.read(CHUNK_SIZE)
+ while len(chunk) > 0:
+ out.write(chunk)
+ chunk = sample.read(CHUNK_SIZE)
+ return 0
+
+
+def short_help(args):
+ name = args[0]
+ print("Usage: {} [OPTIONS] COMMAND [ARGS]...\n".format(name))
+
+
+def help(args):
+ short_help(args)
+ print("\tfuzzing helpers (select a command and pass -h for help)\n")
+ print("Options:")
+ print("\t-h, --help\tPrint this message")
+ print("")
+ print("Commands:")
+ print("\tbuild\t\tBuild a fuzzer")
+ print("\tlibfuzzer\tRun a libFuzzer fuzzer")
+ print("\tafl\t\tRun an AFL fuzzer")
+ print("\tregression\tRun a regression test")
+ print("\tgen\t\tGenerate a seed corpus for a fuzzer")
+
+
+def main():
+ args = sys.argv
+ if len(args) < 2:
+ help(args)
+ return 1
+ if args[1] == '-h' or args[1] == '--help' or args[1] == '-H':
+ help(args)
+ return 1
+ command = args.pop(1)
+ args[0] = "{} {}".format(args[0], command)
+ if command == "build":
+ return build(args)
+ if command == "libfuzzer":
+ return libfuzzer(args)
+ if command == "regression":
+ return regression(args)
+ if command == "afl":
+ return afl(args)
+ if command == "gen":
+ return gen(args)
+ short_help(args)
+ print("Error: No such command {} (pass -h for help)".format(command))
+ return 1
+
+
+if __name__ == "__main__":
+ sys.exit(main())