diff --git a/Makefile b/Makefile index db5e4622..cac830da 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ # PROGNAME = afl -VERSION = 1.88b +VERSION = 1.89b PREFIX ?= /usr/local BIN_PATH = $(PREFIX)/bin diff --git a/afl-fuzz.c b/afl-fuzz.c index e52b7caf..d240e4ef 100644 --- a/afl-fuzz.c +++ b/afl-fuzz.c @@ -2072,9 +2072,9 @@ static void init_forkserver(char** argv) { " - Less likely, there is a horrible bug in the fuzzer. If other options\n" " fail, poke for troubleshooting tips.\n", - getenv("AFL_DEFER_FORKSRV") ? "three" : "two", - getenv("AFL_DEFER_FORKSRV") ? - " - You are using AFL_DEFER_FORKSRV, but __afl_manual_init() is never\n" + getenv(DEFER_ENV_VAR) ? "three" : "two", + getenv(DEFER_ENV_VAR) ? + " - You are using deferred forkserver, but __AFL_INIT() is never\n" " reached before the program terminates.\n\n" : "", DMS(mem_limit << 20), mem_limit - 1); @@ -6605,6 +6605,23 @@ static void check_binary(u8* fname) { if (memmem(f_data, f_len, "libasan.so", 10) || memmem(f_data, f_len, "__msan_init", 11)) uses_asan = 1; + /* Detect persistent & deferred init signatures in the binary. */ + + if (memmem(f_data, f_len, PERSIST_SIG, strlen(PERSIST_SIG) + 1)) { + + OKF("Persistent-mode binary detected."); + setenv(PERSIST_ENV_VAR, "1", 1); + no_var_check = 1; + + } + + if (memmem(f_data, f_len, DEFER_SIG, strlen(DEFER_SIG) + 1)) { + + OKF("Deferred forkserver binary detected."); + setenv(DEFER_ENV_VAR, "1", 1); + + } + if (munmap(f_data, f_len)) PFATAL("unmap() failed"); } @@ -6877,7 +6894,8 @@ static void check_crash_handling(void) { " launchctl unload -w ${SL}/LaunchAgents/${PL}.plist\n" " sudo launchctl unload -w ${SL}/LaunchDaemons/${PL}.Root.plist\n"); - FATAL("Crash reporter detected"); + if (!getenv("AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES")) + FATAL("Crash reporter detected"); #else @@ -6904,7 +6922,8 @@ static void check_crash_handling(void) { " echo core >/proc/sys/kernel/core_pattern\n"); - FATAL("Pipe at the beginning of 'core_pattern'"); + if (!getenv("AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES")) + FATAL("Pipe at the beginning of 'core_pattern'"); } @@ -7491,11 +7510,9 @@ int main(int argc, char** argv) { } - if (getenv("AFL_NO_FORKSRV")) no_forkserver = 1; - if (getenv("AFL_NO_CPU_RED")) no_cpu_meter_red = 1; - - if (getenv("AFL_NO_VAR_CHECK") || getenv("AFL_PERSISTENT")) - no_var_check = 1; + if (getenv("AFL_NO_FORKSRV")) no_forkserver = 1; + if (getenv("AFL_NO_CPU_RED")) no_cpu_meter_red = 1; + if (getenv("AFL_NO_VAR_CHECK")) no_var_check = 1; if (dumb_mode == 2 && no_forkserver) FATAL("AFL_DUMB_FORKSRV and AFL_NO_FORKSRV are mutually exclusive"); diff --git a/afl-showmap.c b/afl-showmap.c index e7f53be6..a534f5b9 100644 --- a/afl-showmap.c +++ b/afl-showmap.c @@ -337,8 +337,6 @@ static void set_up_environment(void) { setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":" "msan_track_origins=0", 0); - unsetenv("AFL_PERSISTENT"); - } @@ -444,7 +442,8 @@ static void usage(u8* argv0) { " -q - sink program's output and don't show messages\n" " -e - show edge coverage only, ignore hit counts\n\n" - "For additional tips, please consult %s/README.\n\n", + "This tool displays raw tuple data captured by AFL instrumentation.\n" + "For additional help, consult %s/README.\n\n", argv0, MEM_LIMIT, doc_path); diff --git a/afl-tmin.c b/afl-tmin.c index e82e9420..c9a678b3 100644 --- a/afl-tmin.c +++ b/afl-tmin.c @@ -684,8 +684,6 @@ static void set_up_environment(void) { setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":" "msan_track_origins=0", 0); - unsetenv("AFL_PERSISTENT"); - } diff --git a/config.h b/config.h index 5ef46b03..9f16afbc 100644 --- a/config.h +++ b/config.h @@ -263,6 +263,13 @@ #define CLANG_ENV_VAR "__AFL_CLANG_MODE" #define AS_LOOP_ENV_VAR "__AFL_AS_LOOPCHECK" +#define PERSIST_ENV_VAR "__AFL_PERSISTENT" +#define DEFER_ENV_VAR "__AFL_DEFER_FORKSRV" + +/* In-code signatures for deferred and persistent mode. */ + +#define PERSIST_SIG "##SIG_AFL_PERSISTENT##" +#define DEFER_SIG "##SIG_AFL_DEFER_FORKSRV##" /* Distinctive bitmap signature used to indicate failed execution: */ diff --git a/docs/ChangeLog b/docs/ChangeLog index 41f9eab8..8979eaf0 100644 --- a/docs/ChangeLog +++ b/docs/ChangeLog @@ -16,6 +16,16 @@ Not sure if you should upgrade? The lowest currently recommended version is 1.76b. If you're stuck on an earlier release, it's strongly advisable to get on with the times. +-------------- +Version 1.89b: +-------------- + + - Revamped the support for persistent and deferred forkserver modes. + Both now feature simpler syntax and do not require companion env + variables. Suggested by Jakub Wilk. + + - Added a bit more info about afl-showmap. Suggested by Jacek Wielemborek. + -------------- Version 1.88b: -------------- diff --git a/docs/env_variables.txt b/docs/env_variables.txt index fac42a78..7bad8f82 100644 --- a/docs/env_variables.txt +++ b/docs/env_variables.txt @@ -122,14 +122,8 @@ checks or alter some of the more exotic semantics of the tool: - When running in the -M or -S mode, setting AFL_IMPORT_FIRST causes the fuzzer to import test cases from other instances before doing anything - else. This is in contrast with the default mode, where the instances are - allowed to run on their own for a while before syncing, so that they - acquire some initial variability in their respective input sets. - - The benefit of AFL_IMPORT_FIRST is that it makes the "own finds" counter - in the UI more accurately reflect the number of findings made by the - instance (that the remaining instances did not already have). Beyond - counter aesthetics, not much else should change. + else. This makes the "own finds" counter in the UI more accurate + Beyond counter aesthetics, not much else should change. - Setting AFL_POST_LIBRARY allows you to configure a postprocessor for mutated files - say, to fix up checksums. See experimental/post_library/ @@ -139,18 +133,11 @@ checks or alter some of the more exotic semantics of the tool: may complain of high load prematurely, especially on systems with low core counts. To avoid the alarming red color, you can set AFL_NO_CPU_RED. - - In LLVM mode, AFL_DEFER_FORKSRV can be set to require the forkserver to - be initialized manually (see llvm_mode/README.llvm). This setting has no - effect for programs not compiled with afl-clang-fast. - - (Technically speaking, the setting is passed down to the binary itself, - and not handled in any special way by afl-fuzz.) - - - In LLVM mode, AFL_PERSISTENT can be set to fuzz in persistent mode. See - llvm_mode/README.llvm for additional information on what this means. - - In QEMU mode (-Q), AFL_PATH will be searched for afl-qemu-trace. + - If you are Jakub, you may need AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES. + Others need not apply. + 4) Settings for afl-qemu-trace ------------------------------ diff --git a/experimental/persistent_demo/persistent_demo.c b/experimental/persistent_demo/persistent_demo.c index 9b9f776c..b3e8abd6 100644 --- a/experimental/persistent_demo/persistent_demo.c +++ b/experimental/persistent_demo/persistent_demo.c @@ -18,8 +18,7 @@ across runs. To make this work, the library and this shim need to be compiled in LLVM - mode using afl-clang-fast (other compiler wrappers will *not* work); and - afl-fuzz must be called with AFL_PERSISTENT set. + mode using afl-clang-fast (other compiler wrappers will *not* work). */ @@ -29,14 +28,6 @@ #include #include -/* This constant specifies the number of inputs to process before restarting. - This is optional, but helps limit the impact of memory leaks and similar - hiccups. */ - -#define PERSIST_MAX 1000 - -unsigned int persist_cnt; - /* Main entry point. */ @@ -45,57 +36,52 @@ int main(int argc, char** argv) { char buf[100]; /* Example-only buffer, you'd replace it with other global or local variables appropriate for your use case. */ -try_again: + /* The number passed to __AFL_LOOP() controls the maximum number of + iterations before the loop exits and the program is allowed to + terminate normally. This limits the impact of accidental memory leaks + and similar hiccups. */ + + while (__AFL_LOOP(1000)) { - /*** PLACEHOLDER CODE ***/ + /*** PLACEHOLDER CODE ***/ - /* STEP 1: Fully re-initialize all critical variables. In our example, this - involves zeroing buf[], our input buffer. */ + /* STEP 1: Fully re-initialize all critical variables. In our example, this + involves zeroing buf[], our input buffer. */ - memset(buf, 0, 100); + memset(buf, 0, 100); - /* STEP 2: Read input data. When reading from stdin, no special preparation - is required. When reading from a named file, you need to close the - old descriptor and reopen the file first! + /* STEP 2: Read input data. When reading from stdin, no special preparation + is required. When reading from a named file, you need to close + the old descriptor and reopen the file first! - Beware of reading from buffered FILE* objects such as stdin. Use - raw file descriptors or call fopen() / fdopen() in every pass. */ + Beware of reading from buffered FILE* objects such as stdin. Use + raw file descriptors or call fopen() / fdopen() in every pass. */ - read(0, buf, 100); + read(0, buf, 100); - /* STEP 3: This is where we'd call the tested library on the read data. Here, - we just have some trivial inline code that faults on 'foo!'. */ + /* STEP 3: This is where we'd call the tested library on the read data. + We just have some trivial inline code that faults on 'foo!'. */ - if (buf[0] == 'f') { - printf("one\n"); - if (buf[1] == 'o') { - printf("two\n"); - if (buf[2] == 'o') { - printf("three\n"); - if (buf[3] == '!') { - printf("four\n"); - abort(); + if (buf[0] == 'f') { + printf("one\n"); + if (buf[1] == 'o') { + printf("two\n"); + if (buf[2] == 'o') { + printf("three\n"); + if (buf[3] == '!') { + printf("four\n"); + abort(); + } } } } - } - - /*** END PLACEHOLDER CODE ***/ - - /* STEP 4: To signal successful completion of a run, we need to deliver - SIGSTOP to our own process, then loop to the very beginning - once we're resumed by the supervisor process. We do this only - if AFL_PERSISTENT is set to retain normal behavior when the - program is executed directly; and take note of PERSIST_MAX. */ - - if (getenv("AFL_PERSISTENT") && persist_cnt++ < PERSIST_MAX) { - raise(SIGSTOP); - goto try_again; + /*** END PLACEHOLDER CODE ***/ } - /* If AFL_PERSISTENT not set or PERSIST_MAX exceeded, exit normally. */ + /* Once the loop is exited, terminate normally - AFL will restat the process + from scratch. */ return 0; diff --git a/llvm_mode/Makefile b/llvm_mode/Makefile index d4180ff2..7ea3c3f5 100644 --- a/llvm_mode/Makefile +++ b/llvm_mode/Makefile @@ -80,7 +80,7 @@ test_deps: test_build: $(PROGS) @echo "[*] Testing the CC wrapper and instrumentation output..." - unset AFL_USE_ASAN AFL_USE_MSAN AFL_DEFER_FORKSRV; AFL_QUIET=1 AFL_INST_RATIO=100 AFL_PATH=. AFL_CC=$(CC) ../afl-clang-fast $(CFLAGS) ../test-instr.c -o test-instr $(LDFLAGS) + unset AFL_USE_ASAN AFL_USE_MSAN; AFL_QUIET=1 AFL_INST_RATIO=100 AFL_PATH=. AFL_CC=$(CC) ../afl-clang-fast $(CFLAGS) ../test-instr.c -o test-instr $(LDFLAGS) echo 0 | ../afl-showmap -m none -q -o .test-instr0 ./test-instr echo 1 | ../afl-showmap -m none -q -o .test-instr1 ./test-instr @rm -f test-instr diff --git a/llvm_mode/README.llvm b/llvm_mode/README.llvm index 97ae1341..6eebce4b 100644 --- a/llvm_mode/README.llvm +++ b/llvm_mode/README.llvm @@ -89,51 +89,41 @@ 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. +perform other time-consuming initialization steps - say, parsing a large config +file before getting to the fuzzed data. -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: +In such cases, it's 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; in some cases, this can offer a 10x+ +performance gain. You can implement delayed initialization 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: +First, locate a suitable location in the code where the delayed cloning can +take place. This needs to be done with *extreme* care to avoid breaking the +binary. In particular, the program will probably malfunction if you select +a location after: - - The creation of any vital threads or child processes - since the forkserver - can't clone them easily. + - 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. + - 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 reading the metadata about its - size. + - Any access to the fuzzed input, including reading the metadata about its + size. - Of course, things will also not work if the forkserver is never initialized - at all and the program is allowed to exit before that; in this case, afl-fuzz - will complain about failed handshake and bail out. +With the location selected, add this code in the appropriate spot: -2. Next, insert the following global function declaration somewhere in the - source file: +#ifdef __AFL_HAVE_MANUAL_CONTROL + __AFL_INIT(); +#endif - void __afl_manual_init(void); +You don't need the #ifdef guards, but they will make the program still work as +usual when compiled with a tool other than afl-clang-fast. - ...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). You - can put this in between #ifdef __AFL_HAVE_MANUAL_INIT to allow the code to - build correctly without afl-clang-fast. - -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 +Finally, recompile the pogram with afl-clang-fast (afl-gcc or afl-clang will +*not* generate a deferred-initialization binary) - and you should be all set! 5) Bonus feature #2: persistent mode ------------------------------------ @@ -141,18 +131,27 @@ so: Some libraries provide APIs that are stateless, or whose state can be reset in between processing different input files. When such a reset is performed, a single long-lived process can be reused to try out multiple test cases, -eliminating the need for repeated fork() calls and the associated OS overhead: +eliminating the need for repeated fork() calls and the associated OS overhead. + +The basic structure of the program that does this would be: + + while (__AFL_LOOP(1000)) { + + /* Read input data. */ + /* Call library code to be fuzzed. */ + /* Reset state. */ + + } - http://lcamtuf.blogspot.com/2015/06/new-in-afl-persistent-mode.html + /* Exit normally */ -With certain fast targets, such an approach can offer dramatic (5x+) -performance gains. The LLVM mode allows you to build such persistent targets -using the template provided in ../experimental/persistent_demo/. +The numerical value specified within the loop controls the maximum number +of iterations before AFL will restart the process from scratch. This minimizes +the impact of memory leaks and similar glitches; 1000 is a good starting point. -To leverage this functionality, you need to set AFL_PERSISTENT before -invoking afl-fuzz, and the target binary needs to incorporate a simple pattern -documented in the aforementioned example file. It also needs to be compiled with -afl-clang-fast; other compiler wrappers will not work. +A more detailed template is shown in ../experimental/persistent_demo/. +Similarly to the previous mode, the feature works only with afl-clang-gast; +#ifdef guards can be used to suppress it when using other compilers. Note that as with the previous mode, the feature is easy to misuse; if you do not reset the critical state fully, you may end up with false positives or @@ -161,7 +160,7 @@ wary of memory leaks. When running in this mode, the execution paths will inherently vary a bit depending on whether the input loop is being entered for the first time or -executed again. To avoid spurious warnings, AFL_PERSISTENT implies +executed again. To avoid spurious warnings, the feature implies AFL_NO_VAR_CHECK and hides the "variable path" warnings in the UI. PS. Because there are task switches still involved, the mode isn't as fast as diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c index c3462979..0a221113 100644 --- a/llvm_mode/afl-clang-fast.c +++ b/llvm_mode/afl-clang-fast.c @@ -99,7 +99,7 @@ static void edit_params(u32 argc, char** argv) { u8 fortify_set = 0, asan_set = 0, x_set = 0, maybe_linking = 1; u8 *name; - cc_params = ck_alloc((argc + 32) * sizeof(u8*)); + cc_params = ck_alloc((argc + 64) * sizeof(u8*)); name = strrchr(argv[0], '/'); if (!name) name = argv[0]; else name++; @@ -176,7 +176,15 @@ static void edit_params(u32 argc, char** argv) { } - cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_INIT=1"; + cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_CONTROL=1"; + + cc_params[cc_par_cnt++] = "-D__AFL_LOOP(_A)=" + "({ static char _B[] __attribute__((used)) = \"" PERSIST_SIG "\"; " + "int __afl_persistent_loop(unsigned int); __afl_persistent_loop(_A); })"; + + cc_params[cc_par_cnt++] = "-D__AFL_INIT()=" + "do { static char _A[] __attribute__((used)) = \"" DEFER_SIG "\"; " + "void __afl_manual_init(void); __afl_manual_init(); } while (0)"; if (maybe_linking) { diff --git a/llvm_mode/afl-llvm-rt.o.c b/llvm_mode/afl-llvm-rt.o.c index fbf2a3d9..ab9151e9 100644 --- a/llvm_mode/afl-llvm-rt.o.c +++ b/llvm_mode/afl-llvm-rt.o.c @@ -42,6 +42,11 @@ u8* __afl_area_ptr = __afl_area_initial; u16 __afl_prev_loc; +/* Running in persistent mode? */ + +static u8 is_persistent; + + /* SHM setup. */ static void __afl_map_shm(void) { @@ -80,7 +85,6 @@ static void __afl_start_forkserver(void) { s32 child_pid; u8 child_stopped = 0; - u8 use_persistent = !!getenv("AFL_PERSISTENT"); /* Phone home and tell the parent that we're OK. If parent isn't there, assume we're not running in forkserver mode and just execute program. */ @@ -136,7 +140,7 @@ static void __afl_start_forkserver(void) { if (write(FORKSRV_FD + 1, &child_pid, 4) != 4) exit(1); - if (waitpid(child_pid, &status, use_persistent ? WUNTRACED : 0) < 0) + if (waitpid(child_pid, &status, is_persistent ? WUNTRACED : 0) < 0) exit(1); /* In persistent mode, the child stops itself with SIGSTOP to indicate @@ -154,7 +158,33 @@ static void __afl_start_forkserver(void) { } -/* This one can be called from user code when AFL_DEFER_FORKSRV is set. */ +/* A simplified persistent mode handler, used as explained in README.llvm. */ + +int __afl_persistent_loop(unsigned int max_cnt) { + + static u8 first_pass = 1; + static u32 cycle_cnt; + + if (first_pass) { + + cycle_cnt = max_cnt; + first_pass = 0; + return 1; + + } + + if (is_persistent && --cycle_cnt) { + + raise(SIGSTOP); + return 1; + + } else return 0; + +} + + +/* This one can be called from user code when deferred forkserver mode + is enabled. */ void __afl_manual_init(void) { @@ -175,7 +205,10 @@ void __afl_manual_init(void) { __attribute__((constructor(0))) void __afl_auto_init(void) { - if (getenv("AFL_DEFER_FORKSRV")) return; + is_persistent = !!getenv(PERSIST_ENV_VAR); + + if (getenv(DEFER_ENV_VAR)) return; + __afl_manual_init(); }