Add support for standalone leak-sanitizer, introducting the environment
variable AFL_USE_LSAN.
AFL_USE_LSAN introduces the macro __AFL_CHECK_LEAK() which will check
for a memory leak when the macro is run. This is especially helpful
when using __AFL_LOOP().
If __AFL_LEAK_CHECK() is not used when AFL_USE_LSAN=1 is set,
the leak checker will run when the program exits.
MODIFY
GNUmakefile
GNUmakefile
@@ -517,7 +517,7 @@ code-format:
ifndef AFL_NO_X86
test_build: afl-cc afl-gcc afl-as afl-showmap
@echo "[*] Testing the CC wrapper afl-cc and its instrumentation output..."
- @unset AFL_MAP_SIZE AFL_USE_UBSAN AFL_USE_CFISAN AFL_USE_ASAN AFL_USE_MSAN; ASAN_OPTIONS=detect_leaks=0 AFL_INST_RATIO=100 AFL_PATH=. ./afl-cc test-instr.c -o test-instr 2>&1 || (echo "Oops, afl-cc failed"; exit 1 )
+ @unset AFL_MAP_SIZE AFL_USE_UBSAN AFL_USE_CFISAN AFL_USE_LSAN AFL_USE_ASAN AFL_USE_MSAN; ASAN_OPTIONS=detect_leaks=0 AFL_INST_RATIO=100 AFL_PATH=. ./afl-cc test-instr.c -o test-instr 2>&1 || (echo "Oops, afl-cc failed"; exit 1 )
ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr0 ./test-instr < /dev/null
echo 1 | ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr1 ./test-instr
@rm -f test-instr
@@ -525,7 +525,7 @@ test_build: afl-cc afl-gcc afl-as afl-showmap
@echo
@echo "[+] All right, the instrumentation of afl-cc seems to be working!"
# @echo "[*] Testing the CC wrapper afl-gcc and its instrumentation output..."
-# @unset AFL_MAP_SIZE AFL_USE_UBSAN AFL_USE_CFISAN AFL_USE_ASAN AFL_USE_MSAN; AFL_CC=$(CC) ASAN_OPTIONS=detect_leaks=0 AFL_INST_RATIO=100 AFL_PATH=. ./afl-gcc test-instr.c -o test-instr 2>&1 || (echo "Oops, afl-gcc failed"; exit 1 )
+# @unset AFL_MAP_SIZE AFL_USE_UBSAN AFL_USE_CFISAN AFL_USE_LSAN AFL_USE_ASAN AFL_USE_MSAN; AFL_CC=$(CC) ASAN_OPTIONS=detect_leaks=0 AFL_INST_RATIO=100 AFL_PATH=. ./afl-gcc test-instr.c -o test-instr 2>&1 || (echo "Oops, afl-gcc failed"; exit 1 )
# ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr0 ./test-instr < /dev/null
# echo 1 | ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr1 ./test-instr
# @rm -f test-instr

