So, there was a bug report of an assertion failure in tor_inet_pton(), and I wanted to track it down. Here's my reconstruction of my process, in case it helps anybody else write fuzzing code.
First, I wrote a new fuzzing target in src/test/fuzz/fuzz_pton.c:
====== #include "orconfig.h" #include "or.h"
#include "fuzzing.h"
int fuzz_init(void) { return 0; } int fuzz_cleanup(void) { return 0; }
int fuzz_main(const uint8_t *stdin_buf, size_t data_size) { struct in_addr *in = tor_malloc(sizeof(*in)); struct in6_addr *in6 = tor_malloc(sizeof(*in6)); char *cp = tor_memdup_nulterm(stdin_buf, data_size); int in_ok = tor_inet_pton(AF_INET, cp, in); int in6_ok = tor_inet_pton(AF_INET6, cp, in6); tor_free(cp); tor_free(in); tor_free(in6); log_info(LD_GENERAL, "%d %d", in_ok, in6_ok); return 0; } ====== Notes:
* The init and cleanup functions don't have anything to do above, but the test harness in fuzzing_common.c expects to find them. * This fuzzer tries to parse each address twice: first as an IPv4 address, then as an IPv6 address. * The fuzzer puts its outputs into heap-allocated RAM to improve the odds that any bugs will get caught by asan, though this shouldn't really be necessary. * Because tor_inet_pton() takes a nul-terminated string, this fuzzer nul-term inates its input. You shouldn't do that unless you're fuzing a function that requires nul-terminated inputs.
Next, I edited the script in scripts/codegen/fuzzing_include_am.py to add "pton" to the "FUZZERS" list at the top of the file, and I re-ran the script and used its output to replace src/test/fuzz/include.am, so that our build system can know about the new fuzzer.
I knew I would need a corpus of pton examples, so I started one in fuzzing-corpora (fuzzing-corpora is a separate repository). I made a new "pton" directory there, and filled it with 4 or 5 examples of things that looked like they might be addresses. I chose:
127.0.0.1 1.2.3.4 ::1 80f0:9999:ffff::3
Each example went into its own file, without a terminating newline.
I tried the fuzzing corpora examples out with the fuzzing program, to make sure that I got the expected results
$ ./src/test/fuzz/fuzz-pton --info < ../tor-fuzz-corpora/pton/b Jul 05 15:00:56.371 [info] fuzz_main(): 1 0
===== AFL
Then I re-built Tor for use with AFL: ./configure CC=afl-gcc --enable-fragile-hardening make clean AFL_HARDEN=1 make fuzzers
To fuzz it, I started by telling afl-fuzz to run with no memory limit, writing results to pton-output, taking inputs from the corpus:
afl-fuzz -i ../tor-fuzz-corpora/pton -o ./pton-output/ -m none \ -- ./src/test/fuzz/fuzz-pton
AFL wound up complaining about some kernel settings, so I had to do this as root. YMMV. AFL told me to do this:
echo core >/proc/sys/kernel/core_pattern cd /sys/devices/system/cpu echo performance | tee cpu*/cpufreq/scaling_governor
Then AFL started running!
I watched the "total paths" value climb. That's how many different distinct paths through the code that the fuzzer was able to find. I didn't see any "uniq crashes" or "uniq hangs" values, so I knew the fuzzer hadn't found anything.
===== Libfuzzer
After that had run for a day, I stopped it and tried again with libfuzzer:
./configure --enable-libfuzzer CC=/home/nickm/build/llvm-build/bin/clang make clean make fuzzers
Obviously, you'll need to point CC to your own clang here. I wasn't able to use the system clang because libfuzzer needs features I didn't have.
Then I made a new file for the libfuzzer results, and ran libfuzzer using the original corpus and the output from AFL: mkdir pton-output-libfuzzer ./src/test/fuzz/lf-fuzz-pton pton-output-libfuzzer/ \ ./pton-output/queue/ ../tor-fuzz-corpora/pton/
And libfuzzer began giving results. Every "NEW" line was a new path; every "pulse" line meant that time had passed but no new paths were found.