Home | History | Annotate | Line # | Download | only in fuzz
      1  1.1  christos #!/usr/bin/env python
      2  1.1  christos 
      3  1.1  christos # ################################################################
      4  1.1  christos # Copyright (c) Meta Platforms, Inc. and affiliates.
      5  1.1  christos # All rights reserved.
      6  1.1  christos #
      7  1.1  christos # This source code is licensed under both the BSD-style license (found in the
      8  1.1  christos # LICENSE file in the root directory of this source tree) and the GPLv2 (found
      9  1.1  christos # in the COPYING file in the root directory of this source tree).
     10  1.1  christos # You may select, at your option, one of the above-listed licenses.
     11  1.1  christos # ##########################################################################
     12  1.1  christos 
     13  1.1  christos import argparse
     14  1.1  christos import contextlib
     15  1.1  christos import os
     16  1.1  christos import re
     17  1.1  christos import shlex
     18  1.1  christos import shutil
     19  1.1  christos import subprocess
     20  1.1  christos import sys
     21  1.1  christos import tempfile
     22  1.1  christos 
     23  1.1  christos 
     24  1.1  christos def abs_join(a, *p):
     25  1.1  christos     return os.path.abspath(os.path.join(a, *p))
     26  1.1  christos 
     27  1.1  christos 
     28  1.1  christos class InputType(object):
     29  1.1  christos     RAW_DATA = 1
     30  1.1  christos     COMPRESSED_DATA = 2
     31  1.1  christos     DICTIONARY_DATA = 3
     32  1.1  christos 
     33  1.1  christos 
     34  1.1  christos class FrameType(object):
     35  1.1  christos     ZSTD = 1
     36  1.1  christos     BLOCK = 2
     37  1.1  christos 
     38  1.1  christos 
     39  1.1  christos class TargetInfo(object):
     40  1.1  christos     def __init__(self, input_type, frame_type=FrameType.ZSTD):
     41  1.1  christos         self.input_type = input_type
     42  1.1  christos         self.frame_type = frame_type
     43  1.1  christos 
     44  1.1  christos 
     45  1.1  christos # Constants
     46  1.1  christos FUZZ_DIR = os.path.abspath(os.path.dirname(__file__))
     47  1.1  christos CORPORA_DIR = abs_join(FUZZ_DIR, 'corpora')
     48  1.1  christos TARGET_INFO = {
     49  1.1  christos     'simple_round_trip': TargetInfo(InputType.RAW_DATA),
     50  1.1  christos     'stream_round_trip': TargetInfo(InputType.RAW_DATA),
     51  1.1  christos     'block_round_trip': TargetInfo(InputType.RAW_DATA, FrameType.BLOCK),
     52  1.1  christos     'simple_decompress': TargetInfo(InputType.COMPRESSED_DATA),
     53  1.1  christos     'stream_decompress': TargetInfo(InputType.COMPRESSED_DATA),
     54  1.1  christos     'block_decompress': TargetInfo(InputType.COMPRESSED_DATA, FrameType.BLOCK),
     55  1.1  christos     'dictionary_round_trip': TargetInfo(InputType.RAW_DATA),
     56  1.1  christos     'dictionary_decompress': TargetInfo(InputType.COMPRESSED_DATA),
     57  1.1  christos     'zstd_frame_info': TargetInfo(InputType.COMPRESSED_DATA),
     58  1.1  christos     'simple_compress': TargetInfo(InputType.RAW_DATA),
     59  1.1  christos     'dictionary_loader': TargetInfo(InputType.DICTIONARY_DATA),
     60  1.1  christos     'raw_dictionary_round_trip': TargetInfo(InputType.RAW_DATA),
     61  1.1  christos     'dictionary_stream_round_trip': TargetInfo(InputType.RAW_DATA),
     62  1.1  christos     'decompress_dstSize_tooSmall': TargetInfo(InputType.RAW_DATA),
     63  1.1  christos     'fse_read_ncount': TargetInfo(InputType.RAW_DATA),
     64  1.1  christos     'sequence_compression_api': TargetInfo(InputType.RAW_DATA),
     65  1.1  christos     'seekable_roundtrip': TargetInfo(InputType.RAW_DATA),
     66  1.1  christos     'huf_round_trip': TargetInfo(InputType.RAW_DATA),
     67  1.1  christos     'huf_decompress': TargetInfo(InputType.RAW_DATA),
     68  1.1  christos     'decompress_cross_format': TargetInfo(InputType.RAW_DATA),
     69  1.1  christos     'generate_sequences': TargetInfo(InputType.RAW_DATA),
     70  1.1  christos }
     71  1.1  christos TARGETS = list(TARGET_INFO.keys())
     72  1.1  christos ALL_TARGETS = TARGETS + ['all']
     73  1.1  christos FUZZ_RNG_SEED_SIZE = 4
     74  1.1  christos 
     75  1.1  christos # Standard environment variables
     76  1.1  christos CC = os.environ.get('CC', 'cc')
     77  1.1  christos CXX = os.environ.get('CXX', 'c++')
     78  1.1  christos CPPFLAGS = os.environ.get('CPPFLAGS', '')
     79  1.1  christos CFLAGS = os.environ.get('CFLAGS', '-O3')
     80  1.1  christos CXXFLAGS = os.environ.get('CXXFLAGS', CFLAGS)
     81  1.1  christos LDFLAGS = os.environ.get('LDFLAGS', '')
     82  1.1  christos MFLAGS = os.environ.get('MFLAGS', '-j')
     83  1.1  christos THIRD_PARTY_SEQ_PROD_OBJ = os.environ.get('THIRD_PARTY_SEQ_PROD_OBJ', '')
     84  1.1  christos 
     85  1.1  christos # Fuzzing environment variables
     86  1.1  christos LIB_FUZZING_ENGINE = os.environ.get('LIB_FUZZING_ENGINE', 'libregression.a')
     87  1.1  christos AFL_FUZZ = os.environ.get('AFL_FUZZ', 'afl-fuzz')
     88  1.1  christos DECODECORPUS = os.environ.get('DECODECORPUS',
     89  1.1  christos                               abs_join(FUZZ_DIR, '..', 'decodecorpus'))
     90  1.1  christos ZSTD = os.environ.get('ZSTD', abs_join(FUZZ_DIR, '..', '..', 'zstd'))
     91  1.1  christos 
     92  1.1  christos # Sanitizer environment variables
     93  1.1  christos MSAN_EXTRA_CPPFLAGS = os.environ.get('MSAN_EXTRA_CPPFLAGS', '')
     94  1.1  christos MSAN_EXTRA_CFLAGS = os.environ.get('MSAN_EXTRA_CFLAGS', '')
     95  1.1  christos MSAN_EXTRA_CXXFLAGS = os.environ.get('MSAN_EXTRA_CXXFLAGS', '')
     96  1.1  christos MSAN_EXTRA_LDFLAGS = os.environ.get('MSAN_EXTRA_LDFLAGS', '')
     97  1.1  christos 
     98  1.1  christos 
     99  1.1  christos def create(r):
    100  1.1  christos     d = os.path.abspath(r)
    101  1.1  christos     if not os.path.isdir(d):
    102  1.1  christos         os.makedirs(d)
    103  1.1  christos     return d
    104  1.1  christos 
    105  1.1  christos 
    106  1.1  christos def check(r):
    107  1.1  christos     d = os.path.abspath(r)
    108  1.1  christos     if not os.path.isdir(d):
    109  1.1  christos         return None
    110  1.1  christos     return d
    111  1.1  christos 
    112  1.1  christos 
    113  1.1  christos @contextlib.contextmanager
    114  1.1  christos def tmpdir():
    115  1.1  christos     dirpath = tempfile.mkdtemp()
    116  1.1  christos     try:
    117  1.1  christos         yield dirpath
    118  1.1  christos     finally:
    119  1.1  christos         shutil.rmtree(dirpath, ignore_errors=True)
    120  1.1  christos 
    121  1.1  christos 
    122  1.1  christos def parse_targets(in_targets):
    123  1.1  christos     targets = set()
    124  1.1  christos     for target in in_targets:
    125  1.1  christos         if not target:
    126  1.1  christos             continue
    127  1.1  christos         if target == 'all':
    128  1.1  christos             targets = targets.union(TARGETS)
    129  1.1  christos         elif target in TARGETS:
    130  1.1  christos             targets.add(target)
    131  1.1  christos         else:
    132  1.1  christos             raise RuntimeError('{} is not a valid target'.format(target))
    133  1.1  christos     return list(targets)
    134  1.1  christos 
    135  1.1  christos 
    136  1.1  christos def targets_parser(args, description):
    137  1.1  christos     parser = argparse.ArgumentParser(prog=args.pop(0), description=description)
    138  1.1  christos     parser.add_argument(
    139  1.1  christos         'TARGET',
    140  1.1  christos         nargs='*',
    141  1.1  christos         type=str,
    142  1.1  christos         help='Fuzz target(s) to build {{{}}}'.format(', '.join(ALL_TARGETS)))
    143  1.1  christos     args, extra = parser.parse_known_args(args)
    144  1.1  christos     args.extra = extra
    145  1.1  christos 
    146  1.1  christos     args.TARGET = parse_targets(args.TARGET)
    147  1.1  christos 
    148  1.1  christos     return args
    149  1.1  christos 
    150  1.1  christos 
    151  1.1  christos def parse_env_flags(args, flags):
    152  1.1  christos     """
    153  1.1  christos     Look for flags set by environment variables.
    154  1.1  christos     """
    155  1.1  christos     san_flags = ','.join(re.findall('-fsanitize=((?:[a-z]+,?)+)', flags))
    156  1.1  christos     nosan_flags = ','.join(re.findall('-fno-sanitize=((?:[a-z]+,?)+)', flags))
    157  1.1  christos 
    158  1.1  christos     def set_sanitizer(sanitizer, default, san, nosan):
    159  1.1  christos         if sanitizer in san and sanitizer in nosan:
    160  1.1  christos             raise RuntimeError('-fno-sanitize={s} and -fsanitize={s} passed'.
    161  1.1  christos                                format(s=sanitizer))
    162  1.1  christos         if sanitizer in san:
    163  1.1  christos             return True
    164  1.1  christos         if sanitizer in nosan:
    165  1.1  christos             return False
    166  1.1  christos         return default
    167  1.1  christos 
    168  1.1  christos     san = set(san_flags.split(','))
    169  1.1  christos     nosan = set(nosan_flags.split(','))
    170  1.1  christos 
    171  1.1  christos     args.asan = set_sanitizer('address', args.asan, san, nosan)
    172  1.1  christos     args.msan = set_sanitizer('memory', args.msan, san, nosan)
    173  1.1  christos     args.ubsan = set_sanitizer('undefined', args.ubsan, san, nosan)
    174  1.1  christos 
    175  1.1  christos     args.sanitize = args.asan or args.msan or args.ubsan
    176  1.1  christos 
    177  1.1  christos     return args
    178  1.1  christos 
    179  1.1  christos 
    180  1.1  christos def compiler_version(cc, cxx):
    181  1.1  christos     """
    182  1.1  christos     Determines the compiler and version.
    183  1.1  christos     Only works for clang and gcc.
    184  1.1  christos     """
    185  1.1  christos     cc_version_bytes = subprocess.check_output([cc, "--version"])
    186  1.1  christos     cxx_version_bytes = subprocess.check_output([cxx, "--version"])
    187  1.1  christos     compiler = None
    188  1.1  christos     version = None
    189  1.1  christos     print("{} --version:\n{}".format(cc, cc_version_bytes.decode('ascii')))
    190  1.1  christos     if b'clang' in cc_version_bytes:
    191  1.1  christos         assert(b'clang' in cxx_version_bytes)
    192  1.1  christos         compiler = 'clang'
    193  1.1  christos     elif b'gcc' in cc_version_bytes or b'GCC' in cc_version_bytes:
    194  1.1  christos         assert(b'gcc' in cxx_version_bytes or b'g++' in cxx_version_bytes)
    195  1.1  christos         compiler = 'gcc'
    196  1.1  christos     if compiler is not None:
    197  1.1  christos         version_regex = b'([0-9]+)\.([0-9]+)\.([0-9]+)'
    198  1.1  christos         version_match = re.search(version_regex, cc_version_bytes)
    199  1.1  christos         version = tuple(int(version_match.group(i)) for i in range(1, 4))
    200  1.1  christos     return compiler, version
    201  1.1  christos 
    202  1.1  christos 
    203  1.1  christos def overflow_ubsan_flags(cc, cxx):
    204  1.1  christos     compiler, version = compiler_version(cc, cxx)
    205  1.1  christos     if compiler == 'gcc' and version < (8, 0, 0):
    206  1.1  christos         return ['-fno-sanitize=signed-integer-overflow']
    207  1.1  christos     if compiler == 'gcc' or (compiler == 'clang' and version >= (5, 0, 0)):
    208  1.1  christos         return ['-fno-sanitize=pointer-overflow']
    209  1.1  christos     return []
    210  1.1  christos 
    211  1.1  christos 
    212  1.1  christos def build_parser(args):
    213  1.1  christos     description = """
    214  1.1  christos     Cleans the repository and builds a fuzz target (or all).
    215  1.1  christos     Many flags default to environment variables (default says $X='y').
    216  1.1  christos     Options that aren't enabling features default to the correct values for
    217  1.1  christos     zstd.
    218  1.1  christos     Enable sanitizers with --enable-*san.
    219  1.1  christos     For regression testing just build.
    220  1.1  christos     For libFuzzer set LIB_FUZZING_ENGINE and pass --enable-coverage.
    221  1.1  christos     For AFL set CC and CXX to AFL's compilers and set
    222  1.1  christos     LIB_FUZZING_ENGINE='libregression.a'.
    223  1.1  christos     """
    224  1.1  christos     parser = argparse.ArgumentParser(prog=args.pop(0), description=description)
    225  1.1  christos     parser.add_argument(
    226  1.1  christos         '--lib-fuzzing-engine',
    227  1.1  christos         dest='lib_fuzzing_engine',
    228  1.1  christos         type=str,
    229  1.1  christos         default=LIB_FUZZING_ENGINE,
    230  1.1  christos         help=('The fuzzing engine to use e.g. /path/to/libFuzzer.a '
    231  1.1  christos               "(default: $LIB_FUZZING_ENGINE='{})".format(LIB_FUZZING_ENGINE)))
    232  1.1  christos 
    233  1.1  christos     fuzz_group = parser.add_mutually_exclusive_group()
    234  1.1  christos     fuzz_group.add_argument(
    235  1.1  christos         '--enable-coverage',
    236  1.1  christos         dest='coverage',
    237  1.1  christos         action='store_true',
    238  1.1  christos         help='Enable coverage instrumentation (-fsanitize-coverage)')
    239  1.1  christos     fuzz_group.add_argument(
    240  1.1  christos         '--enable-fuzzer',
    241  1.1  christos         dest='fuzzer',
    242  1.1  christos         action='store_true',
    243  1.1  christos         help=('Enable clang fuzzer (-fsanitize=fuzzer). When enabled '
    244  1.1  christos               'LIB_FUZZING_ENGINE is ignored')
    245  1.1  christos     )
    246  1.1  christos 
    247  1.1  christos     parser.add_argument(
    248  1.1  christos         '--enable-asan', dest='asan', action='store_true', help='Enable UBSAN')
    249  1.1  christos     parser.add_argument(
    250  1.1  christos         '--enable-ubsan',
    251  1.1  christos         dest='ubsan',
    252  1.1  christos         action='store_true',
    253  1.1  christos         help='Enable UBSAN')
    254  1.1  christos     parser.add_argument(
    255  1.1  christos         '--disable-ubsan-pointer-overflow',
    256  1.1  christos         dest='ubsan_pointer_overflow',
    257  1.1  christos         action='store_false',
    258  1.1  christos         help='Disable UBSAN pointer overflow check (known failure)')
    259  1.1  christos     parser.add_argument(
    260  1.1  christos         '--enable-msan', dest='msan', action='store_true', help='Enable MSAN')
    261  1.1  christos     parser.add_argument(
    262  1.1  christos         '--enable-msan-track-origins', dest='msan_track_origins',
    263  1.1  christos         action='store_true', help='Enable MSAN origin tracking')
    264  1.1  christos     parser.add_argument(
    265  1.1  christos         '--msan-extra-cppflags',
    266  1.1  christos         dest='msan_extra_cppflags',
    267  1.1  christos         type=str,
    268  1.1  christos         default=MSAN_EXTRA_CPPFLAGS,
    269  1.1  christos         help="Extra CPPFLAGS for MSAN (default: $MSAN_EXTRA_CPPFLAGS='{}')".
    270  1.1  christos         format(MSAN_EXTRA_CPPFLAGS))
    271  1.1  christos     parser.add_argument(
    272  1.1  christos         '--msan-extra-cflags',
    273  1.1  christos         dest='msan_extra_cflags',
    274  1.1  christos         type=str,
    275  1.1  christos         default=MSAN_EXTRA_CFLAGS,
    276  1.1  christos         help="Extra CFLAGS for MSAN (default: $MSAN_EXTRA_CFLAGS='{}')".format(
    277  1.1  christos             MSAN_EXTRA_CFLAGS))
    278  1.1  christos     parser.add_argument(
    279  1.1  christos         '--msan-extra-cxxflags',
    280  1.1  christos         dest='msan_extra_cxxflags',
    281  1.1  christos         type=str,
    282  1.1  christos         default=MSAN_EXTRA_CXXFLAGS,
    283  1.1  christos         help="Extra CXXFLAGS for MSAN (default: $MSAN_EXTRA_CXXFLAGS='{}')".
    284  1.1  christos         format(MSAN_EXTRA_CXXFLAGS))
    285  1.1  christos     parser.add_argument(
    286  1.1  christos         '--msan-extra-ldflags',
    287  1.1  christos         dest='msan_extra_ldflags',
    288  1.1  christos         type=str,
    289  1.1  christos         default=MSAN_EXTRA_LDFLAGS,
    290  1.1  christos         help="Extra LDFLAGS for MSAN (default: $MSAN_EXTRA_LDFLAGS='{}')".
    291  1.1  christos         format(MSAN_EXTRA_LDFLAGS))
    292  1.1  christos     parser.add_argument(
    293  1.1  christos         '--enable-sanitize-recover',
    294  1.1  christos         dest='sanitize_recover',
    295  1.1  christos         action='store_true',
    296  1.1  christos         help='Non-fatal sanitizer errors where possible')
    297  1.1  christos     parser.add_argument(
    298  1.1  christos         '--debug',
    299  1.1  christos         dest='debug',
    300  1.1  christos         type=int,
    301  1.1  christos         default=1,
    302  1.1  christos         help='Set DEBUGLEVEL (default: 1)')
    303  1.1  christos     parser.add_argument(
    304  1.1  christos         '--force-memory-access',
    305  1.1  christos         dest='memory_access',
    306  1.1  christos         type=int,
    307  1.1  christos         default=0,
    308  1.1  christos         help='Set MEM_FORCE_MEMORY_ACCESS (default: 0)')
    309  1.1  christos     parser.add_argument(
    310  1.1  christos         '--fuzz-rng-seed-size',
    311  1.1  christos         dest='fuzz_rng_seed_size',
    312  1.1  christos         type=int,
    313  1.1  christos         default=4,
    314  1.1  christos         help='Set FUZZ_RNG_SEED_SIZE (default: 4)')
    315  1.1  christos     parser.add_argument(
    316  1.1  christos         '--disable-fuzzing-mode',
    317  1.1  christos         dest='fuzzing_mode',
    318  1.1  christos         action='store_false',
    319  1.1  christos         help='Do not define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION')
    320  1.1  christos     parser.add_argument(
    321  1.1  christos         '--enable-stateful-fuzzing',
    322  1.1  christos         dest='stateful_fuzzing',
    323  1.1  christos         action='store_true',
    324  1.1  christos         help='Reuse contexts between runs (makes reproduction impossible)')
    325  1.1  christos     parser.add_argument(
    326  1.1  christos         '--custom-seq-prod',
    327  1.1  christos         dest='third_party_seq_prod_obj',
    328  1.1  christos         type=str,
    329  1.1  christos         default=THIRD_PARTY_SEQ_PROD_OBJ,
    330  1.1  christos         help='Path to an object file with symbols for fuzzing your sequence producer plugin.')
    331  1.1  christos     parser.add_argument(
    332  1.1  christos         '--cc',
    333  1.1  christos         dest='cc',
    334  1.1  christos         type=str,
    335  1.1  christos         default=CC,
    336  1.1  christos         help="CC (default: $CC='{}')".format(CC))
    337  1.1  christos     parser.add_argument(
    338  1.1  christos         '--cxx',
    339  1.1  christos         dest='cxx',
    340  1.1  christos         type=str,
    341  1.1  christos         default=CXX,
    342  1.1  christos         help="CXX (default: $CXX='{}')".format(CXX))
    343  1.1  christos     parser.add_argument(
    344  1.1  christos         '--cppflags',
    345  1.1  christos         dest='cppflags',
    346  1.1  christos         type=str,
    347  1.1  christos         default=CPPFLAGS,
    348  1.1  christos         help="CPPFLAGS (default: $CPPFLAGS='{}')".format(CPPFLAGS))
    349  1.1  christos     parser.add_argument(
    350  1.1  christos         '--cflags',
    351  1.1  christos         dest='cflags',
    352  1.1  christos         type=str,
    353  1.1  christos         default=CFLAGS,
    354  1.1  christos         help="CFLAGS (default: $CFLAGS='{}')".format(CFLAGS))
    355  1.1  christos     parser.add_argument(
    356  1.1  christos         '--cxxflags',
    357  1.1  christos         dest='cxxflags',
    358  1.1  christos         type=str,
    359  1.1  christos         default=CXXFLAGS,
    360  1.1  christos         help="CXXFLAGS (default: $CXXFLAGS='{}')".format(CXXFLAGS))
    361  1.1  christos     parser.add_argument(
    362  1.1  christos         '--ldflags',
    363  1.1  christos         dest='ldflags',
    364  1.1  christos         type=str,
    365  1.1  christos         default=LDFLAGS,
    366  1.1  christos         help="LDFLAGS (default: $LDFLAGS='{}')".format(LDFLAGS))
    367  1.1  christos     parser.add_argument(
    368  1.1  christos         '--mflags',
    369  1.1  christos         dest='mflags',
    370  1.1  christos         type=str,
    371  1.1  christos         default=MFLAGS,
    372  1.1  christos         help="Extra Make flags (default: $MFLAGS='{}')".format(MFLAGS))
    373  1.1  christos     parser.add_argument(
    374  1.1  christos         'TARGET',
    375  1.1  christos         nargs='*',
    376  1.1  christos         type=str,
    377  1.1  christos         help='Fuzz target(s) to build {{{}}}'.format(', '.join(ALL_TARGETS))
    378  1.1  christos     )
    379  1.1  christos     args = parser.parse_args(args)
    380  1.1  christos     args = parse_env_flags(args, ' '.join(
    381  1.1  christos         [args.cppflags, args.cflags, args.cxxflags, args.ldflags]))
    382  1.1  christos 
    383  1.1  christos     # Check option sanity
    384  1.1  christos     if args.msan and (args.asan or args.ubsan):
    385  1.1  christos         raise RuntimeError('MSAN may not be used with any other sanitizers')
    386  1.1  christos     if args.msan_track_origins and not args.msan:
    387  1.1  christos         raise RuntimeError('--enable-msan-track-origins requires MSAN')
    388  1.1  christos     if args.sanitize_recover and not args.sanitize:
    389  1.1  christos         raise RuntimeError('--enable-sanitize-recover but no sanitizers used')
    390  1.1  christos 
    391  1.1  christos     return args
    392  1.1  christos 
    393  1.1  christos 
    394  1.1  christos def build(args):
    395  1.1  christos     try:
    396  1.1  christos         args = build_parser(args)
    397  1.1  christos     except Exception as e:
    398  1.1  christos         print(e)
    399  1.1  christos         return 1
    400  1.1  christos     # The compilation flags we are setting
    401  1.1  christos     targets = args.TARGET
    402  1.1  christos     cc = args.cc
    403  1.1  christos     cxx = args.cxx
    404  1.1  christos     cppflags = shlex.split(args.cppflags)
    405  1.1  christos     cflags = shlex.split(args.cflags)
    406  1.1  christos     ldflags = shlex.split(args.ldflags)
    407  1.1  christos     cxxflags = shlex.split(args.cxxflags)
    408  1.1  christos     mflags = shlex.split(args.mflags)
    409  1.1  christos     # Flags to be added to both cflags and cxxflags
    410  1.1  christos     common_flags = [
    411  1.1  christos         '-Werror',
    412  1.1  christos         '-Wno-error=declaration-after-statement',
    413  1.1  christos         '-Wno-error=c++-compat',
    414  1.1  christos         '-Wno-error=deprecated' # C files are sometimes compiled with CXX
    415  1.1  christos     ]
    416  1.1  christos 
    417  1.1  christos     cppflags += [
    418  1.1  christos         '-DDEBUGLEVEL={}'.format(args.debug),
    419  1.1  christos         '-DMEM_FORCE_MEMORY_ACCESS={}'.format(args.memory_access),
    420  1.1  christos         '-DFUZZ_RNG_SEED_SIZE={}'.format(args.fuzz_rng_seed_size),
    421  1.1  christos     ]
    422  1.1  christos 
    423  1.1  christos     # Set flags for options
    424  1.1  christos     assert not (args.fuzzer and args.coverage)
    425  1.1  christos     if args.coverage:
    426  1.1  christos         common_flags += [
    427  1.1  christos             '-fsanitize-coverage=trace-pc-guard,indirect-calls,trace-cmp'
    428  1.1  christos         ]
    429  1.1  christos     if args.fuzzer:
    430  1.1  christos         common_flags += ['-fsanitize=fuzzer']
    431  1.1  christos         args.lib_fuzzing_engine = ''
    432  1.1  christos 
    433  1.1  christos     mflags += ['LIB_FUZZING_ENGINE={}'.format(args.lib_fuzzing_engine)]
    434  1.1  christos 
    435  1.1  christos     if args.sanitize_recover:
    436  1.1  christos         recover_flags = ['-fsanitize-recover=all']
    437  1.1  christos     else:
    438  1.1  christos         recover_flags = ['-fno-sanitize-recover=all']
    439  1.1  christos     if args.sanitize:
    440  1.1  christos         common_flags += recover_flags
    441  1.1  christos 
    442  1.1  christos     if args.msan:
    443  1.1  christos         msan_flags = ['-fsanitize=memory']
    444  1.1  christos         if args.msan_track_origins:
    445  1.1  christos             msan_flags += ['-fsanitize-memory-track-origins']
    446  1.1  christos         common_flags += msan_flags
    447  1.1  christos         # Append extra MSAN flags (it might require special setup)
    448  1.1  christos         cppflags += [args.msan_extra_cppflags]
    449  1.1  christos         cflags += [args.msan_extra_cflags]
    450  1.1  christos         cxxflags += [args.msan_extra_cxxflags]
    451  1.1  christos         ldflags += [args.msan_extra_ldflags]
    452  1.1  christos 
    453  1.1  christos     if args.asan:
    454  1.1  christos         common_flags += ['-fsanitize=address']
    455  1.1  christos 
    456  1.1  christos     if args.ubsan:
    457  1.1  christos         ubsan_flags = ['-fsanitize=undefined']
    458  1.1  christos         if not args.ubsan_pointer_overflow:
    459  1.1  christos             ubsan_flags += overflow_ubsan_flags(cc, cxx)
    460  1.1  christos         common_flags += ubsan_flags
    461  1.1  christos 
    462  1.1  christos     if args.stateful_fuzzing:
    463  1.1  christos         cppflags += ['-DSTATEFUL_FUZZING']
    464  1.1  christos 
    465  1.1  christos     if args.third_party_seq_prod_obj:
    466  1.1  christos         cppflags += ['-DFUZZ_THIRD_PARTY_SEQ_PROD']
    467  1.1  christos         mflags += ['THIRD_PARTY_SEQ_PROD_OBJ={}'.format(args.third_party_seq_prod_obj)]
    468  1.1  christos 
    469  1.1  christos     if args.fuzzing_mode:
    470  1.1  christos         cppflags += ['-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION']
    471  1.1  christos 
    472  1.1  christos     if args.lib_fuzzing_engine == 'libregression.a':
    473  1.1  christos         targets = ['libregression.a'] + targets
    474  1.1  christos 
    475  1.1  christos     # Append the common flags
    476  1.1  christos     cflags += common_flags
    477  1.1  christos     cxxflags += common_flags
    478  1.1  christos 
    479  1.1  christos     # Prepare the flags for Make
    480  1.1  christos     cc_str = "CC={}".format(cc)
    481  1.1  christos     cxx_str = "CXX={}".format(cxx)
    482  1.1  christos     cppflags_str = "CPPFLAGS={}".format(' '.join(cppflags))
    483  1.1  christos     cflags_str = "CFLAGS={}".format(' '.join(cflags))
    484  1.1  christos     cxxflags_str = "CXXFLAGS={}".format(' '.join(cxxflags))
    485  1.1  christos     ldflags_str = "LDFLAGS={}".format(' '.join(ldflags))
    486  1.1  christos 
    487  1.1  christos     # Print the flags
    488  1.1  christos     print('MFLAGS={}'.format(' '.join(mflags)))
    489  1.1  christos     print(cc_str)
    490  1.1  christos     print(cxx_str)
    491  1.1  christos     print(cppflags_str)
    492  1.1  christos     print(cflags_str)
    493  1.1  christos     print(cxxflags_str)
    494  1.1  christos     print(ldflags_str)
    495  1.1  christos 
    496  1.1  christos     # Clean and build
    497  1.1  christos     clean_cmd = ['make', 'clean'] + mflags
    498  1.1  christos     print(' '.join(clean_cmd))
    499  1.1  christos     subprocess.check_call(clean_cmd)
    500  1.1  christos     build_cmd = [
    501  1.1  christos         'make',
    502  1.1  christos         '-j',
    503  1.1  christos         cc_str,
    504  1.1  christos         cxx_str,
    505  1.1  christos         cppflags_str,
    506  1.1  christos         cflags_str,
    507  1.1  christos         cxxflags_str,
    508  1.1  christos         ldflags_str,
    509  1.1  christos     ] + mflags + targets
    510  1.1  christos     print(' '.join(build_cmd))
    511  1.1  christos     subprocess.check_call(build_cmd)
    512  1.1  christos     return 0
    513  1.1  christos 
    514  1.1  christos 
    515  1.1  christos def libfuzzer_parser(args):
    516  1.1  christos     description = """
    517  1.1  christos     Runs a libfuzzer binary.
    518  1.1  christos     Passes all extra arguments to libfuzzer.
    519  1.1  christos     The fuzzer should have been build with LIB_FUZZING_ENGINE pointing to
    520  1.1  christos     libFuzzer.a.
    521  1.1  christos     Generates output in the CORPORA directory, puts crashes in the ARTIFACT
    522  1.1  christos     directory, and takes extra input from the SEED directory.
    523  1.1  christos     To merge AFL's output pass the SEED as AFL's output directory and pass
    524  1.1  christos     '-merge=1'.
    525  1.1  christos     """
    526  1.1  christos     parser = argparse.ArgumentParser(prog=args.pop(0), description=description)
    527  1.1  christos     parser.add_argument(
    528  1.1  christos         '--corpora',
    529  1.1  christos         type=str,
    530  1.1  christos         help='Override the default corpora dir (default: {})'.format(
    531  1.1  christos             abs_join(CORPORA_DIR, 'TARGET')))
    532  1.1  christos     parser.add_argument(
    533  1.1  christos         '--artifact',
    534  1.1  christos         type=str,
    535  1.1  christos         help='Override the default artifact dir (default: {})'.format(
    536  1.1  christos             abs_join(CORPORA_DIR, 'TARGET-crash')))
    537  1.1  christos     parser.add_argument(
    538  1.1  christos         '--seed',
    539  1.1  christos         type=str,
    540  1.1  christos         help='Override the default seed dir (default: {})'.format(
    541  1.1  christos             abs_join(CORPORA_DIR, 'TARGET-seed')))
    542  1.1  christos     parser.add_argument(
    543  1.1  christos         'TARGET',
    544  1.1  christos         type=str,
    545  1.1  christos         help='Fuzz target(s) to build {{{}}}'.format(', '.join(TARGETS)))
    546  1.1  christos     args, extra = parser.parse_known_args(args)
    547  1.1  christos     args.extra = extra
    548  1.1  christos 
    549  1.1  christos     if args.TARGET and args.TARGET not in TARGETS:
    550  1.1  christos         raise RuntimeError('{} is not a valid target'.format(args.TARGET))
    551  1.1  christos 
    552  1.1  christos     return args
    553  1.1  christos 
    554  1.1  christos 
    555  1.1  christos def libfuzzer(target, corpora=None, artifact=None, seed=None, extra_args=None):
    556  1.1  christos     if corpora is None:
    557  1.1  christos         corpora = abs_join(CORPORA_DIR, target)
    558  1.1  christos     if artifact is None:
    559  1.1  christos         artifact = abs_join(CORPORA_DIR, '{}-crash'.format(target))
    560  1.1  christos     if seed is None:
    561  1.1  christos         seed = abs_join(CORPORA_DIR, '{}-seed'.format(target))
    562  1.1  christos     if extra_args is None:
    563  1.1  christos         extra_args = []
    564  1.1  christos 
    565  1.1  christos     target = abs_join(FUZZ_DIR, target)
    566  1.1  christos 
    567  1.1  christos     corpora = [create(corpora)]
    568  1.1  christos     artifact = create(artifact)
    569  1.1  christos     seed = check(seed)
    570  1.1  christos 
    571  1.1  christos     corpora += [artifact]
    572  1.1  christos     if seed is not None:
    573  1.1  christos         corpora += [seed]
    574  1.1  christos 
    575  1.1  christos     cmd = [target, '-artifact_prefix={}/'.format(artifact)]
    576  1.1  christos     cmd += corpora + extra_args
    577  1.1  christos     print(' '.join(cmd))
    578  1.1  christos     subprocess.check_call(cmd)
    579  1.1  christos 
    580  1.1  christos 
    581  1.1  christos def libfuzzer_cmd(args):
    582  1.1  christos     try:
    583  1.1  christos         args = libfuzzer_parser(args)
    584  1.1  christos     except Exception as e:
    585  1.1  christos         print(e)
    586  1.1  christos         return 1
    587  1.1  christos     libfuzzer(args.TARGET, args.corpora, args.artifact, args.seed, args.extra)
    588  1.1  christos     return 0
    589  1.1  christos 
    590  1.1  christos 
    591  1.1  christos def afl_parser(args):
    592  1.1  christos     description = """
    593  1.1  christos     Runs an afl-fuzz job.
    594  1.1  christos     Passes all extra arguments to afl-fuzz.
    595  1.1  christos     The fuzzer should have been built with CC/CXX set to the AFL compilers,
    596  1.1  christos     and with LIB_FUZZING_ENGINE='libregression.a'.
    597  1.1  christos     Takes input from CORPORA and writes output to OUTPUT.
    598  1.1  christos     Uses AFL_FUZZ as the binary (set from flag or environment variable).
    599  1.1  christos     """
    600  1.1  christos     parser = argparse.ArgumentParser(prog=args.pop(0), description=description)
    601  1.1  christos     parser.add_argument(
    602  1.1  christos         '--corpora',
    603  1.1  christos         type=str,
    604  1.1  christos         help='Override the default corpora dir (default: {})'.format(
    605  1.1  christos             abs_join(CORPORA_DIR, 'TARGET')))
    606  1.1  christos     parser.add_argument(
    607  1.1  christos         '--output',
    608  1.1  christos         type=str,
    609  1.1  christos         help='Override the default AFL output dir (default: {})'.format(
    610  1.1  christos             abs_join(CORPORA_DIR, 'TARGET-afl')))
    611  1.1  christos     parser.add_argument(
    612  1.1  christos         '--afl-fuzz',
    613  1.1  christos         type=str,
    614  1.1  christos         default=AFL_FUZZ,
    615  1.1  christos         help='AFL_FUZZ (default: $AFL_FUZZ={})'.format(AFL_FUZZ))
    616  1.1  christos     parser.add_argument(
    617  1.1  christos         'TARGET',
    618  1.1  christos         type=str,
    619  1.1  christos         help='Fuzz target(s) to build {{{}}}'.format(', '.join(TARGETS)))
    620  1.1  christos     args, extra = parser.parse_known_args(args)
    621  1.1  christos     args.extra = extra
    622  1.1  christos 
    623  1.1  christos     if args.TARGET and args.TARGET not in TARGETS:
    624  1.1  christos         raise RuntimeError('{} is not a valid target'.format(args.TARGET))
    625  1.1  christos 
    626  1.1  christos     if not args.corpora:
    627  1.1  christos         args.corpora = abs_join(CORPORA_DIR, args.TARGET)
    628  1.1  christos     if not args.output:
    629  1.1  christos         args.output = abs_join(CORPORA_DIR, '{}-afl'.format(args.TARGET))
    630  1.1  christos 
    631  1.1  christos     return args
    632  1.1  christos 
    633  1.1  christos 
    634  1.1  christos def afl(args):
    635  1.1  christos     try:
    636  1.1  christos         args = afl_parser(args)
    637  1.1  christos     except Exception as e:
    638  1.1  christos         print(e)
    639  1.1  christos         return 1
    640  1.1  christos     target = abs_join(FUZZ_DIR, args.TARGET)
    641  1.1  christos 
    642  1.1  christos     corpora = create(args.corpora)
    643  1.1  christos     output = create(args.output)
    644  1.1  christos 
    645  1.1  christos     cmd = [args.afl_fuzz, '-i', corpora, '-o', output] + args.extra
    646  1.1  christos     cmd += [target, '@@']
    647  1.1  christos     print(' '.join(cmd))
    648  1.1  christos     subprocess.call(cmd)
    649  1.1  christos     return 0
    650  1.1  christos 
    651  1.1  christos 
    652  1.1  christos def regression(args):
    653  1.1  christos     try:
    654  1.1  christos         description = """
    655  1.1  christos         Runs one or more regression tests.
    656  1.1  christos         The fuzzer should have been built with
    657  1.1  christos         LIB_FUZZING_ENGINE='libregression.a'.
    658  1.1  christos         Takes input from CORPORA.
    659  1.1  christos         """
    660  1.1  christos         args = targets_parser(args, description)
    661  1.1  christos     except Exception as e:
    662  1.1  christos         print(e)
    663  1.1  christos         return 1
    664  1.1  christos     for target in args.TARGET:
    665  1.1  christos         corpora = create(abs_join(CORPORA_DIR, target))
    666  1.1  christos         target = abs_join(FUZZ_DIR, target)
    667  1.1  christos         cmd = [target, corpora]
    668  1.1  christos         print(' '.join(cmd))
    669  1.1  christos         subprocess.check_call(cmd)
    670  1.1  christos     return 0
    671  1.1  christos 
    672  1.1  christos 
    673  1.1  christos def gen_parser(args):
    674  1.1  christos     description = """
    675  1.1  christos     Generate a seed corpus appropriate for TARGET with data generated with
    676  1.1  christos     decodecorpus.
    677  1.1  christos     The fuzz inputs are prepended with a seed before the zstd data, so the
    678  1.1  christos     output of decodecorpus shouldn't be used directly.
    679  1.1  christos     Generates NUMBER samples prepended with FUZZ_RNG_SEED_SIZE random bytes and
    680  1.1  christos     puts the output in SEED.
    681  1.1  christos     DECODECORPUS is the decodecorpus binary, and must already be built.
    682  1.1  christos     """
    683  1.1  christos     parser = argparse.ArgumentParser(prog=args.pop(0), description=description)
    684  1.1  christos     parser.add_argument(
    685  1.1  christos         '--number',
    686  1.1  christos         '-n',
    687  1.1  christos         type=int,
    688  1.1  christos         default=100,
    689  1.1  christos         help='Number of samples to generate')
    690  1.1  christos     parser.add_argument(
    691  1.1  christos         '--max-size-log',
    692  1.1  christos         type=int,
    693  1.1  christos         default=18,
    694  1.1  christos         help='Maximum sample size to generate')
    695  1.1  christos     parser.add_argument(
    696  1.1  christos         '--seed',
    697  1.1  christos         type=str,
    698  1.1  christos         help='Override the default seed dir (default: {})'.format(
    699  1.1  christos             abs_join(CORPORA_DIR, 'TARGET-seed')))
    700  1.1  christos     parser.add_argument(
    701  1.1  christos         '--decodecorpus',
    702  1.1  christos         type=str,
    703  1.1  christos         default=DECODECORPUS,
    704  1.1  christos         help="decodecorpus binary (default: $DECODECORPUS='{}')".format(
    705  1.1  christos             DECODECORPUS))
    706  1.1  christos     parser.add_argument(
    707  1.1  christos         '--zstd',
    708  1.1  christos         type=str,
    709  1.1  christos         default=ZSTD,
    710  1.1  christos         help="zstd binary (default: $ZSTD='{}')".format(ZSTD))
    711  1.1  christos     parser.add_argument(
    712  1.1  christos         '--fuzz-rng-seed-size',
    713  1.1  christos         type=int,
    714  1.1  christos         default=4,
    715  1.1  christos         help="FUZZ_RNG_SEED_SIZE used for generate the samples (must match)"
    716  1.1  christos     )
    717  1.1  christos     parser.add_argument(
    718  1.1  christos         'TARGET',
    719  1.1  christos         type=str,
    720  1.1  christos         help='Fuzz target(s) to build {{{}}}'.format(', '.join(TARGETS)))
    721  1.1  christos     args, extra = parser.parse_known_args(args)
    722  1.1  christos     args.extra = extra
    723  1.1  christos 
    724  1.1  christos     if args.TARGET and args.TARGET not in TARGETS:
    725  1.1  christos         raise RuntimeError('{} is not a valid target'.format(args.TARGET))
    726  1.1  christos 
    727  1.1  christos     if not args.seed:
    728  1.1  christos         args.seed = abs_join(CORPORA_DIR, '{}-seed'.format(args.TARGET))
    729  1.1  christos 
    730  1.1  christos     if not os.path.isfile(args.decodecorpus):
    731  1.1  christos         raise RuntimeError("{} is not a file run 'make -C {} decodecorpus'".
    732  1.1  christos                            format(args.decodecorpus, abs_join(FUZZ_DIR, '..')))
    733  1.1  christos 
    734  1.1  christos     return args
    735  1.1  christos 
    736  1.1  christos 
    737  1.1  christos def gen(args):
    738  1.1  christos     try:
    739  1.1  christos         args = gen_parser(args)
    740  1.1  christos     except Exception as e:
    741  1.1  christos         print(e)
    742  1.1  christos         return 1
    743  1.1  christos 
    744  1.1  christos     seed = create(args.seed)
    745  1.1  christos     with tmpdir() as compressed, tmpdir() as decompressed, tmpdir() as dict:
    746  1.1  christos         info = TARGET_INFO[args.TARGET]
    747  1.1  christos 
    748  1.1  christos         if info.input_type == InputType.DICTIONARY_DATA:
    749  1.1  christos             number = max(args.number, 1000)
    750  1.1  christos         else:
    751  1.1  christos             number = args.number
    752  1.1  christos         cmd = [
    753  1.1  christos             args.decodecorpus,
    754  1.1  christos             '-n{}'.format(args.number),
    755  1.1  christos             '-p{}/'.format(compressed),
    756  1.1  christos             '-o{}'.format(decompressed),
    757  1.1  christos         ]
    758  1.1  christos 
    759  1.1  christos         if info.frame_type == FrameType.BLOCK:
    760  1.1  christos             cmd += [
    761  1.1  christos                 '--gen-blocks',
    762  1.1  christos                 '--max-block-size-log={}'.format(min(args.max_size_log, 17))
    763  1.1  christos             ]
    764  1.1  christos         else:
    765  1.1  christos             cmd += ['--max-content-size-log={}'.format(args.max_size_log)]
    766  1.1  christos 
    767  1.1  christos         print(' '.join(cmd))
    768  1.1  christos         subprocess.check_call(cmd)
    769  1.1  christos 
    770  1.1  christos         if info.input_type == InputType.RAW_DATA:
    771  1.1  christos             print('using decompressed data in {}'.format(decompressed))
    772  1.1  christos             samples = decompressed
    773  1.1  christos         elif info.input_type == InputType.COMPRESSED_DATA:
    774  1.1  christos             print('using compressed data in {}'.format(compressed))
    775  1.1  christos             samples = compressed
    776  1.1  christos         else:
    777  1.1  christos             assert info.input_type == InputType.DICTIONARY_DATA
    778  1.1  christos             print('making dictionary data from {}'.format(decompressed))
    779  1.1  christos             samples = dict
    780  1.1  christos             min_dict_size_log = 9
    781  1.1  christos             max_dict_size_log = max(min_dict_size_log + 1, args.max_size_log)
    782  1.1  christos             for dict_size_log in range(min_dict_size_log, max_dict_size_log):
    783  1.1  christos                 dict_size = 1 << dict_size_log
    784  1.1  christos                 cmd = [
    785  1.1  christos                     args.zstd,
    786  1.1  christos                     '--train',
    787  1.1  christos                     '-r', decompressed,
    788  1.1  christos                     '--maxdict={}'.format(dict_size),
    789  1.1  christos                     '-o', abs_join(dict, '{}.zstd-dict'.format(dict_size))
    790  1.1  christos                 ]
    791  1.1  christos                 print(' '.join(cmd))
    792  1.1  christos                 subprocess.check_call(cmd)
    793  1.1  christos 
    794  1.1  christos         # Copy the samples over and prepend the RNG seeds
    795  1.1  christos         for name in os.listdir(samples):
    796  1.1  christos             samplename = abs_join(samples, name)
    797  1.1  christos             outname = abs_join(seed, name)
    798  1.1  christos             with open(samplename, 'rb') as sample:
    799  1.1  christos                 with open(outname, 'wb') as out:
    800  1.1  christos                     CHUNK_SIZE = 131072
    801  1.1  christos                     chunk = sample.read(CHUNK_SIZE)
    802  1.1  christos                     while len(chunk) > 0:
    803  1.1  christos                         out.write(chunk)
    804  1.1  christos                         chunk = sample.read(CHUNK_SIZE)
    805  1.1  christos     return 0
    806  1.1  christos 
    807  1.1  christos 
    808  1.1  christos def minimize(args):
    809  1.1  christos     try:
    810  1.1  christos         description = """
    811  1.1  christos         Runs a libfuzzer fuzzer with -merge=1 to build a minimal corpus in
    812  1.1  christos         TARGET_seed_corpus. All extra args are passed to libfuzzer.
    813  1.1  christos         """
    814  1.1  christos         args = targets_parser(args, description)
    815  1.1  christos     except Exception as e:
    816  1.1  christos         print(e)
    817  1.1  christos         return 1
    818  1.1  christos 
    819  1.1  christos     for target in args.TARGET:
    820  1.1  christos         # Merge the corpus + anything else into the seed_corpus
    821  1.1  christos         corpus = abs_join(CORPORA_DIR, target)
    822  1.1  christos         seed_corpus = abs_join(CORPORA_DIR, "{}_seed_corpus".format(target))
    823  1.1  christos         extra_args = [corpus, "-merge=1"] + args.extra
    824  1.1  christos         libfuzzer(target, corpora=seed_corpus, extra_args=extra_args)
    825  1.1  christos         seeds = set(os.listdir(seed_corpus))
    826  1.1  christos         # Copy all crashes directly into the seed_corpus if not already present
    827  1.1  christos         crashes = abs_join(CORPORA_DIR, '{}-crash'.format(target))
    828  1.1  christos         for crash in os.listdir(crashes):
    829  1.1  christos             if crash not in seeds:
    830  1.1  christos                 shutil.copy(abs_join(crashes, crash), seed_corpus)
    831  1.1  christos                 seeds.add(crash)
    832  1.1  christos 
    833  1.1  christos 
    834  1.1  christos def zip_cmd(args):
    835  1.1  christos     try:
    836  1.1  christos         description = """
    837  1.1  christos         Zips up the seed corpus.
    838  1.1  christos         """
    839  1.1  christos         args = targets_parser(args, description)
    840  1.1  christos     except Exception as e:
    841  1.1  christos         print(e)
    842  1.1  christos         return 1
    843  1.1  christos 
    844  1.1  christos     for target in args.TARGET:
    845  1.1  christos         # Zip the seed_corpus
    846  1.1  christos         seed_corpus = abs_join(CORPORA_DIR, "{}_seed_corpus".format(target))
    847  1.1  christos         zip_file = "{}.zip".format(seed_corpus)
    848  1.1  christos         cmd = ["zip", "-r", "-q", "-j", "-9", zip_file, "."]
    849  1.1  christos         print(' '.join(cmd))
    850  1.1  christos         subprocess.check_call(cmd, cwd=seed_corpus)
    851  1.1  christos 
    852  1.1  christos 
    853  1.1  christos def list_cmd(args):
    854  1.1  christos     print("\n".join(TARGETS))
    855  1.1  christos 
    856  1.1  christos 
    857  1.1  christos def short_help(args):
    858  1.1  christos     name = args[0]
    859  1.1  christos     print("Usage: {} [OPTIONS] COMMAND [ARGS]...\n".format(name))
    860  1.1  christos 
    861  1.1  christos 
    862  1.1  christos def help(args):
    863  1.1  christos     short_help(args)
    864  1.1  christos     print("\tfuzzing helpers (select a command and pass -h for help)\n")
    865  1.1  christos     print("Options:")
    866  1.1  christos     print("\t-h, --help\tPrint this message")
    867  1.1  christos     print("")
    868  1.1  christos     print("Commands:")
    869  1.1  christos     print("\tbuild\t\tBuild a fuzzer")
    870  1.1  christos     print("\tlibfuzzer\tRun a libFuzzer fuzzer")
    871  1.1  christos     print("\tafl\t\tRun an AFL fuzzer")
    872  1.1  christos     print("\tregression\tRun a regression test")
    873  1.1  christos     print("\tgen\t\tGenerate a seed corpus for a fuzzer")
    874  1.1  christos     print("\tminimize\tMinimize the test corpora")
    875  1.1  christos     print("\tzip\t\tZip the minimized corpora up")
    876  1.1  christos     print("\tlist\t\tList the available targets")
    877  1.1  christos 
    878  1.1  christos 
    879  1.1  christos def main():
    880  1.1  christos     args = sys.argv
    881  1.1  christos     if len(args) < 2:
    882  1.1  christos         help(args)
    883  1.1  christos         return 1
    884  1.1  christos     if args[1] == '-h' or args[1] == '--help' or args[1] == '-H':
    885  1.1  christos         help(args)
    886  1.1  christos         return 1
    887  1.1  christos     command = args.pop(1)
    888  1.1  christos     args[0] = "{} {}".format(args[0], command)
    889  1.1  christos     if command == "build":
    890  1.1  christos         return build(args)
    891  1.1  christos     if command == "libfuzzer":
    892  1.1  christos         return libfuzzer_cmd(args)
    893  1.1  christos     if command == "regression":
    894  1.1  christos         return regression(args)
    895  1.1  christos     if command == "afl":
    896  1.1  christos         return afl(args)
    897  1.1  christos     if command == "gen":
    898  1.1  christos         return gen(args)
    899  1.1  christos     if command == "minimize":
    900  1.1  christos         return minimize(args)
    901  1.1  christos     if command == "zip":
    902  1.1  christos         return zip_cmd(args)
    903  1.1  christos     if command == "list":
    904  1.1  christos         return list_cmd(args)
    905  1.1  christos     short_help(args)
    906  1.1  christos     print("Error: No such command {} (pass -h for help)".format(command))
    907  1.1  christos     return 1
    908  1.1  christos 
    909  1.1  christos 
    910  1.1  christos if __name__ == "__main__":
    911  1.1  christos     sys.exit(main())
    912