docs/fuzzing

Fuzzing

We use fuzzing to test the various entrypoints to the library. The fuzzer we use is AFL++. All files related to fuzzing live within the fuzz directory, which has the following structure:

fuzz
├── corpus
│   ├── parse             fuzzing corpus for parsing (a symlink to our fixtures)
│   ├── regexp            fuzzing corpus for regexp
│   └── unescape          fuzzing corpus for unescaping strings
├── dict                  a AFL++ dictionary containing various tokens
├── docker
│   └── Dockerfile        for building a container with the fuzzer toolchain
├── fuzz.c                generic entrypoint for fuzzing
├── heisenbug.c           entrypoint for reproducing a crash or hang
├── parse.c               fuzz handler for parsing
├── parse.sh              script to run parsing fuzzer
├── regexp.c              fuzz handler for regular expression parsing
├── regexp.sh             script to run regexp fuzzer
├── tools
│   ├── backtrace.sh      generates backtrace files for a crash directory
│   └── minimize.sh       generates minimized crash or hang files
├── unescape.c            fuzz handler for unescape functionality
└── unescape.sh           script to run unescape fuzzer

Usage

There are currently three fuzzing targets

  • yp_parse_serialize (parse)
  • yp_regexp_named_capture_group_names (regexp)
  • yp_unescape_manipulate_string (unescape)

Respectively, fuzzing can be performed with

make fuzz-run-parse
make fuzz-run-regexp
make fuzz-run-unescape

To end a fuzzing job, interrupt with CTRL+C. To enter a container with the fuzzing toolchain and debug utilities, run

make fuzz-debug

Out-of-bounds reads

Currently, encoding functionality implementing the yp_encoding_t interface can read outside of inputs. For the time being, ASAN instrumentation is disabled for functions from src/enc. See fuzz/asan.ignore.

To disable ASAN read instrumentation globally, use the FUZZ_FLAGS environment variable e.g.

FUZZ_FLAGS="-mllvm -asan-instrument-reads=false" make fuzz-run-parse

Note, that this may make reproducing bugs difficult as they may depend on memory outside of the input buffer. In that case, try

make fuzz-debug # enter the docker container with build tools
make build/fuzz.heisenbug.parse # or .unescape or .regexp
./build/fuzz.heisenbug.parse path-to-problem-input

Triaging Crashes and Hangs

Triaging crashes and hangs is easier when the inputs are as short as possible. In the fuzz container, an entire crash or hang directory can be minimized using

./fuzz/tools/minimize.sh 

e.g.

./fuzz/tools/minimize.sh fuzz/output/parse/default/crashes

This may take a long time. In the crash/hang directory, for each input file there will appear a minimized version with the extension .min appended.

Backtraces for crashes (not hangs) can be generated en masse with

./fuzz/tools/backtrace.sh 

Files with basename equal to the input file name with extension .bt will be created e.g.

id:000000,sig:06,src:000006+000190,time:8480,execs:18929,op:splice,rep:4
id:000000,sig:06,src:000006+000190,time:8480,execs:18929,op:splice,rep:4.bt