==================================
Notes for using ASAN with afl-fuzz
==================================

  (See README for the general instruction manual)

1) Short version
----------------

ASAN on 64-bit systems uses a lot of memory in a way that can't be easily
distinguished from a misbehaving program that could crash your system.

Because of this, fuzzing with ASAN is recommended in two cases:

  - If you are using a 32-bit system, or can compile the target binary with
    the -m32 flag. In this case, we can still enforce reasonable memory
    limits.

  - On 64-bit, only if you trust the target binary to be well-behaved and not
    attempt to use up all physical memory on the host OS.

To compile with ASAN, set AFL_USE_ASAN=1 before calling 'make clean all'. The
afl-gcc / afl-clang wrappers will pick that up and add the appropriate flags.

(You can also use AFL_USE_MSAN=1 to enable MSAN instead.)

For 32-bit targets, you will need to run afl-fuzz with '-m 800' or so to give
ASAN some breathing room. For 64-bit targets, you need to use '-m none'.

There is also the option of generating a corpus using a non-ASAN binary, and
then feeding it to an ASAN-instrumented one to check for bugs.

2) Long version
---------------

ASAN allocates a huge region of virtual address space for bookkeeping purposes.
Most of this is never actually accessed, so the OS never has to allocate any
real pages of memory for the process, and the memory grabbed by ASAN is
essentially "free" - but the mapping counts against the OS-enforced limit
of virtual memory (RLIMIT_AS, aka ulimit -v).

On our end, afl-fuzz tries to protect you from processes that go off-rails
and start consuming all the available memory in a vain attempt to parse a
malformed input file. This happens surprisingly often, so enforcing such a limit
is important for almost any fuzzer: the alternative is for the kernel OOM
handler to step in and start killing random processes to free up resources.
Needless to say, that's not a very nice prospect to live with.

Unfortunately, un*x systems offer no portable way to limit the amount of
pages actually given to a process in a way that distinguishes between that
and the harmless "land grab" done by ASAN. In principle, there are three standard
ways to limit the size of the heap:

  - The RLIMIT_AS mechanism (ulimit -v) caps the size of the virtual space,
    but as noted, this pays no attention to the number of pages actually
    in use by the process, and doesn't help us here.

  - The RLIMIT_DATA mechanism (ulimit -d) seems like a good fit, but it applies
    only to the traditional sbrk() / brk() methods of requesting heap space;
    modern allocators, including the one in glibc, routinely rely on mmap()
    instead, and circumvent this limit completely.

  - Finally, the RLIMIT_RSS limit (ulimit -m) sounds like what we need, but
    doesn't work on Linux - apparently, mostly for historical reasons.

There are also cgroups, but they are Linux-specific, not universally available
even on Linux systems, and they require root permissions to set up; I'm a bit
hesitant to make afl-fuzz require root permissions just for that.

So, we have no nice, portable way to avoid counting the ASAN allocation toward
the limit. On 32-bit systems, or for binaries compiled in 32-bit mode (-m32),
this is not a big deal: ASAN needs around 600-800 MB or so, depending on the
compiler - so all you need to do is to specify -m that is a bit higher than
that.

On 64-bit systems, the situation is more murky, because the ASAN allocation
is completely outlandish - around 17.5 TB in older versions, and closer to
20 TB with newest ones. The actual amount of memory on your system is
(probably!) just a tiny fraction of that - so unless you dial the limit
just right, you will get no protection from OOM bugs.

On my system, the amount of memory grabbed by ASAN with a slightly older
version of gcc is around 17,825,850 MB; for newest clang, it's 20,971,600.
But there is no guarantee that these numbers are stable, and if you get them
wrong by "just" a couple gigs or so, you will be at risk.

In other words, use of ASAN is generally *not* recommended when fuzzing
64-bit binaries, unless you are confident that they are robust and enforce
reasonable memory limits (in which case, you can specify '-m none' when
calling afl-fuzz), or unless you get the -m value adjusted very precisely.

There are two decent alternatives: build a corpus of test cases using a
non-ASAN binary, and then examine them with ASAN, Valgrind, or other
heavy-duty tools in a more controlled setting; or compile the target program
with -m32 (32-bit mode) if your system supports that.
