about summary refs log tree commit diff
path: root/docs/notes_for_asan.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/notes_for_asan.md')
-rw-r--r--docs/notes_for_asan.md145
1 files changed, 145 insertions, 0 deletions
diff --git a/docs/notes_for_asan.md b/docs/notes_for_asan.md
new file mode 100644
index 00000000..c05d0771
--- /dev/null
+++ b/docs/notes_for_asan.md
@@ -0,0 +1,145 @@
+# Notes for using ASAN with afl-fuzz
+
+  This file discusses some of the caveats for fuzzing under ASAN, and suggests
+  a handful of alternatives. See README for the general instruction manual.
+
+## 1) Short version
+
+ASAN on 64-bit systems requests a lot of memory in a way that can't be easily
+distinguished from a misbehaving program bent on crashing your system.
+
+Because of this, fuzzing with ASAN is recommended only in four scenarios:
+
+  - On 32-bit systems, where we can always enforce a reasonable memory limit
+    (-m 800 or so is a good starting point),
+
+  - On 64-bit systems only if you can do one of the following:
+
+    - Compile the binary in 32-bit mode (gcc -m32),
+
+    - Precisely gauge memory needs using http://jwilk.net/software/recidivm .
+
+    - Limit the memory available to process using cgroups on Linux (see
+      experimental/asan_cgroups).
+
+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.
+Note that ASAN is incompatible with -static, so be mindful of that.
+
+(You can also use AFL_USE_MSAN=1 to enable MSAN instead.)
+
+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. This is faster,
+and can give you somewhat comparable results. You can also try using
+libdislocator (see libdislocator/README.dislocator.md in the parent directory) as a
+lightweight and hassle-free (but less thorough) alternative.
+
+## 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 VM grabbed by ASAN is essentially
+"free" - but the mapping counts against the standard OS-enforced limit
+(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 - mostly because nobody felt like implementing it.
+
+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. That said,
+if you are on Linux and want to use cgroups, check out the contributed script
+that ships in experimental/asan_cgroups/.
+
+In settings where cgroups aren't available, 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
+with surgical precision, 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.
+
+To get the precise number, you can use the recidivm tool developed by Jakub
+Wilk (http://jwilk.net/software/recidivm). In absence of this, ASAN is *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).
+
+Using recidivm or running with no limits aside, there are two other 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.
+
+## 3) Interactions with the QEMU mode
+
+ASAN, MSAN, and other sanitizers appear to be incompatible with QEMU user
+emulation, so please do not try to use them with the -Q option; QEMU doesn't
+seem to appreciate the shadow VM trick used by these tools, and will likely
+just allocate all your physical memory, then crash.
+
+You can, however, use QASan to run binaries that are not instrumented with ASan
+under QEMU with the AFL++ instrumentation.
+
+https://github.com/andreafioraldi/qasan
+
+## 4) ASAN and OOM crashes
+
+By default, ASAN treats memory allocation failures as fatal errors, immediately
+causing the program to crash. Since this is a departure from normal POSIX
+semantics (and creates the appearance of security issues in otherwise
+properly-behaving programs), we try to disable this by specifying 
+allocator_may_return_null=1 in ASAN_OPTIONS.
+
+Unfortunately, it's been reported that this setting still causes ASAN to
+trigger phantom crashes in situations where the standard allocator would
+simply return NULL. If this is interfering with your fuzzing jobs, you may
+want to cc: yourself on this bug:
+
+  https://bugs.llvm.org/show_bug.cgi?id=22026
+
+## 5) What about UBSAN?
+
+New versions of UndefinedBehaviorSanitizer offers the
+-fsanitize=undefined-trap-on-error compiler flag that tells UBSan to insert an
+istruction that will cause SIGILL (ud2 on x86) when an undefined behaviour
+is detected. This is the option that you want to use when combining AFL++
+and UBSan.
+
+AFL_USE_UBSAN=1 env var will add this compiler flag to afl-clang-fast for you.
+
+Old versions of UBSAN don't offer a consistent way
+to abort() on fault conditions or to terminate with a distinctive exit code
+but there are some versions of the library can be binary-patched to address this
+issue. You can also preload a shared library that substitute all the UBSan
+routines used to report errors with abort().