xref: /openbmc/libpldm/docs/fuzzing.md (revision 412fa270c3999a38a911e84088a6b7a8ccf59121)
1# Fuzzing libpldm
2
3## Firmware FD Responder
4
5`tests/fuzz/fd-fuzz.cpp` exercises the FD responder implementation. It can run
6with various fuzzing engines - either AFL++, honggfuzz, or libfuzzer.
7
8Each fuzz corpus input is split into two parts. The first 1024 bytes is a
9"control" stream which used to randomise certain events in the fuzzer, such as
10returning failure from callbacks, or choosing whether to receive a message or
11run progress.
12
13The remainder of the fuzz input is taken as an stream of `length:data` PLDM
14packet contents, as passed to `pldm_fd_handle_msg()`.
15
16## Build
17
18From the top level libpldm directory, run `./tests/fuzz/fuzz-build.py`. That
19will produce several build variants required for different fuzz engines/stages.
20
21## Honggfuzz
22
23[Honggfuzz](https://github.com/google/honggfuzz) handles running across multiple
24threads itself with a single corpus directory, which is easy to work with. It
25needs to be built from source.
26
27Run with
28
29```shell
30nice honggfuzz -i corpusdir --linux_perf_branch --dict tests/fuzz/fd.dict  -- ./bhf/tests/fuzz/fd-fuzz
31```
32
33The `--linux_perf_branch` switch is optional, it requires permissions for perf
34counters:
35
36```shell
37echo 0 | sudo tee /proc/sys/kernel/perf_event_paranoid
38```
39
40Optionally a thread count can be given, 24 threads on a 12 core system seems to
41give best utilisation (`--nthreads 24`).
42
43The corpus directory can be reused between runs with different fuzzers. For a
44totally fresh start, copy in `tests/fuzz/fd-fuzz-input1.dat`, a sample
45handcrafted input.
46
47## AFL++
48
49Running a single instance (just for testing):
50
51```shell
52afl-fuzz -i fuzzrun/hf11/ -o fuzzrun/out12single ./bfuzz/tests/fuzz/fd-fuzz
53```
54
55AFL++ requires a separate GUI instantiation for each CPU thread. The helper
56[AFL Runner](https://github.com/0xricksanchez/afl_runner) makes that easier.
57
58Running with 20 threads:
59
60```shell
61nice aflr run  -t bfuzz/tests/fuzz/fd-fuzz -i workdir/out5/m_fd-fuzz/queue -o workdir/out6 -c bcmplog/tests/fuzz/fd-fuzz -s bfuzzasan/tests/fuzz/fd-fuzz -n 20 -x tests/fuzz/fd.dict --session-name fuzz
62```
63
64Kill it with `aflr kill fuzz`.
65
66`aflr tui workdir/out6` could be used to view progress, though its calculations
67may be inaccurate if some runners are idle. Another option is
68`afl-whatsup workdir/out6`.
69
70## Coverage
71
72The coverage provided by a corpus directory can be reported using
73`tests/fuzz/fuzz-coverage.py`.
74
75It will:
76
77- Run a binary compiled with `--coverage` against each corpus file
78- Use [grcov](https://github.com/mozilla/grcov) to aggregate the coverage traces
79  (much faster than lcov).
80- Use `genhtml` to create a report
81
82Typical usage, with corpus in `fuzzrun/corpus`:
83
84```shell
85./tests/fuzz/fuzz-coverage.py fuzzrun/corpus bnoopt/tests/fuzz/fd-fuzz . bnoopt/ coverage-output
86```
87
88## Reproducing crashes
89
90When the fuzz run encounters a crash, the testcase can be run against the built
91target manually, and stepped through with GDB etc.
92
93```shell
94env TRACEFWFD=1 ./bnoopt/tests/fuzz/fd-fuzz < crashing.bin
95```
96
97The `printf`s are disabled by default to improve normal fuzzing speed.
98