NAME
docs/dev/fuzzing.pod - Fuzzing Parrot with afl - the american fuzzy lop
DESCRIPTION
This document describes how to find parrot crashes on invalid input with the american fuzzy lop.
This will not work on MS Windows. It is tested on x86_64 linux, but should also work on darwin and other POSIX intel systems.
Setup afl
Download afl from http://lcamtuf.coredump.cx/afl/
Make and install afl.
cd ~/ wget http://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz tar xfz afl-latest.tgz cd afl-<TAB> # be sure to unset ASAN_OPTIONS grep ASAN | env unset ASAN_OPTIONS export ASAN_OPTIONS make sudo make install
Compile parrot with the instrumenting afl-gcc
:
cd <your-parrot> perl Configure.pl --cc=afl-gcc --ld=afl-gcc --link=afl-gcc make -s -j4
Read the afl documentation
See http://lcamtuf.coredump.cx/afl/ and under afl-VERSION/docs. And maybe also https://en.wikipedia.org/wiki/Fuzz_testing
Some notes:
afl can work with clang
, but gcc
is preferred.
afl can work with ASAN or MSAN, but since parrot is too slow it is advised to run it without. Our coverage with the normal testsuite is pretty good, so you better smoke the normal testsuite with ASAN
or MSAN
or VALGRIND=valgrind
instead.
afl tries to create bad input files, to crash or hang the process. Bad and uncontrolled input may impact the stability of your testmachine. E.g. it might randomly overwrite files or memory or fill up your logs or harddisc space. Use a fast kvm VM or docker. Only one core is needed if you don't fuzz in parallel.
parrot_old
has faster startup, but does not play nice with the afl forkserver. You can use it with -C
, the crash mode though. Get a testcase which crashes and find more crashes. This way I found https://github.com/parrot/parrot/issues/1168
With perl5
or potion
I got exec speeds of 1500/sec
, with parrot_old
150/sec
, with parrot
100/sec
. This is very slow. So strip your input testcases to the absolute minimum. An afl-fuzz run usually lasts a day or more, but at least a few hours. I did not dare yet to fuzz nqp
or perl6
yet, as it would be even slower.
Set your terminal screen to black background, seriously. You hardly see anything on white.
imcc Parser
You can fuzz .pir and .pasm input to check the imcc parser.
mkdir afl-out afl-testcase cp t/op/literal.t testcase/ afl-fuzz -m4000 -o afl-out -i afl-testcase -- parrot -r @@
With keywords
Adding our regular pir and pasm keywords helps creating more meaningful permutations. One file in a new directory per keyword.
mkdir -p afl-out2 afl-testcase2/keywords perl -e'`echo $_ > afl-testcase2/keywords/$_` for qw(.sym .arg prototyped non_prototyped .class .endclass .param inc dec new defined global clone .call .result .return .local .const .globalconst end goto if unless call branch jump jsr ret invoke invokecc throw rethrow die_hard .emit .eom .sub .end .begin_call .end_call .pcc_sub .begin_return .end_return .begin_yield .end_yield .loadlib .namespace .endnamespace .macro .include int float string pmc ne eq le lt ge gt == =head1 =end =pod)' afl-fuzz -m4000 -i afl-testcase2 -x afl-testcase2/keywords -o afl-out2 -- parrot -r @@
pbc packfiles
Fuzz binary input to check the PackFile reader. Binary files are the typical usecase for afl. See https://github.com/parrot/parrot/issues/1169
mkdir -p afl-out3 afl-testcase3 cp t/native_pbc/*_8_le.pbc afl-testcase3/ afl-fuzz -m4000 -i afl-testcase3 -o afl-out3 -- parrot -r @@
During fuzzing
You can examine the results while running the fuzzer, and you can even fix the found error, recompile, Ctrl-C the fuzzer and resume it with -i-
, i.e. afl-fuzz -m4000 -i- -o afl-out3 -- parrot -r @@
It will reinstrument the existing paths and cases, and save away the old results.
Crosscheck the results for false positives
afl-fuzz
does sometimes categorize all exit codes > 0 as crash, while it should do only do it with > 128. So you need to verify the found crashes, like this:
for c in afl-out/crashes/*; do ./parrot $c >/dev/null 2>&1 || echo $? $c; done
and check only the exit codes > 128.
Further investigations - crash explorer
If you found a new crash, try out the crash explorer mode -C
by copying this crash result as new testcase and start a new afl-fuzz with -C
. This will find other variations of the crash faster than in the normal mode. See http://lcamtuf.blogspot.de/2014/11/afl-fuzz-crash-exploration-mode.html
Other tools
There exist also less intelligent blackbox fuzzers, like csmith.
And more intelligent whitebox fuzzers, which do taint symbolic input and use constraint solvers to track fishy source paths back to the input, e.g. mayhem, cbmc or the mythical smart Microsoft fuzzer (https://en.wikipedia.org/wiki/Fuzz_testing#cite_ref-AutoDO-14_16-0).
However afl is usually the best option for now. It is fast, simple and good enough.
AUTHOR
Reini Urban <rurban@cpan.org>