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.