Fuzzing libslirp state and instructions

Current state

We chose to use libFuzzer because of its custom mutator feature, which allows to keep coherent informations inside the packets being sent to libslirp. This ease the process of fuzzing as packets are less likely to be rejected early during processing them.

In the current state, the meson.build file is not compatible with the original one used by libSlirp main repository but it should be easy to merge them in a clean way.

Six harness are currently available, more are to be added later to focus on other parts of the code :

  • fuzz-ip-header : the mutator focuses on the ip header field informations,
  • fuzz-udp : the mutator only work on udp packets, mutating the udp header and content, or only one or the other (-h,-d),
  • fuzz-tcp : the mutator targets tcp packets, header+data or only one or the other, or only one or the other (-h,-d),
  • fuzz-icmp : the mutator focuses on icmp packets,

These harness should be good starting examples on how to fuzz libslirp using libFuzzer.

Running the fuzzer

Building the fuzzers/harness requires the use of clang as libFuzzer is part of LLVM. You can build it running :

CC=clang meson build && ninja -C build

It will build the fuzzer in the ./build/fuzzing/ directory.

A script named fuzzing/coverage.py is available to generate coverage informations. It makes a lot of assumptions on the directory structure and should be read before use.

To run the fuzzer, simply run some of:

  • build/fuzzing/fuzz-ip-header fuzzing/IN_ip-header
  • build/fuzzing/fuzz-udp fuzzing/IN_udp
  • build/fuzzing/fuzz-udp-h fuzzing/IN_udp-h
  • build/fuzzing/fuzz-tftp fuzzing/IN_tftp
  • build/fuzzing/fuzz-dhcp fuzzing/IN_dhcp
  • build/fuzzing/fuzz-icmp fuzzing/IN_icmp
  • build/fuzzing/fuzz-tcp fuzzing/IN_tcp

Your current directory should be a separate directory as crashes to it. New inputs found by the fuzzer will go directly in the IN folder.

Adding new files to the corpus

In its current state, the fuzzing code is taking pcap files as input, we produced some using tcpdump on linux inside qemu with default settings. Those files should be captured using the EN10MB (Ethernet) data link type, this can be set with the flag -y but it seems this can't be done while listening on all interfaces (-i any). New files should give new coverage, to ensure a new file is usefull the coverage.py script (see next section) can be used to compare the coverage with and without that new file.

Coverage

The coverage.py script allows to see coverage informations about the corpus. It makes a lot of assumptions on the directory structure so it should be read and probably modified before running it. It must be called with the protocol to cover: python coverage.py udp report. To generate coverage informations, the following flags are passed to the fuzzer and libslirp :

  • g
  • fsanitize-coverage=edge,indirect-calls,trace-cmp
  • fprofile-instr-generate
  • fcoverage-mapping

The last 2 arguments should also be passed to the linker.

Then the llvm-profdata and llvm-cov tools can be used to generate a report and a fancy set of HTML files with line-coverage informations.