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