afl-fuzz gives you great fuzzer coverage for very little investment of development time. Its primary target platform is Linux on x86 / x86-64, however since the introduction of LLVM mode / afl-clang-fast, the docs (link is to an unofficial mirror) offer some hope for those of us working on less mainstream architectures:

The instrumentation is CPU-independent. At least in principle, you should be able to rely on it to fuzz programs on non-x86 architectures (after building afl-fuzz with AFL_NO_X86=1).

This is, however, the grand total of the documentation available for using afl on other architectures. I’m pleased to report that it works in practice as well as principle, though it’s not straight forward. Here is what I did to start fuzzing with afl when cross compiling for PowerPC targets (ppc & ppc64).

(Update 2017-07-17: minor improvements and a cleaner approach that doesn’t involve installing afl.)

  1. Prerequisites
  2. Aside: cross compilation with clang
  3. Cross compile afl-fuzz and the afl runtime
  4. Usage
  5. Summary

Prerequisites

For this to work you need to be able to compile the software to be fuzzed with vanilla clang and successfully run it on the target. How you do this depends on your software, see below for some difficulties I encountered.

Then you need to compile the afl LLVM mode compilers in the usual fashion for your host system, we’re going to put it in a directory called afl-cross:

wget http://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz
tar xzf afl-latest.tgz
mv afl-2.46b afl-cross
cd afl-cross
make && make -C llvm_mode

Aside: cross compilation with clang

This isn’t afl specific, but is another problem I needed to solve before I could use afl’s LLVM mode compiler.

On the one hand, cross compiling with clang is a breeze because it has built in support for it - you don’t need to compile a dedicated cross-compiler for the job. On the other hand….

With gcc cross compilation we normally specify two different paths - the sysroot for the target headers, and the path to the host’s cross compilation tools. clang doesn’t support this concept properly, having only the concept of the sysroot where it expects everything to be. As clang doesn’t have full support for cross compiling to ppc, it still needs to use your cross-tools version of ld and as. To fix this you can use the undocumented -B switch which instructs clang to search for its guts there first, before its standard paths. Very unsatisfactory, but it does work. You get an invocation that looks like this:

clang -target magic-target-triple -B /path/to/cross/tools/dir -mcpu=<mycpu> \
 --sysroot=/path/to/sysroot -o myExec myExec.c

Actually working out the correct magic-target-triple was far harder than it should have been. Hopefully these improvements to clang will be implemented to help you enumerate the supported targets - until then, searching + guessing was all I had. 32bit ppc target triples seem to take the form powerpc-<vendor>-linux.

If you’re cross compiling for a target that has full support in LLVM then this should be a lot easier.

Cross compile afl-fuzz and the afl runtime

Now we need two things that are compiled to run on the ppc target: afl-fuzz (and any other utilities like afl-analyze); and the tiny blob of afl code that afl-clang-fast will add to every object you compile - afl-llvm-rt.o (and potentially its 64bit companion). Unpack another copy of afl into a new directory, let’s call it afl-target. The appropriate invocation will look something like:

tar xzf afl-latest.tgz
mv afl-2.46b afl-target
cd afl-target
( export CC="clang" CFLAGS="-O3 -funroll-loops -target magic-target-triple \
-m32 -mcpu=<mycpu> --sysroot=/path/to/my/sysroot -B /path/to/cross/tools/dir" \
AFL_NO_X86=1 make && make -C llvm_mode )

(I’m using export to avoid repeating CFLAGS for each make command, and a ( subshell ) so the export doesn’t taint the parent.)

Note that the final step in the Makefile is to run a test by compiling something and executing it - this will fail, because the host architecture is different to the target. That’s fine.

Now replace the cross-compiler’s version of this object with the target one, so our cross compiling afl-clang-fast injects the right code:

cp afl-target/afl-llvm-rt* afl-cross/

Usage

At this point everything just works. Cross compile your target software, e.g. with CC="/path/to/afl-cross/afl-clang-fast" CFLAGS="<all-the-flags>" ./configure && make; copy the instrumented binaries to the target; copy afl-fuzz from the afl-target directory to the target; and fuzz in the normal way.

Summary

You can use afl on non-x86 targets, but need to (a) be able to cross compile for your target with clang, and (b) compile the target architecture variant of afl-llvm-rt*.o for use by the host version of afl-clang-fast. afl-fuzz and other utilities compiled for the target can then be copied on to the target, enabling you to start torturing non-x86 software.