1 1.1 christos # Fuzzing 2 1.1 christos 3 1.1 christos Each fuzzing target can be built with multiple engines. 4 1.1 christos Zstd provides a fuzz corpus for each target that can be downloaded with 5 1.1 christos the command: 6 1.1 christos 7 1.1 christos ``` 8 1.1 christos make corpora 9 1.1 christos ``` 10 1.1 christos 11 1.1 christos It will download each corpus into `./corpora/TARGET`. 12 1.1 christos 13 1.1 christos ## fuzz.py 14 1.1 christos 15 1.1 christos `fuzz.py` is a helper script for building and running fuzzers. 16 1.1 christos Run `./fuzz.py -h` for the commands and run `./fuzz.py COMMAND -h` for 17 1.1 christos command specific help. 18 1.1 christos 19 1.1 christos ### Generating Data 20 1.1 christos 21 1.1 christos `fuzz.py` provides a utility to generate seed data for each fuzzer. 22 1.1 christos 23 1.1 christos ``` 24 1.1 christos make -C ../tests decodecorpus 25 1.1 christos ./fuzz.py gen TARGET 26 1.1 christos ``` 27 1.1 christos 28 1.1 christos By default it outputs 100 samples, each at most 8KB into `corpora/TARGET-seed`, 29 1.1 christos but that can be configured with the `--number`, `--max-size-log` and `--seed` 30 1.1 christos flags. 31 1.1 christos 32 1.1 christos ### Build 33 1.1 christos It respects the usual build environment variables `CC`, `CFLAGS`, etc. 34 1.1 christos The environment variables can be overridden with the corresponding flags 35 1.1 christos `--cc`, `--cflags`, etc. 36 1.1 christos The specific fuzzing engine is selected with `LIB_FUZZING_ENGINE` or 37 1.1 christos `--lib-fuzzing-engine`, the default is `libregression.a`. 38 1.1 christos Alternatively, you can use Clang's built in fuzzing engine with 39 1.1 christos `--enable-fuzzer`. 40 1.1 christos It has flags that can easily set up sanitizers `--enable-{a,ub,m}san`, and 41 1.1 christos coverage instrumentation `--enable-coverage`. 42 1.1 christos It sets sane defaults which can be overridden with flags `--debug`, 43 1.1 christos `--enable-ubsan-pointer-overflow`, etc. 44 1.1 christos Run `./fuzz.py build -h` for help. 45 1.1 christos 46 1.1 christos ### Running Fuzzers 47 1.1 christos 48 1.1 christos `./fuzz.py` can run `libfuzzer`, `afl`, and `regression` tests. 49 1.1 christos See the help of the relevant command for options. 50 1.1 christos Flags not parsed by `fuzz.py` are passed to the fuzzing engine. 51 1.1 christos The command used to run the fuzzer is printed for debugging. 52 1.1 christos 53 1.1 christos Here's a helpful command to fuzz each target across all cores, 54 1.1 christos stopping only if a bug is found: 55 1.1 christos ``` 56 1.1 christos for target in $(./fuzz.py list); do 57 1.1 christos ./fuzz.py libfuzzer $target -jobs=10 -workers=10 -max_total_time=1000 || break; 58 1.1 christos done 59 1.1 christos ``` 60 1.1 christos Alternatively, you can fuzz all targets in parallel, using one core per target: 61 1.1 christos ``` 62 1.1 christos python3 ./fuzz.py list | xargs -P$(python3 ./fuzz.py list | wc -l) -I__ sh -c "python3 ./fuzz.py libfuzzer __ 2>&1 | tee __.log" 63 1.1 christos ``` 64 1.1 christos Either way, to double-check that no crashes were found, run `ls corpora/*crash`. 65 1.1 christos If any crashes were found, you can use the hashes to reproduce them. 66 1.1 christos 67 1.1 christos ## LibFuzzer 68 1.1 christos 69 1.1 christos ``` 70 1.1 christos # Build the fuzz targets 71 1.1 christos ./fuzz.py build all --enable-fuzzer --enable-asan --enable-ubsan --cc clang --cxx clang++ 72 1.1 christos # OR equivalently 73 1.1 christos CC=clang CXX=clang++ ./fuzz.py build all --enable-fuzzer --enable-asan --enable-ubsan 74 1.1 christos # Run the fuzzer 75 1.1 christos ./fuzz.py libfuzzer TARGET <libfuzzer args like -jobs=4> 76 1.1 christos ``` 77 1.1 christos 78 1.1 christos where `TARGET` could be `simple_decompress`, `stream_round_trip`, etc. 79 1.1 christos 80 1.1 christos ### MSAN 81 1.1 christos 82 1.1 christos Fuzzing with `libFuzzer` and `MSAN` is as easy as: 83 1.1 christos 84 1.1 christos ``` 85 1.1 christos CC=clang CXX=clang++ ./fuzz.py build all --enable-fuzzer --enable-msan 86 1.1 christos ./fuzz.py libfuzzer TARGET <libfuzzer args> 87 1.1 christos ``` 88 1.1 christos 89 1.1 christos `fuzz.py` respects the environment variables / flags `MSAN_EXTRA_CPPFLAGS`, 90 1.1 christos `MSAN_EXTRA_CFLAGS`, `MSAN_EXTRA_CXXFLAGS`, `MSAN_EXTRA_LDFLAGS` to easily pass 91 1.1 christos the extra parameters only for MSAN. 92 1.1 christos 93 1.1 christos ## AFL 94 1.1 christos 95 1.1 christos The default `LIB_FUZZING_ENGINE` is `libregression.a`, which produces a binary 96 1.1 christos that AFL can use. 97 1.1 christos 98 1.1 christos ``` 99 1.1 christos # Build the fuzz targets 100 1.1 christos CC=afl-clang CXX=afl-clang++ ./fuzz.py build all --enable-asan --enable-ubsan 101 1.1 christos # Run the fuzzer without a memory limit because of ASAN 102 1.1 christos ./fuzz.py afl TARGET -m none 103 1.1 christos ``` 104 1.1 christos 105 1.1 christos ## Regression Testing 106 1.1 christos 107 1.1 christos The regression test supports the `all` target to run all the fuzzers in one 108 1.1 christos command. 109 1.1 christos 110 1.1 christos ``` 111 1.1 christos CC=clang CXX=clang++ ./fuzz.py build all --enable-asan --enable-ubsan 112 1.1 christos ./fuzz.py regression all 113 1.1 christos CC=clang CXX=clang++ ./fuzz.py build all --enable-msan 114 1.1 christos ./fuzz.py regression all 115 1.1 christos ``` 116 1.1 christos 117 1.1 christos ## Fuzzing a custom sequence producer plugin 118 1.1 christos Sequence producer plugin authors can use the zstd fuzzers to stress-test their code. 119 1.1 christos See the documentation in `fuzz_third_party_seq_prod.h` for details. 120 1.1 christos 121 1.1 christos ## Adding a new fuzzer 122 1.1 christos There are several steps involved in adding a new fuzzer harness. 123 1.1 christos 124 1.1 christos ### Build your harness 125 1.1 christos 1. Create a new your fuzzer harness `tests/fuzz/your_harness.c`. 126 1.1 christos 127 1.1 christos 2. Add your harness to the Makefile 128 1.1 christos 129 1.1 christos 2.1 Follow [this example](https://github.com/facebook/zstd/blob/e124e39301381de8f323436a3e4c46539747ba24/tests/fuzz/Makefile#L216) if your fuzzer requires both compression and decompression symbols (prefix `rt_`). If your fuzzer only requires decompression symbols, follow [this example](https://github.com/facebook/zstd/blob/6a0052a409e2604bd40354b76b86272b712edd7d/tests/fuzz/Makefile#L194) (prefix `d_`). 130 1.1 christos 131 1.1 christos 2.2 Add your target to [`FUZZ_TARGETS`](https://github.com/facebook/zstd/blob/6a0052a409e2604bd40354b76b86272b712edd7d/tests/fuzz/Makefile#L108). 132 1.1 christos 133 1.1 christos 3. Add your harness to [`fuzz.py`](https://github.com/facebook/zstd/blob/6a0052a409e2604bd40354b76b86272b712edd7d/tests/fuzz/fuzz.py#L48). 134 1.1 christos 135 1.1 christos ### Generate seed data 136 1.1 christos Follow the instructions above to generate seed data: 137 1.1 christos ``` 138 1.1 christos make -C ../tests decodecorpus 139 1.1 christos ./fuzz.py gen your_harness 140 1.1 christos ``` 141 1.1 christos 142 1.1 christos ### Run the harness 143 1.1 christos Follow the instructions above to run your harness and fix any crashes: 144 1.1 christos ``` 145 1.1 christos ./fuzz.py build your_harness --enable-fuzzer --enable-asan --enable-ubsan --cc clang --cxx clang++ 146 1.1 christos ./fuzz.py libfuzzer your_harness 147 1.1 christos ``` 148 1.1 christos 149 1.1 christos ### Minimize and zip the corpus 150 1.1 christos After running the fuzzer for a while, you will have a large corpus at `tests/fuzz/corpora/your_harness*`. 151 1.1 christos This corpus must be minimized and zipped before uploading to GitHub for regression testing: 152 1.1 christos ``` 153 1.1 christos ./fuzz.py minimize your_harness 154 1.1 christos ./fuzz.py zip your_harness 155 1.1 christos ``` 156 1.1 christos 157 1.1 christos ### Upload the zip file to GitHub 158 1.1 christos The previous step should produce a `.zip` file containing the corpus for your new harness. 159 1.1 christos This corpus must be uploaded to GitHub here: https://github.com/facebook/zstd/releases/tag/fuzz-corpora 160 1.1 christos 161 1.1 christos 162