Barely working LLVM mode for AFL on OSX
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
afl-instr
Makefile
README.llvm
README.osx
afl-clang-fast.c
afl-llvm-pass.so.cc
afl-llvm-rt.o.c
compile_llvm.sh

README.llvm

============================================
Fast LLVM-based instrumentation for afl-fuzz
============================================

  (See ../docs/README for the general instruction manual.)

1) Introduction
---------------

The code in this directory allows you to instrument programs for AFL using
true compiler-level instrumentation, instead of the more crude
assembly-level rewriting approach taken by afl-gcc and afl-clang. This has
several interesting properties:

  - The compiler can make many optimizations that are hard to pull off when
    manually inserting assembly. As a result, some slow, CPU-bound programs will
    run up to around 2x faster.

    The gains are less pronounced for fast binaries, where the speed is limited
    chiefly by the cost of creating new processes. In such cases, the gain will
    probably stay within 10%.

  - 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_NOX86=1).

  - Because the features relies on the internals of LLVM, it is clang-specific
    and will *not* work with GCC.

Once this implementation is shown to be sufficiently robust, it will probably
replace afl-clang. For now, it can be built separately and co-exists with the
original code.

The idea and much of the implementation comes from Laszlo Szekeres.

2) How to use
-------------

In order to leverage this mechanism, you need to have clang installed on your
system. You should also make sure that the llvm-config tool is in your path;
if not, be sure to set LLVM_CONFIG in the environment beforehand.

Unfortunately, some systems come without llvm-config or the LLVM development
headers. In such a case, you may have to do a full install on your own, or
download pre-built binaries from:

  http://llvm.org/releases/download.html

To build the instrumentation, type 'make'. This will generate binaries called
afl-clang-fast and afl-clang-fast++ in the parent directory. Once this is done,
you can instrument third-party code in a way similar to the standard operating
mode of AFL, e.g.:

  CC=/path/to/afl/afl-clang-fast ./configure [...options...]
  make

...or:

  CXX=/path/to/afl/afl-clang-fast++ ./configure [...options...]
  make

The tool honors roughly the same environmental variables as afl-gcc (see
../docs/env_variables.txt). This includes AFL_INST_RATIO, AFL_USE_ASAN,
AFL_HARDEN, and AFL_DONT_OPTIMIZE.

Note: if you want the LLVM helper to be installed on your system for all
users, you need to build it before issuing 'make install' in the parent
directory.

3) Gotchas, feedback, bugs
--------------------------

This is an early-stage mechanism, so field reports are welcome. You can send
bug reports to <afl-users@googlegroups.com>.

4) Bonus feature: deferred instrumentation
------------------------------------------

AFL tries to optimize performance by executing the targeted binary just once,
stopping it just before main(), and then cloning this "master" process to get
a steady supply of targets to fuzz.

Although this approach eliminates much of the OS-, linker- and libc-level
costs of executing the program, it does not always help with binaries that
perform other time-consuming initialization steps before getting to the input
file.

In such cases, it would be beneficial to initialize the forkserver a bit later,
once most of the initialization work is already done, but before the binary
attempts to read the fuzzed input and parse it. You can do this in LLVM mode in
a fairly simple way:

1) First, locate a suitable location in the code for the deferred initialization
   to take place. This needs to be done with *extreme* care to avoid breaking
   the binary. In particular, the program will probably malfunction if the
   initialization happens after:

   - The creation of any vital threads or child processes - since the forkserver
     can't clone them easily.

   - The creation of temporary files, network sockets, offset-sensitive file
     descriptors, and similar shared-state resources - but only provided that
     their state meaningfully influences the behavior of the program later on.

   - Any access to the fuzzed input, including the metadata about its size.

   Of course, things will also not work if the forkserver is never initialized
   at all; in this case, afl-fuzz will complain about failed handshake and bail
   out.

2) Next, insert the following global function declaration somewhere in the
   source file:

   void __afl_manual_init(void);

   ...and add a call to this function in the desired location before recompiling
   the project with afl-clang-fast (afl-gcc and afl-clang will *not* work).

3) Finally, be sure to set AFL_DEFER_FORKSRV=1 before invoking afl-fuzz.

Again, this feature is easy to misuse; be careful and double-test that the
coverage and the number of discovered paths is comparable between normal and
deferred runs. That said, when you do it well, you can see gains up to 10x or
so:

  https://groups.google.com/forum/#!topic/afl-users/fNMJHl7Fhzs