diff options
Diffstat (limited to 'tests/fuzz/fuzz.py')
-rwxr-xr-x | tests/fuzz/fuzz.py | 884 |
1 files changed, 0 insertions, 884 deletions
diff --git a/tests/fuzz/fuzz.py b/tests/fuzz/fuzz.py deleted file mode 100755 index 87f115afde99..000000000000 --- a/tests/fuzz/fuzz.py +++ /dev/null @@ -1,884 +0,0 @@ -#!/usr/bin/env python - -# ################################################################ -# Copyright (c) 2016-present, Facebook, Inc. -# All rights reserved. -# -# This source code is licensed under both the BSD-style license (found in the -# LICENSE file in the root directory of this source tree) and the GPLv2 (found -# in the COPYING file in the root directory of this source tree). -# ########################################################################## - -import argparse -import contextlib -import os -import re -import shlex -import shutil -import subprocess -import sys -import tempfile - - -def abs_join(a, *p): - return os.path.abspath(os.path.join(a, *p)) - - -class InputType(object): - RAW_DATA = 1 - COMPRESSED_DATA = 2 - DICTIONARY_DATA = 3 - - -class FrameType(object): - ZSTD = 1 - BLOCK = 2 - - -class TargetInfo(object): - def __init__(self, input_type, frame_type=FrameType.ZSTD): - self.input_type = input_type - self.frame_type = frame_type - - -# Constants -FUZZ_DIR = os.path.abspath(os.path.dirname(__file__)) -CORPORA_DIR = abs_join(FUZZ_DIR, 'corpora') -TARGET_INFO = { - 'simple_round_trip': TargetInfo(InputType.RAW_DATA), - 'stream_round_trip': TargetInfo(InputType.RAW_DATA), - 'block_round_trip': TargetInfo(InputType.RAW_DATA, FrameType.BLOCK), - 'simple_decompress': TargetInfo(InputType.COMPRESSED_DATA), - 'stream_decompress': TargetInfo(InputType.COMPRESSED_DATA), - 'block_decompress': TargetInfo(InputType.COMPRESSED_DATA, FrameType.BLOCK), - 'dictionary_round_trip': TargetInfo(InputType.RAW_DATA), - 'dictionary_decompress': TargetInfo(InputType.COMPRESSED_DATA), - 'zstd_frame_info': TargetInfo(InputType.COMPRESSED_DATA), - 'simple_compress': TargetInfo(InputType.RAW_DATA), - 'dictionary_loader': TargetInfo(InputType.DICTIONARY_DATA), -} -TARGETS = list(TARGET_INFO.keys()) -ALL_TARGETS = TARGETS + ['all'] -FUZZ_RNG_SEED_SIZE = 4 - -# Standard environment variables -CC = os.environ.get('CC', 'cc') -CXX = os.environ.get('CXX', 'c++') -CPPFLAGS = os.environ.get('CPPFLAGS', '') -CFLAGS = os.environ.get('CFLAGS', '-O3') -CXXFLAGS = os.environ.get('CXXFLAGS', CFLAGS) -LDFLAGS = os.environ.get('LDFLAGS', '') -MFLAGS = os.environ.get('MFLAGS', '-j') - -# Fuzzing environment variables -LIB_FUZZING_ENGINE = os.environ.get('LIB_FUZZING_ENGINE', 'libregression.a') -AFL_FUZZ = os.environ.get('AFL_FUZZ', 'afl-fuzz') -DECODECORPUS = os.environ.get('DECODECORPUS', - abs_join(FUZZ_DIR, '..', 'decodecorpus')) -ZSTD = os.environ.get('ZSTD', abs_join(FUZZ_DIR, '..', '..', 'zstd')) - -# Sanitizer environment variables -MSAN_EXTRA_CPPFLAGS = os.environ.get('MSAN_EXTRA_CPPFLAGS', '') -MSAN_EXTRA_CFLAGS = os.environ.get('MSAN_EXTRA_CFLAGS', '') -MSAN_EXTRA_CXXFLAGS = os.environ.get('MSAN_EXTRA_CXXFLAGS', '') -MSAN_EXTRA_LDFLAGS = os.environ.get('MSAN_EXTRA_LDFLAGS', '') - - -def create(r): - d = os.path.abspath(r) - if not os.path.isdir(d): - os.makedirs(d) - return d - - -def check(r): - d = os.path.abspath(r) - if not os.path.isdir(d): - return None - return d - - -@contextlib.contextmanager -def tmpdir(): - dirpath = tempfile.mkdtemp() - try: - yield dirpath - finally: - shutil.rmtree(dirpath, ignore_errors=True) - - -def parse_targets(in_targets): - targets = set() - for target in in_targets: - if not target: - continue - if target == 'all': - targets = targets.union(TARGETS) - elif target in TARGETS: - targets.add(target) - else: - raise RuntimeError('{} is not a valid target'.format(target)) - return list(targets) - - -def targets_parser(args, description): - parser = argparse.ArgumentParser(prog=args.pop(0), description=description) - parser.add_argument( - 'TARGET', - nargs='*', - type=str, - help='Fuzz target(s) to build {{{}}}'.format(', '.join(ALL_TARGETS))) - args, extra = parser.parse_known_args(args) - args.extra = extra - - args.TARGET = parse_targets(args.TARGET) - - return args - - -def parse_env_flags(args, flags): - """ - Look for flags set by environment variables. - """ - san_flags = ','.join(re.findall('-fsanitize=((?:[a-z]+,?)+)', flags)) - nosan_flags = ','.join(re.findall('-fno-sanitize=((?:[a-z]+,?)+)', flags)) - - def set_sanitizer(sanitizer, default, san, nosan): - if sanitizer in san and sanitizer in nosan: - raise RuntimeError('-fno-sanitize={s} and -fsanitize={s} passed'. - format(s=sanitizer)) - if sanitizer in san: - return True - if sanitizer in nosan: - return False - return default - - san = set(san_flags.split(',')) - nosan = set(nosan_flags.split(',')) - - args.asan = set_sanitizer('address', args.asan, san, nosan) - args.msan = set_sanitizer('memory', args.msan, san, nosan) - args.ubsan = set_sanitizer('undefined', args.ubsan, san, nosan) - - args.sanitize = args.asan or args.msan or args.ubsan - - return args - - -def compiler_version(cc, cxx): - """ - Determines the compiler and version. - Only works for clang and gcc. - """ - cc_version_bytes = subprocess.check_output([cc, "--version"]) - cxx_version_bytes = subprocess.check_output([cxx, "--version"]) - compiler = None - version = None - if b'clang' in cc_version_bytes: - assert(b'clang' in cxx_version_bytes) - compiler = 'clang' - elif b'gcc' in cc_version_bytes: - assert(b'gcc' in cxx_version_bytes or b'g++' in cxx_version_bytes) - compiler = 'gcc' - if compiler is not None: - version_regex = b'([0-9])+\.([0-9])+\.([0-9])+' - version_match = re.search(version_regex, cc_version_bytes) - version = tuple(int(version_match.group(i)) for i in range(1, 4)) - return compiler, version - - -def overflow_ubsan_flags(cc, cxx): - compiler, version = compiler_version(cc, cxx) - if compiler == 'gcc': - return ['-fno-sanitize=signed-integer-overflow'] - if compiler == 'clang' and version >= (5, 0, 0): - return ['-fno-sanitize=pointer-overflow'] - return [] - - -def build_parser(args): - description = """ - Cleans the repository and builds a fuzz target (or all). - Many flags default to environment variables (default says $X='y'). - Options that aren't enabling features default to the correct values for - zstd. - Enable sanitizers with --enable-*san. - For regression testing just build. - For libFuzzer set LIB_FUZZING_ENGINE and pass --enable-coverage. - For AFL set CC and CXX to AFL's compilers and set - LIB_FUZZING_ENGINE='libregression.a'. - """ - parser = argparse.ArgumentParser(prog=args.pop(0), description=description) - parser.add_argument( - '--lib-fuzzing-engine', - dest='lib_fuzzing_engine', - type=str, - default=LIB_FUZZING_ENGINE, - help=('The fuzzing engine to use e.g. /path/to/libFuzzer.a ' - "(default: $LIB_FUZZING_ENGINE='{})".format(LIB_FUZZING_ENGINE))) - - fuzz_group = parser.add_mutually_exclusive_group() - fuzz_group.add_argument( - '--enable-coverage', - dest='coverage', - action='store_true', - help='Enable coverage instrumentation (-fsanitize-coverage)') - fuzz_group.add_argument( - '--enable-fuzzer', - dest='fuzzer', - action='store_true', - help=('Enable clang fuzzer (-fsanitize=fuzzer). When enabled ' - 'LIB_FUZZING_ENGINE is ignored') - ) - - parser.add_argument( - '--enable-asan', dest='asan', action='store_true', help='Enable UBSAN') - parser.add_argument( - '--enable-ubsan', - dest='ubsan', - action='store_true', - help='Enable UBSAN') - parser.add_argument( - '--enable-ubsan-pointer-overflow', - dest='ubsan_pointer_overflow', - action='store_true', - help='Enable UBSAN pointer overflow check (known failure)') - parser.add_argument( - '--enable-msan', dest='msan', action='store_true', help='Enable MSAN') - parser.add_argument( - '--enable-msan-track-origins', dest='msan_track_origins', - action='store_true', help='Enable MSAN origin tracking') - parser.add_argument( - '--msan-extra-cppflags', - dest='msan_extra_cppflags', - type=str, - default=MSAN_EXTRA_CPPFLAGS, - help="Extra CPPFLAGS for MSAN (default: $MSAN_EXTRA_CPPFLAGS='{}')". - format(MSAN_EXTRA_CPPFLAGS)) - parser.add_argument( - '--msan-extra-cflags', - dest='msan_extra_cflags', - type=str, - default=MSAN_EXTRA_CFLAGS, - help="Extra CFLAGS for MSAN (default: $MSAN_EXTRA_CFLAGS='{}')".format( - MSAN_EXTRA_CFLAGS)) - parser.add_argument( - '--msan-extra-cxxflags', - dest='msan_extra_cxxflags', - type=str, - default=MSAN_EXTRA_CXXFLAGS, - help="Extra CXXFLAGS for MSAN (default: $MSAN_EXTRA_CXXFLAGS='{}')". - format(MSAN_EXTRA_CXXFLAGS)) - parser.add_argument( - '--msan-extra-ldflags', - dest='msan_extra_ldflags', - type=str, - default=MSAN_EXTRA_LDFLAGS, - help="Extra LDFLAGS for MSAN (default: $MSAN_EXTRA_LDFLAGS='{}')". - format(MSAN_EXTRA_LDFLAGS)) - parser.add_argument( - '--enable-sanitize-recover', - dest='sanitize_recover', - action='store_true', - help='Non-fatal sanitizer errors where possible') - parser.add_argument( - '--debug', - dest='debug', - type=int, - default=1, - help='Set DEBUGLEVEL (default: 1)') - parser.add_argument( - '--force-memory-access', - dest='memory_access', - type=int, - default=0, - help='Set MEM_FORCE_MEMORY_ACCESS (default: 0)') - parser.add_argument( - '--fuzz-rng-seed-size', - dest='fuzz_rng_seed_size', - type=int, - default=4, - help='Set FUZZ_RNG_SEED_SIZE (default: 4)') - parser.add_argument( - '--disable-fuzzing-mode', - dest='fuzzing_mode', - action='store_false', - help='Do not define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION') - parser.add_argument( - '--enable-stateful-fuzzing', - dest='stateful_fuzzing', - action='store_true', - help='Reuse contexts between runs (makes reproduction impossible)') - parser.add_argument( - '--cc', - dest='cc', - type=str, - default=CC, - help="CC (default: $CC='{}')".format(CC)) - parser.add_argument( - '--cxx', - dest='cxx', - type=str, - default=CXX, - help="CXX (default: $CXX='{}')".format(CXX)) - parser.add_argument( - '--cppflags', - dest='cppflags', - type=str, - default=CPPFLAGS, - help="CPPFLAGS (default: $CPPFLAGS='{}')".format(CPPFLAGS)) - parser.add_argument( - '--cflags', - dest='cflags', - type=str, - default=CFLAGS, - help="CFLAGS (default: $CFLAGS='{}')".format(CFLAGS)) - parser.add_argument( - '--cxxflags', - dest='cxxflags', - type=str, - default=CXXFLAGS, - help="CXXFLAGS (default: $CXXFLAGS='{}')".format(CXXFLAGS)) - parser.add_argument( - '--ldflags', - dest='ldflags', - type=str, - default=LDFLAGS, - help="LDFLAGS (default: $LDFLAGS='{}')".format(LDFLAGS)) - parser.add_argument( - '--mflags', - dest='mflags', - type=str, - default=MFLAGS, - help="Extra Make flags (default: $MFLAGS='{}')".format(MFLAGS)) - parser.add_argument( - 'TARGET', - nargs='*', - type=str, - help='Fuzz target(s) to build {{{}}}'.format(', '.join(ALL_TARGETS)) - ) - args = parser.parse_args(args) - args = parse_env_flags(args, ' '.join( - [args.cppflags, args.cflags, args.cxxflags, args.ldflags])) - - # Check option sanity - if args.msan and (args.asan or args.ubsan): - raise RuntimeError('MSAN may not be used with any other sanitizers') - if args.msan_track_origins and not args.msan: - raise RuntimeError('--enable-msan-track-origins requires MSAN') - if args.ubsan_pointer_overflow and not args.ubsan: - raise RuntimeError('--enable-ubsan-pointer-overflow requires UBSAN') - if args.sanitize_recover and not args.sanitize: - raise RuntimeError('--enable-sanitize-recover but no sanitizers used') - - return args - - -def build(args): - try: - args = build_parser(args) - except Exception as e: - print(e) - return 1 - # The compilation flags we are setting - targets = args.TARGET - cc = args.cc - cxx = args.cxx - cppflags = shlex.split(args.cppflags) - cflags = shlex.split(args.cflags) - ldflags = shlex.split(args.ldflags) - cxxflags = shlex.split(args.cxxflags) - mflags = shlex.split(args.mflags) - # Flags to be added to both cflags and cxxflags - common_flags = [] - - cppflags += [ - '-DDEBUGLEVEL={}'.format(args.debug), - '-DMEM_FORCE_MEMORY_ACCESS={}'.format(args.memory_access), - '-DFUZZ_RNG_SEED_SIZE={}'.format(args.fuzz_rng_seed_size), - ] - - # Set flags for options - assert not (args.fuzzer and args.coverage) - if args.coverage: - common_flags += [ - '-fsanitize-coverage=trace-pc-guard,indirect-calls,trace-cmp' - ] - if args.fuzzer: - common_flags += ['-fsanitize=fuzzer'] - args.lib_fuzzing_engine = '' - - mflags += ['LIB_FUZZING_ENGINE={}'.format(args.lib_fuzzing_engine)] - - if args.sanitize_recover: - recover_flags = ['-fsanitize-recover=all'] - else: - recover_flags = ['-fno-sanitize-recover=all'] - if args.sanitize: - common_flags += recover_flags - - if args.msan: - msan_flags = ['-fsanitize=memory'] - if args.msan_track_origins: - msan_flags += ['-fsanitize-memory-track-origins'] - common_flags += msan_flags - # Append extra MSAN flags (it might require special setup) - cppflags += [args.msan_extra_cppflags] - cflags += [args.msan_extra_cflags] - cxxflags += [args.msan_extra_cxxflags] - ldflags += [args.msan_extra_ldflags] - - if args.asan: - common_flags += ['-fsanitize=address'] - - if args.ubsan: - ubsan_flags = ['-fsanitize=undefined'] - if not args.ubsan_pointer_overflow: - ubsan_flags += overflow_ubsan_flags(cc, cxx) - common_flags += ubsan_flags - - if args.stateful_fuzzing: - cppflags += ['-DSTATEFUL_FUZZING'] - - if args.fuzzing_mode: - cppflags += ['-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION'] - - if args.lib_fuzzing_engine == 'libregression.a': - targets = ['libregression.a'] + targets - - # Append the common flags - cflags += common_flags - cxxflags += common_flags - - # Prepare the flags for Make - cc_str = "CC={}".format(cc) - cxx_str = "CXX={}".format(cxx) - cppflags_str = "CPPFLAGS={}".format(' '.join(cppflags)) - cflags_str = "CFLAGS={}".format(' '.join(cflags)) - cxxflags_str = "CXXFLAGS={}".format(' '.join(cxxflags)) - ldflags_str = "LDFLAGS={}".format(' '.join(ldflags)) - - # Print the flags - print('MFLAGS={}'.format(' '.join(mflags))) - print(cc_str) - print(cxx_str) - print(cppflags_str) - print(cflags_str) - print(cxxflags_str) - print(ldflags_str) - - # Clean and build - clean_cmd = ['make', 'clean'] + mflags - print(' '.join(clean_cmd)) - subprocess.check_call(clean_cmd) - build_cmd = [ - 'make', - cc_str, - cxx_str, - cppflags_str, - cflags_str, - cxxflags_str, - ldflags_str, - ] + mflags + targets - print(' '.join(build_cmd)) - subprocess.check_call(build_cmd) - return 0 - - -def libfuzzer_parser(args): - description = """ - Runs a libfuzzer binary. - Passes all extra arguments to libfuzzer. - The fuzzer should have been build with LIB_FUZZING_ENGINE pointing to - libFuzzer.a. - Generates output in the CORPORA directory, puts crashes in the ARTIFACT - directory, and takes extra input from the SEED directory. - To merge AFL's output pass the SEED as AFL's output directory and pass - '-merge=1'. - """ - parser = argparse.ArgumentParser(prog=args.pop(0), description=description) - parser.add_argument( - '--corpora', - type=str, - help='Override the default corpora dir (default: {})'.format( - abs_join(CORPORA_DIR, 'TARGET'))) - parser.add_argument( - '--artifact', - type=str, - help='Override the default artifact dir (default: {})'.format( - abs_join(CORPORA_DIR, 'TARGET-crash'))) - parser.add_argument( - '--seed', - type=str, - help='Override the default seed dir (default: {})'.format( - abs_join(CORPORA_DIR, 'TARGET-seed'))) - parser.add_argument( - 'TARGET', - type=str, - help='Fuzz target(s) to build {{{}}}'.format(', '.join(TARGETS))) - args, extra = parser.parse_known_args(args) - args.extra = extra - - if args.TARGET and args.TARGET not in TARGETS: - raise RuntimeError('{} is not a valid target'.format(args.TARGET)) - - return args - - -def libfuzzer(target, corpora=None, artifact=None, seed=None, extra_args=None): - if corpora is None: - corpora = abs_join(CORPORA_DIR, target) - if artifact is None: - artifact = abs_join(CORPORA_DIR, '{}-crash'.format(target)) - if seed is None: - seed = abs_join(CORPORA_DIR, '{}-seed'.format(target)) - if extra_args is None: - extra_args = [] - - target = abs_join(FUZZ_DIR, target) - - corpora = [create(corpora)] - artifact = create(artifact) - seed = check(seed) - - corpora += [artifact] - if seed is not None: - corpora += [seed] - - cmd = [target, '-artifact_prefix={}/'.format(artifact)] - cmd += corpora + extra_args - print(' '.join(cmd)) - subprocess.check_call(cmd) - - -def libfuzzer_cmd(args): - try: - args = libfuzzer_parser(args) - except Exception as e: - print(e) - return 1 - libfuzzer(args.TARGET, args.corpora, args.artifact, args.seed, args.extra) - return 0 - - -def afl_parser(args): - description = """ - Runs an afl-fuzz job. - Passes all extra arguments to afl-fuzz. - The fuzzer should have been built with CC/CXX set to the AFL compilers, - and with LIB_FUZZING_ENGINE='libregression.a'. - Takes input from CORPORA and writes output to OUTPUT. - Uses AFL_FUZZ as the binary (set from flag or environment variable). - """ - parser = argparse.ArgumentParser(prog=args.pop(0), description=description) - parser.add_argument( - '--corpora', - type=str, - help='Override the default corpora dir (default: {})'.format( - abs_join(CORPORA_DIR, 'TARGET'))) - parser.add_argument( - '--output', - type=str, - help='Override the default AFL output dir (default: {})'.format( - abs_join(CORPORA_DIR, 'TARGET-afl'))) - parser.add_argument( - '--afl-fuzz', - type=str, - default=AFL_FUZZ, - help='AFL_FUZZ (default: $AFL_FUZZ={})'.format(AFL_FUZZ)) - parser.add_argument( - 'TARGET', - type=str, - help='Fuzz target(s) to build {{{}}}'.format(', '.join(TARGETS))) - args, extra = parser.parse_known_args(args) - args.extra = extra - - if args.TARGET and args.TARGET not in TARGETS: - raise RuntimeError('{} is not a valid target'.format(args.TARGET)) - - if not args.corpora: - args.corpora = abs_join(CORPORA_DIR, args.TARGET) - if not args.output: - args.output = abs_join(CORPORA_DIR, '{}-afl'.format(args.TARGET)) - - return args - - -def afl(args): - try: - args = afl_parser(args) - except Exception as e: - print(e) - return 1 - target = abs_join(FUZZ_DIR, args.TARGET) - - corpora = create(args.corpora) - output = create(args.output) - - cmd = [args.afl_fuzz, '-i', corpora, '-o', output] + args.extra - cmd += [target, '@@'] - print(' '.join(cmd)) - subprocess.call(cmd) - return 0 - - -def regression(args): - try: - description = """ - Runs one or more regression tests. - The fuzzer should have been built with with - LIB_FUZZING_ENGINE='libregression.a'. - Takes input from CORPORA. - """ - args = targets_parser(args, description) - except Exception as e: - print(e) - return 1 - for target in args.TARGET: - corpora = create(abs_join(CORPORA_DIR, target)) - target = abs_join(FUZZ_DIR, target) - cmd = [target, corpora] - print(' '.join(cmd)) - subprocess.check_call(cmd) - return 0 - - -def gen_parser(args): - description = """ - Generate a seed corpus appropriate for TARGET with data generated with - decodecorpus. - The fuzz inputs are prepended with a seed before the zstd data, so the - output of decodecorpus shouldn't be used directly. - Generates NUMBER samples prepended with FUZZ_RNG_SEED_SIZE random bytes and - puts the output in SEED. - DECODECORPUS is the decodecorpus binary, and must already be built. - """ - parser = argparse.ArgumentParser(prog=args.pop(0), description=description) - parser.add_argument( - '--number', - '-n', - type=int, - default=100, - help='Number of samples to generate') - parser.add_argument( - '--max-size-log', - type=int, - default=18, - help='Maximum sample size to generate') - parser.add_argument( - '--seed', - type=str, - help='Override the default seed dir (default: {})'.format( - abs_join(CORPORA_DIR, 'TARGET-seed'))) - parser.add_argument( - '--decodecorpus', - type=str, - default=DECODECORPUS, - help="decodecorpus binary (default: $DECODECORPUS='{}')".format( - DECODECORPUS)) - parser.add_argument( - '--zstd', - type=str, - default=ZSTD, - help="zstd binary (default: $ZSTD='{}')".format(ZSTD)) - parser.add_argument( - '--fuzz-rng-seed-size', - type=int, - default=4, - help="FUZZ_RNG_SEED_SIZE used for generate the samples (must match)" - ) - parser.add_argument( - 'TARGET', - type=str, - help='Fuzz target(s) to build {{{}}}'.format(', '.join(TARGETS))) - args, extra = parser.parse_known_args(args) - args.extra = extra - - if args.TARGET and args.TARGET not in TARGETS: - raise RuntimeError('{} is not a valid target'.format(args.TARGET)) - - if not args.seed: - args.seed = abs_join(CORPORA_DIR, '{}-seed'.format(args.TARGET)) - - if not os.path.isfile(args.decodecorpus): - raise RuntimeError("{} is not a file run 'make -C {} decodecorpus'". - format(args.decodecorpus, abs_join(FUZZ_DIR, '..'))) - - return args - - -def gen(args): - try: - args = gen_parser(args) - except Exception as e: - print(e) - return 1 - - seed = create(args.seed) - with tmpdir() as compressed, tmpdir() as decompressed, tmpdir() as dict: - info = TARGET_INFO[args.TARGET] - - if info.input_type == InputType.DICTIONARY_DATA: - number = max(args.number, 1000) - else: - number = args.number - cmd = [ - args.decodecorpus, - '-n{}'.format(args.number), - '-p{}/'.format(compressed), - '-o{}'.format(decompressed), - ] - - if info.frame_type == FrameType.BLOCK: - cmd += [ - '--gen-blocks', - '--max-block-size-log={}'.format(min(args.max_size_log, 17)) - ] - else: - cmd += ['--max-content-size-log={}'.format(args.max_size_log)] - - print(' '.join(cmd)) - subprocess.check_call(cmd) - - if info.input_type == InputType.RAW_DATA: - print('using decompressed data in {}'.format(decompressed)) - samples = decompressed - elif info.input_type == InputType.COMPRESSED_DATA: - print('using compressed data in {}'.format(compressed)) - samples = compressed - else: - assert info.input_type == InputType.DICTIONARY_DATA - print('making dictionary data from {}'.format(decompressed)) - samples = dict - min_dict_size_log = 9 - max_dict_size_log = max(min_dict_size_log + 1, args.max_size_log) - for dict_size_log in range(min_dict_size_log, max_dict_size_log): - dict_size = 1 << dict_size_log - cmd = [ - args.zstd, - '--train', - '-r', decompressed, - '--maxdict={}'.format(dict_size), - '-o', abs_join(dict, '{}.zstd-dict'.format(dict_size)) - ] - print(' '.join(cmd)) - subprocess.check_call(cmd) - - # Copy the samples over and prepend the RNG seeds - for name in os.listdir(samples): - samplename = abs_join(samples, name) - outname = abs_join(seed, name) - with open(samplename, 'rb') as sample: - with open(outname, 'wb') as out: - CHUNK_SIZE = 131072 - chunk = sample.read(CHUNK_SIZE) - while len(chunk) > 0: - out.write(chunk) - chunk = sample.read(CHUNK_SIZE) - return 0 - - -def minimize(args): - try: - description = """ - Runs a libfuzzer fuzzer with -merge=1 to build a minimal corpus in - TARGET_seed_corpus. All extra args are passed to libfuzzer. - """ - args = targets_parser(args, description) - except Exception as e: - print(e) - return 1 - - for target in args.TARGET: - # Merge the corpus + anything else into the seed_corpus - corpus = abs_join(CORPORA_DIR, target) - seed_corpus = abs_join(CORPORA_DIR, "{}_seed_corpus".format(target)) - extra_args = [corpus, "-merge=1"] + args.extra - libfuzzer(target, corpora=seed_corpus, extra_args=extra_args) - seeds = set(os.listdir(seed_corpus)) - # Copy all crashes directly into the seed_corpus if not already present - crashes = abs_join(CORPORA_DIR, '{}-crash'.format(target)) - for crash in os.listdir(crashes): - if crash not in seeds: - shutil.copy(abs_join(crashes, crash), seed_corpus) - seeds.add(crash) - - -def zip_cmd(args): - try: - description = """ - Zips up the seed corpus. - """ - args = targets_parser(args, description) - except Exception as e: - print(e) - return 1 - - for target in args.TARGET: - # Zip the seed_corpus - seed_corpus = abs_join(CORPORA_DIR, "{}_seed_corpus".format(target)) - zip_file = "{}.zip".format(seed_corpus) - cmd = ["zip", "-r", "-q", "-j", "-9", zip_file, "."] - print(' '.join(cmd)) - subprocess.check_call(cmd, cwd=seed_corpus) - - -def list_cmd(args): - print("\n".join(TARGETS)) - - -def short_help(args): - name = args[0] - print("Usage: {} [OPTIONS] COMMAND [ARGS]...\n".format(name)) - - -def help(args): - short_help(args) - print("\tfuzzing helpers (select a command and pass -h for help)\n") - print("Options:") - print("\t-h, --help\tPrint this message") - print("") - print("Commands:") - print("\tbuild\t\tBuild a fuzzer") - print("\tlibfuzzer\tRun a libFuzzer fuzzer") - print("\tafl\t\tRun an AFL fuzzer") - print("\tregression\tRun a regression test") - print("\tgen\t\tGenerate a seed corpus for a fuzzer") - print("\tminimize\tMinimize the test corpora") - print("\tzip\t\tZip the minimized corpora up") - print("\tlist\t\tList the available targets") - - -def main(): - args = sys.argv - if len(args) < 2: - help(args) - return 1 - if args[1] == '-h' or args[1] == '--help' or args[1] == '-H': - help(args) - return 1 - command = args.pop(1) - args[0] = "{} {}".format(args[0], command) - if command == "build": - return build(args) - if command == "libfuzzer": - return libfuzzer_cmd(args) - if command == "regression": - return regression(args) - if command == "afl": - return afl(args) - if command == "gen": - return gen(args) - if command == "minimize": - return minimize(args) - if command == "zip": - return zip_cmd(args) - if command == "list": - return list_cmd(args) - short_help(args) - print("Error: No such command {} (pass -h for help)".format(command)) - return 1 - - -if __name__ == "__main__": - sys.exit(main()) |