MODIFY
README.md
README.md
@@ -601,7 +601,7 @@ Every -M/-S entry needs a unique name (that can be whatever), however the same
For every secondary fuzzer there should be a variation, e.g.:
* one should fuzz the target that was compiled differently: with sanitizers
activated (`export AFL_USE_ASAN=1 ; export AFL_USE_UBSAN=1 ;
- export AFL_USE_CFISAN=1 ; `
+ export AFL_USE_CFISAN=1 ; export AFL_USE_LSAN`)
* one should fuzz the target with CMPLOG/redqueen (see above)
* one to three fuzzers should fuzz a target compiled with laf-intel/COMPCOV
(see above). Important note: If you run more than one laf-intel/COMPCOV

MODIFY
docs/env_variables.md
docs/env_variables.md
@@ -55,7 +55,7 @@ make fairly broad use of environmental variables instead:
overridden.
- Setting `AFL_USE_ASAN` automatically enables ASAN, provided that your
- compiler supports that. Note that fuzzing with ASAN is mildly challenging
+ compiler supports itt. Note that fuzzing with ASAN is mildly challenging
- see [notes_for_asan.md](notes_for_asan.md).
(You can also enable MSAN via `AFL_USE_MSAN`; ASAN and MSAN come with the
@@ -64,6 +64,13 @@ make fairly broad use of environmental variables instead:
there is the Control Flow Integrity sanitizer that can be activated by
`AFL_USE_CFISAN=1`)
+ - Setting `AFL_USE_LSAN` automatically enables Leak-Sanitizer, provided
+ that your compiler supports it. To perform a leak check within your
+ program at a certain point (such as at the end of an __AFL_LOOP,
+ you can run the macro __AFL_CHECK_LEAK(); which will cause
+ an abort if any memory is leaked (you can combine this with the
+ LSAN_OPTIONS=suppressions option to supress some known leaks).
+
- Setting `AFL_CC`, `AFL_CXX`, and `AFL_AS` lets you use alternate downstream
compilation tools, rather than the default 'clang', 'gcc', or 'as' binaries
in your `$PATH`.
@@ -628,7 +635,12 @@ optimal values if not already present in the environment:
msan_track_origins=0
allocator_may_return_null=1
```
- Be sure to include the first one when customizing anything, since some
- MSAN versions don't call `abort()` on error, and we need a way to detect
- faults.
+ - Similarly, the default `LSAN_OPTIONS` are set to:
+```
+ exit_code=86
+ fast_unwind_on_malloc=0
+````
+ Be sure to include the first ones for LSAN and MSAN when customizing
+ anything, since some MSAN and LSAN versions don't call `abort()` on
+ error, and we need a way to detect faults.

MODIFY
docs/notes_for_asan.md
docs/notes_for_asan.md
@@ -28,6 +28,13 @@ Note that ASAN is incompatible with -static, so be mindful of that.
(You can also use AFL_USE_MSAN=1 to enable MSAN instead.)
+When compiling with AFL_USE_LSAN, the leak sanitizer will normally run
+when the program exits. In order to utilize this check at different times,
+such as at the end of a loop, you may use the macro __AFL_CHECK_LEAK();.
+This macro will report a crash in afl-fuzz if any memory is left leaking
+at this stage. You can also use LSAN_OPTIONS and a supressions file
+for more fine-tuned checking, however make sure you keep exitcode=23.
+
NOTE: if you run several secondary instances, only one should run the target
compiled with ASAN (and UBSAN, CFISAN), the others should run the target with
no sanitizers compiled in.

MODIFY
include/config.h
include/config.h
#define MSAN_ERROR 86
+/* Distinctive exit code used to indicate LSAN trip condition: */
+
+#define LSAN_ERROR 23
+
/* Designated file descriptors for forkserver commands (the application will
use FORKSRV_FD and FORKSRV_FD + 1): */

MODIFY
include/envs.h
include/envs.h
@@ -172,6 +172,7 @@ static char *afl_environment_variables[] = {
"AFL_USE_TRACE_PC",
"AFL_USE_UBSAN",
"AFL_USE_CFISAN",
+ "AFL_USE_LSAN",
"AFL_WINE_PATH",
"AFL_NO_SNAPSHOT",
"AFL_EXPAND_HAVOC_NOW",

MODIFY
src/afl-analyze.c
src/afl-analyze.c
@@ -781,6 +781,19 @@ static void set_up_environment(void) {
}
+ x = get_afl_env("LSAN_OPTIONS");
+
+ if (x) {
+
+ if (!strstr(x, "exit_code=" STRINGIFY(LSAN_ERROR))) {
+
+ FATAL("Custom LSAN_OPTIONS set without exit_code=" STRINGIFY(
+ LSAN_ERROR) " - please fix!");
+
+ }
+
+ }
+
setenv("ASAN_OPTIONS",
"abort_on_error=1:"
"detect_leaks=0:"
@@ -818,6 +831,12 @@ static void set_up_environment(void) {
"handle_sigfpe=0:"
"handle_sigill=0", 0);
+ setenv("LSAN_OPTIONS",
+ "exitcode=" STRINGIFY(MSAN_ERROR) ":"
+ "fast_unwind_on_malloc=0",
+ 0);
+
+
if (get_afl_env("AFL_PRELOAD")) {
if (qemu_mode) {

MODIFY
src/afl-as.c
src/afl-as.c
@@ -517,11 +517,12 @@ static void add_instrumentation(void) {
} else {
char modeline[100];
- snprintf(modeline, sizeof(modeline), "%s%s%s%s",
+ snprintf(modeline, sizeof(modeline), "%s%s%s%s%s",
getenv("AFL_HARDEN") ? "hardened" : "non-hardened",
getenv("AFL_USE_ASAN") ? ", ASAN" : "",
getenv("AFL_USE_MSAN") ? ", MSAN" : "",
- getenv("AFL_USE_UBSAN") ? ", UBSAN" : "");
+ getenv("AFL_USE_UBSAN") ? ", UBSAN" : "",
+ getenv("AFL_USE_LSAN") ? ", LSAN" : "");
OKF("Instrumented %u locations (%s-bit, %s mode, ratio %u%%).", ins_lines,
use_64bit ? "64" : "32", modeline, inst_ratio);
@@ -585,7 +586,7 @@ int main(int argc, char **argv) {
"AFL_QUIET: suppress verbose output\n"
"AFL_KEEP_ASSEMBLY: leave instrumented assembly files\n"
"AFL_AS_FORCE_INSTRUMENT: force instrumentation for asm sources\n"
- "AFL_HARDEN, AFL_USE_ASAN, AFL_USE_MSAN, AFL_USE_UBSAN:\n"
+ "AFL_HARDEN, AFL_USE_ASAN, AFL_USE_MSAN, AFL_USE_UBSAN, AFL_USE_LSAN:\n"
" used in the instrumentation summary message\n",
argv[0]);

MODIFY
src/afl-cc.c
src/afl-cc.c
@@ -758,7 +758,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
if (!strncmp(cur, "-fsanitize-coverage-", 20) && strstr(cur, "list="))
have_instr_list = 1;
- if (!strcmp(cur, "-fsanitize=address") || !strcmp(cur, "-fsanitize=memory"))
+ if (!(strcmp(cur, "-fsanitize=address") && strcmp(cur, "-fsanitize=memory")))
asan_set = 1;
if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1;
@@ -817,6 +817,10 @@ static void edit_params(u32 argc, char **argv, char **envp) {
}
+ if (getenv("AFL_USE_LSAN")) {
+ cc_params[cc_par_cnt++] = "-fsanitize=leak";
+ }
+
if (getenv("AFL_USE_CFISAN")) {
if (!lto_mode) {
@@ -914,6 +918,13 @@ static void edit_params(u32 argc, char **argv, char **envp) {
}
+ if (getenv("AFL_USE_LSAN")) {
+ cc_params[cc_par_cnt++] = "-includesanitizer/lsan_interface.h";
+ }
+
+ cc_params[cc_par_cnt++] =
+ "-D__AFL_CHECK_LEAK()=__lsan_do_leak_check()";
+
cc_params[cc_par_cnt++] =
"-D__AFL_COVERAGE_START_OFF()=int __afl_selective_coverage_start_off = "
"1;";
@@ -1740,7 +1751,8 @@ int main(int argc, char **argv, char **envp) {
" AFL_USE_ASAN: activate address sanitizer\n"
" AFL_USE_CFISAN: activate control flow sanitizer\n"
" AFL_USE_MSAN: activate memory sanitizer\n"
- " AFL_USE_UBSAN: activate undefined behaviour sanitizer\n");
+ " AFL_USE_UBSAN: activate undefined behaviour sanitizer\n"
+ " AFL_USE_LSAN: activate leak-checker sanitizer\n");
if (have_gcc_plugin)
SAYF(

MODIFY
src/afl-forkserver.c
src/afl-forkserver.c
@@ -483,7 +483,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
if (!getenv("LD_BIND_LAZY")) { setenv("LD_BIND_NOW", "1", 1); }
- /* Set sane defaults for ASAN if nothing else specified. */
+ /* Set sane defaults for ASAN if nothing else is specified. */
if (!getenv("ASAN_OPTIONS"))
setenv("ASAN_OPTIONS",
@@ -500,7 +500,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
"handle_sigill=0",
1);
- /* Set sane defaults for UBSAN if nothing else specified. */
+ /* Set sane defaults for UBSAN if nothing else is specified. */
if (!getenv("UBSAN_OPTIONS"))
setenv("UBSAN_OPTIONS",
@@ -538,6 +538,14 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
"handle_sigill=0",
1);
+ /* LSAN, too, does not support abort_on_error=1. */
+
+ if (!getenv("LSAN_OPTIONS"))
+ setenv("LSAN_OPTIONS",
+ "exitcode=" STRINGIFY(LSAN_ERROR) ":"
+ "fast_unwind_on_malloc=0",
+ 1);
+
fsrv->init_child_func(fsrv, argv);
/* Use a distinctive bitmap signature to tell the parent about execv()
@@ -1210,8 +1218,9 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
if (unlikely(
/* A normal crash/abort */
(WIFSIGNALED(fsrv->child_status)) ||
- /* special handling for msan */
- (fsrv->uses_asan && WEXITSTATUS(fsrv->child_status) == MSAN_ERROR) ||
+ /* special handling for msan and lsan */
+ (fsrv->uses_asan && (WEXITSTATUS(fsrv->child_status) == MSAN_ERROR ||
+ WEXITSTATUS(fsrv->child_status) == LSAN_ERROR)) ||
/* the custom crash_exitcode was returned by the target */
(fsrv->uses_crash_exitcode &&
WEXITSTATUS(fsrv->child_status) == fsrv->crash_exitcode))) {

MODIFY
src/afl-fuzz-init.c
src/afl-fuzz-init.c
@@ -2466,6 +2466,20 @@ void check_asan_opts(afl_state_t *afl) {
}
+ x = get_afl_env("LSAN_OPTIONS");
+
+ if (x) {
+
+ if (!strstr(x, "exit_code=" STRINGIFY(LSAN_ERROR))) {
+
+ FATAL("Custom LSAN_OPTIONS set without exit_code=" STRINGIFY(
+ LSAN_ERROR) " - please fix!");
+
+ }
+
+ }
+
+
}
/* Handle stop signal (Ctrl-C, etc). */
@@ -2711,7 +2725,8 @@ void check_binary(afl_state_t *afl, u8 *fname) {
}
if (memmem(f_data, f_len, "__asan_init", 11) ||
- memmem(f_data, f_len, "__msan_init", 11)) {
+ memmem(f_data, f_len, "__msan_init", 11) ||
+ memmem(f_data, f_len, "__lsan_init", 11)) {
afl->fsrv.uses_asan = 1;

MODIFY
src/afl-showmap.c
src/afl-showmap.c
@@ -570,6 +570,10 @@ static void set_up_environment(afl_forkserver_t *fsrv) {
"handle_sigfpe=0:"
"handle_sigill=0",
0);
+ setenv("LSAN_OPTIONS",
+ "exitcode=" STRINGIFY(LSAN_ERROR) ":"
+ "fast_unwind_on_malloc=0",
+ 0);
setenv("UBSAN_OPTIONS",
"halt_on_error=1:"

MODIFY
src/afl-tmin.c
src/afl-tmin.c
@@ -712,6 +712,19 @@ static void set_up_environment(afl_forkserver_t *fsrv) {
}
+ x = get_afl_env("LSAN_OPTIONS");
+
+ if (x) {
+
+ if (!strstr(x, "exit_code=" STRINGIFY(LSAN_ERROR))) {
+
+ FATAL("Custom LSAN_OPTIONS set without exit_code=" STRINGIFY(
+ LSAN_ERROR) " - please fix!");
+
+ }
+
+ }
+
setenv("ASAN_OPTIONS",
"abort_on_error=1:"
"detect_leaks=0:"
@@ -749,6 +762,11 @@ static void set_up_environment(afl_forkserver_t *fsrv) {
"handle_sigfpe=0:"
"handle_sigill=0", 0);
+ setenv("LSAN_OPTIONS",
+ "exitcode=" STRINGIFY(LSAN_ERROR) ":"
+ "fast_unwind_on_malloc=0",
+ 0);
+
if (get_afl_env("AFL_PRELOAD")) {
if (fsrv->qemu_mode) {

MODIFY
test/test-pre.sh
test/test-pre.sh
@@ -71,6 +71,7 @@ unset AFL_HARDEN
unset AFL_USE_ASAN
unset AFL_USE_MSAN
unset AFL_USE_UBSAN
+unset AFL_USE_LSAN
unset AFL_TMPDIR
unset AFL_CC
unset AFL_PRELOAD

Add support for standalone leak-sanitizer, introducting the environment
variable AFL_USE_LSAN.

AFL_USE_LSAN introduces the macro __AFL_CHECK_LEAK() which will check
for a memory leak when the macro is run. This is especially helpful
when using __AFL_LOOP().

If __AFL_LEAK_CHECK() is not used when AFL_USE_LSAN=1 is set,
the leak checker will run when the program exits.