[tor-dev] Fuzzing tor_inet_pton(): A fuzzing example
nickm at torproject.org
Wed Jul 5 19:19:43 UTC 2017
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:
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);
log_info(LD_GENERAL, "%d %d", in_ok, in6_ok);
* 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:
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
Then I re-built Tor for use with AFL:
./configure CC=afl-gcc --enable-fragile-hardening
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 \
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
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.
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
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
Then I made a new file for the libfuzzer results, and ran libfuzzer
using the original corpus and the output from AFL:
./src/test/fuzz/lf-fuzz-pton pton-output-libfuzzer/ \
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
More information about the tor-dev