diff options
-rw-r--r-- | Makefile | 23 | ||||
-rw-r--r-- | TODO | 23 | ||||
-rw-r--r-- | afl-analyze.c | 2 | ||||
-rw-r--r-- | afl-common.c | 2 | ||||
-rw-r--r-- | afl-forkserver.c | 401 | ||||
-rw-r--r-- | afl-forkserver.h | 25 | ||||
-rw-r--r-- | afl-fuzz.c | 344 | ||||
-rw-r--r-- | afl-sharedmem.c (renamed from sharedmem.c) | 2 | ||||
-rw-r--r-- | afl-sharedmem.h (renamed from sharedmem.h) | 5 | ||||
-rw-r--r-- | afl-showmap.c | 2 | ||||
-rw-r--r-- | afl-tmin.c | 124 | ||||
-rw-r--r-- | docs/ChangeLog | 5 |
12 files changed, 553 insertions, 405 deletions
diff --git a/Makefile b/Makefile index e6e3af85..3d5059f7 100644 --- a/Makefile +++ b/Makefile @@ -134,20 +134,23 @@ afl-as: afl-as.c afl-as.h $(COMM_HDR) | test_x86 afl-common.o : afl-common.c $(CC) $(CFLAGS) -c afl-common.c -sharedmem.o : sharedmem.c - $(CC) $(CFLAGS) -c sharedmem.c +afl-forkserver.o : afl-forkserver.c + $(CC) $(CFLAGS) -c afl-forkserver.c -afl-fuzz: afl-fuzz.c afl-common.o sharedmem.o $(COMM_HDR) | test_x86 - $(CC) $(CFLAGS) $@.c afl-common.o sharedmem.o -o $@ $(LDFLAGS) $(PYFLAGS) +afl-sharedmem.o : afl-sharedmem.c + $(CC) $(CFLAGS) -c afl-sharedmem.c -afl-showmap: afl-showmap.c afl-common.o sharedmem.o $(COMM_HDR) | test_x86 - $(CC) $(CFLAGS) $@.c afl-common.o sharedmem.o -o $@ $(LDFLAGS) +afl-fuzz: afl-fuzz.c afl-common.o afl-sharedmem.o afl-forkserver.o $(COMM_HDR) | test_x86 + $(CC) $(CFLAGS) $@.c afl-common.o afl-sharedmem.o afl-forkserver.o -o $@ $(LDFLAGS) $(PYFLAGS) -afl-tmin: afl-tmin.c afl-common.o sharedmem.o $(COMM_HDR) | test_x86 - $(CC) $(CFLAGS) $@.c afl-common.o sharedmem.o -o $@ $(LDFLAGS) +afl-showmap: afl-showmap.c afl-common.o afl-sharedmem.o $(COMM_HDR) | test_x86 + $(CC) $(CFLAGS) $@.c afl-common.o afl-sharedmem.o -o $@ $(LDFLAGS) -afl-analyze: afl-analyze.c afl-common.o sharedmem.o $(COMM_HDR) | test_x86 - $(CC) $(CFLAGS) $@.c afl-common.o sharedmem.o -o $@ $(LDFLAGS) +afl-tmin: afl-tmin.c afl-common.o afl-sharedmem.o afl-forkserver.o $(COMM_HDR) | test_x86 + $(CC) $(CFLAGS) $@.c afl-common.o afl-sharedmem.o afl-forkserver.o -o $@ $(LDFLAGS) + +afl-analyze: afl-analyze.c afl-common.o afl-sharedmem.o $(COMM_HDR) | test_x86 + $(CC) $(CFLAGS) $@.c afl-common.o afl-sharedmem.o -o $@ $(LDFLAGS) afl-gotcpu: afl-gotcpu.c $(COMM_HDR) | test_x86 $(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS) diff --git a/TODO b/TODO index 42987cb9..cc075abd 100644 --- a/TODO +++ b/TODO @@ -1,12 +1,24 @@ Roadmap 2.53d: ============== - - indent all the code: .clang-format - - update docs/sister_projects.txt +all: + - indent all the code: .clang-format? + (vh: tried, the variable definion look very ugly then, what to do?) afl-fuzz: - - put mutator, scheduler, forkserver and input channels in individual files - - reuse forkserver for showmap, afl-cmin, etc. + - modularize: forkserver is in a module + others: + mutator - is deeply integrated and would loose performance if split + scheduler - is within this and as the values it operates on are afl + specific it does not make sense to seperate this + input - if we get different input vectors then this would make sense, + e.g. network (which we have seen is super non-performant and using + desock is much faster) + so for the moment we are done? (vh) + +docs/: + - update docs/sister_projects.txt + - doc + example for AFL_CUSTOM_MUTATOR_LIBRARY gcc_plugin: - needs to be rewritten @@ -17,8 +29,9 @@ gcc_plugin: - neverZero qemu_mode: + - update to 4.x - deferred mode with AFL_DEFERRED_QEMU=0xaddress - @andrea - dont we have that already with AFL_ENTRYPOINT? + (vh: @andrea - dont we have that already with AFL_ENTRYPOINT?) unit testing / or large testcase campaign diff --git a/afl-analyze.c b/afl-analyze.c index af93150e..18b7456d 100644 --- a/afl-analyze.c +++ b/afl-analyze.c @@ -26,7 +26,7 @@ #include "debug.h" #include "alloc-inl.h" #include "hash.h" -#include "sharedmem.h" +#include "afl-sharedmem.h" #include "afl-common.h" #include <stdio.h> diff --git a/afl-common.c b/afl-common.c index 5e2d0628..f3bbdfb4 100644 --- a/afl-common.c +++ b/afl-common.c @@ -15,6 +15,8 @@ #ifndef __glibc__ #include <unistd.h> #endif + + void detect_file_args(char** argv, u8* prog_in) { u32 i = 0; diff --git a/afl-forkserver.c b/afl-forkserver.c new file mode 100644 index 00000000..226175e1 --- /dev/null +++ b/afl-forkserver.c @@ -0,0 +1,401 @@ +#include "config.h" +#include "types.h" +#include "debug.h" +#include "afl-forkserver.h" + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <errno.h> +#include <signal.h> +#include <sys/time.h> +#include <sys/wait.h> +#include <sys/resource.h> + +/* a program that includes afl-forkserver needs to define these */ +extern u8 uses_asan; +extern u8 *trace_bits; +extern s32 forksrv_pid, child_pid, fsrv_ctl_fd, fsrv_st_fd; +extern s32 out_fd, out_dir_fd, dev_urandom_fd, dev_null_fd; /* initialize these with -1 */ +extern u32 exec_tmout; +extern u64 mem_limit; +extern u8 *out_file, *target_path, *doc_path; +extern FILE *plot_file; + +/* we need this internally but can be defined and read extern in the main source */ +u8 child_timed_out; + + +/* Describe integer as memory size. */ + +u8* forkserver_DMS(u64 val) { + + static u8 tmp[12][16]; + static u8 cur; + +#define CHK_FORMAT(_divisor, _limit_mult, _fmt, _cast) do { \ + if (val < (_divisor) * (_limit_mult)) { \ + sprintf(tmp[cur], _fmt, ((_cast)val) / (_divisor)); \ + return tmp[cur]; \ + } \ + } while (0) + + + cur = (cur + 1) % 12; + + /* 0-9999 */ + CHK_FORMAT(1, 10000, "%llu B", u64); + + /* 10.0k - 99.9k */ + CHK_FORMAT(1024, 99.95, "%0.01f kB", double); + + /* 100k - 999k */ + CHK_FORMAT(1024, 1000, "%llu kB", u64); + + /* 1.00M - 9.99M */ + CHK_FORMAT(1024 * 1024, 9.995, "%0.02f MB", double); + + /* 10.0M - 99.9M */ + CHK_FORMAT(1024 * 1024, 99.95, "%0.01f MB", double); + + /* 100M - 999M */ + CHK_FORMAT(1024 * 1024, 1000, "%llu MB", u64); + + /* 1.00G - 9.99G */ + CHK_FORMAT(1024LL * 1024 * 1024, 9.995, "%0.02f GB", double); + + /* 10.0G - 99.9G */ + CHK_FORMAT(1024LL * 1024 * 1024, 99.95, "%0.01f GB", double); + + /* 100G - 999G */ + CHK_FORMAT(1024LL * 1024 * 1024, 1000, "%llu GB", u64); + + /* 1.00T - 9.99G */ + CHK_FORMAT(1024LL * 1024 * 1024 * 1024, 9.995, "%0.02f TB", double); + + /* 10.0T - 99.9T */ + CHK_FORMAT(1024LL * 1024 * 1024 * 1024, 99.95, "%0.01f TB", double); + +#undef CHK_FORMAT + + /* 100T+ */ + strcpy(tmp[cur], "infty"); + return tmp[cur]; + +} + + + +/* the timeout handler */ + +void handle_timeout(int sig) { + if (child_pid > 0) { + child_timed_out = 1; + kill(child_pid, SIGKILL); + } else if (child_pid == -1 && forksrv_pid > 0) { + child_timed_out = 1; + kill(forksrv_pid, SIGKILL); + } +} + + +/* Spin up fork server (instrumented mode only). The idea is explained here: + + http://lcamtuf.blogspot.com/2014/10/fuzzing-binaries-without-execve.html + + In essence, the instrumentation allows us to skip execve(), and just keep + cloning a stopped child. So, we just execute once, and then send commands + through a pipe. The other part of this logic is in afl-as.h / llvm_mode */ + +void init_forkserver(char **argv) { + + static struct itimerval it; + int st_pipe[2], ctl_pipe[2]; + int status; + s32 rlen; + + ACTF("Spinning up the fork server..."); + + if (pipe(st_pipe) || pipe(ctl_pipe)) + PFATAL("pipe() failed"); + + child_timed_out = 0; + forksrv_pid = fork(); + + if (forksrv_pid < 0) + PFATAL("fork() failed"); + + if (!forksrv_pid) { + + /* CHILD PROCESS */ + + struct rlimit r; + + /* Umpf. On OpenBSD, the default fd limit for root users is set to + soft 128. Let's try to fix that... */ + + if (!getrlimit(RLIMIT_NOFILE, &r) && r.rlim_cur < FORKSRV_FD + 2) { + r.rlim_cur = FORKSRV_FD + 2; + setrlimit(RLIMIT_NOFILE, &r); /* Ignore errors */ + } + + if (mem_limit) { + r.rlim_max = r.rlim_cur = ((rlim_t)mem_limit) << 20; + +#ifdef RLIMIT_AS + setrlimit(RLIMIT_AS, &r); /* Ignore errors */ +#else + /* This takes care of OpenBSD, which doesn't have RLIMIT_AS, but + according to reliable sources, RLIMIT_DATA covers anonymous + maps - so we should be getting good protection against OOM bugs. */ + + setrlimit(RLIMIT_DATA, &r); /* Ignore errors */ +#endif /* ^RLIMIT_AS */ + } + + /* Dumping cores is slow and can lead to anomalies if SIGKILL is delivered + before the dump is complete. */ + +// r.rlim_max = r.rlim_cur = 0; +// setrlimit(RLIMIT_CORE, &r); /* Ignore errors */ + + /* Isolate the process and configure standard descriptors. If out_file is + specified, stdin is /dev/null; otherwise, out_fd is cloned instead. */ + + setsid(); + + if (!getenv("AFL_DEBUG_CHILD_OUTPUT")) { + dup2(dev_null_fd, 1); + dup2(dev_null_fd, 2); + } + + if (out_file) { + dup2(dev_null_fd, 0); + } else { + dup2(out_fd, 0); + close(out_fd); + } + + /* Set up control and status pipes, close the unneeded original fds. */ + + if (dup2(ctl_pipe[0], FORKSRV_FD) < 0) + PFATAL("dup2() failed"); + if (dup2(st_pipe[1], FORKSRV_FD + 1) < 0) + PFATAL("dup2() failed"); + + close(ctl_pipe[0]); + close(ctl_pipe[1]); + close(st_pipe[0]); + close(st_pipe[1]); + + close(out_dir_fd); + close(dev_null_fd); + close(dev_urandom_fd); + close(plot_file == NULL ? -1 : fileno(plot_file)); + + /* This should improve performance a bit, since it stops the linker from + doing extra work post-fork(). */ + + if (!getenv("LD_BIND_LAZY")) + setenv("LD_BIND_NOW", "1", 0); + + /* Set sane defaults for ASAN if nothing else specified. */ + + setenv("ASAN_OPTIONS", + "abort_on_error=1:" + "detect_leaks=0:" + "symbolize=0:" + "allocator_may_return_null=1", + 0); + + /* MSAN is tricky, because it doesn't support abort_on_error=1 at this + point. So, we do this in a very hacky way. */ + + setenv("MSAN_OPTIONS", + "exit_code=" STRINGIFY(MSAN_ERROR) ":" + "symbolize=0:" + "abort_on_error=1:" + "allocator_may_return_null=1:" + "msan_track_origins=0", + 0); + + execv(target_path, argv); + + /* Use a distinctive bitmap signature to tell the parent about execv() + falling through. */ + + *(u32 *)trace_bits = EXEC_FAIL_SIG; + exit(0); + } + + /* PARENT PROCESS */ + + /* Close the unneeded endpoints. */ + + close(ctl_pipe[0]); + close(st_pipe[1]); + + fsrv_ctl_fd = ctl_pipe[1]; + fsrv_st_fd = st_pipe[0]; + + /* Wait for the fork server to come up, but don't wait too long. */ + + if (exec_tmout) { + it.it_value.tv_sec = ((exec_tmout * FORK_WAIT_MULT) / 1000); + it.it_value.tv_usec = ((exec_tmout * FORK_WAIT_MULT) % 1000) * 1000; + } + + setitimer(ITIMER_REAL, &it, NULL); + + rlen = read(fsrv_st_fd, &status, 4); + + it.it_value.tv_sec = 0; + it.it_value.tv_usec = 0; + + setitimer(ITIMER_REAL, &it, NULL); + + /* If we have a four-byte "hello" message from the server, we're all set. + Otherwise, try to figure out what went wrong. */ + + if (rlen == 4) { + OKF("All right - fork server is up."); + return; + } + + if (child_timed_out) + FATAL("Timeout while initializing fork server (adjusting -t may help)"); + + if (waitpid(forksrv_pid, &status, 0) <= 0) + PFATAL("waitpid() failed"); + + if (WIFSIGNALED(status)) { + + if (mem_limit && mem_limit < 500 && uses_asan) { + + SAYF("\n" cLRD "[-] " cRST "Whoops, the target binary crashed suddenly, " + "before receiving any input\n" + " from the fuzzer! Since it seems to be built with ASAN and you " + "have a\n" + " restrictive memory limit configured, this is expected; please " + "read\n" + " %s/notes_for_asan.txt for help.\n", + doc_path); + + } else if (!mem_limit) { + + SAYF("\n" cLRD "[-] " cRST "Whoops, the target binary crashed suddenly, " + "before receiving any input\n" + " from the fuzzer! There are several probable explanations:\n\n" + + " - The binary is just buggy and explodes entirely on its own. " + "If so, you\n" + " need to fix the underlying problem or find a better " + "replacement.\n\n" + + MSG_FORK_ON_APPLE + + " - Less likely, there is a horrible bug in the fuzzer. If other " + "options\n" + " fail, poke <afl-users@googlegroups.com> for troubleshooting " + "tips.\n"); + + } else { + + SAYF("\n" cLRD "[-] " cRST "Whoops, the target binary crashed suddenly, " + "before receiving any input\n" + " from the fuzzer! There are several probable explanations:\n\n" + + " - The current memory limit (%s) is too restrictive, causing " + "the\n" + " target to hit an OOM condition in the dynamic linker. Try " + "bumping up\n" + " the limit with the -m setting in the command line. A simple " + "way confirm\n" + " this diagnosis would be:\n\n" + + MSG_ULIMIT_USAGE " /path/to/fuzzed_app )\n\n" + + " Tip: you can use http://jwilk.net/software/recidivm to " + "quickly\n" + " estimate the required amount of virtual memory for the " + "binary.\n\n" + + " - The binary is just buggy and explodes entirely on its own. " + "If so, you\n" + " need to fix the underlying problem or find a better " + "replacement.\n\n" + + MSG_FORK_ON_APPLE + + " - Less likely, there is a horrible bug in the fuzzer. If other " + "options\n" + " fail, poke <afl-users@googlegroups.com> for troubleshooting " + "tips.\n", + forkserver_DMS(mem_limit << 20), mem_limit - 1); + } + + FATAL("Fork server crashed with signal %d", WTERMSIG(status)); + } + + if (*(u32 *)trace_bits == EXEC_FAIL_SIG) + FATAL("Unable to execute target application ('%s')", argv[0]); + + if (mem_limit && mem_limit < 500 && uses_asan) { + + SAYF("\n" cLRD "[-] " cRST "Hmm, looks like the target binary terminated " + "before we could complete a\n" + " handshake with the injected code. Since it seems to be built " + "with ASAN and\n" + " you have a restrictive memory limit configured, this is " + "expected; please\n" + " read %s/notes_for_asan.txt for help.\n", + doc_path); + + } else if (!mem_limit) { + + SAYF("\n" cLRD "[-] " cRST "Hmm, looks like the target binary terminated " + "before we could complete a\n" + " handshake with the injected code. Perhaps there is a horrible " + "bug in the\n" + " fuzzer. Poke <afl-users@googlegroups.com> for troubleshooting " + "tips.\n"); + + } else { + + SAYF( + "\n" cLRD "[-] " cRST "Hmm, looks like the target binary terminated " + "before we could complete a\n" + " handshake with the injected code. There are %s probable " + "explanations:\n\n" + + "%s" + " - The current memory limit (%s) is too restrictive, causing an " + "OOM\n" + " fault in the dynamic linker. This can be fixed with the -m " + "option. A\n" + " simple way to confirm the diagnosis may be:\n\n" + + MSG_ULIMIT_USAGE " /path/to/fuzzed_app )\n\n" + + " Tip: you can use http://jwilk.net/software/recidivm to quickly\n" + " estimate the required amount of virtual memory for the " + "binary.\n\n" + + " - Less likely, there is a horrible bug in the fuzzer. If other " + "options\n" + " fail, poke <afl-users@googlegroups.com> for troubleshooting " + "tips.\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" + : "", + forkserver_DMS(mem_limit << 20), mem_limit - 1); + } + + FATAL("Fork server handshake failed"); +} + diff --git a/afl-forkserver.h b/afl-forkserver.h new file mode 100644 index 00000000..fa40d9c6 --- /dev/null +++ b/afl-forkserver.h @@ -0,0 +1,25 @@ +#ifndef __AFL_FORKSERVER_H +#define __AFL_FORKSERVER_H + +void handle_timeout(int sig); +void init_forkserver(char **argv); + +#ifdef __APPLE__ +#define MSG_FORK_ON_APPLE \ + " - On MacOS X, the semantics of fork() syscalls are non-standard and " \ + "may\n" \ + " break afl-fuzz performance optimizations when running " \ + "platform-specific\n" \ + " targets. To fix this, set AFL_NO_FORKSRV=1 in the environment.\n\n" +#else +#define MSG_FORK_ON_APPLE "" +#endif + +#ifdef RLIMIT_AS + #define MSG_ULIMIT_USAGE " ( ulimit -Sv $[%llu << 10];" +#else + #define MSG_ULIMIT_USAGE " ( ulimit -Sd $[%llu << 10];" +#endif /* ^RLIMIT_AS */ + + +#endif diff --git a/afl-fuzz.c b/afl-fuzz.c index 2accde86..ec54cc85 100644 --- a/afl-fuzz.c +++ b/afl-fuzz.c @@ -33,7 +33,8 @@ #include "debug.h" #include "alloc-inl.h" #include "hash.h" -#include "sharedmem.h" +#include "afl-sharedmem.h" +#include "afl-forkserver.h" #include "afl-common.h" #include <stdio.h> @@ -144,7 +145,6 @@ double period_pilot_tmp = 5000.0; int key_lv = 0; EXP_ST u8 *in_dir, /* Input directory with test cases */ - *out_file, /* File to fuzz, if any */ *out_dir, /* Working & output directory */ *tmp_dir , /* Temporary directory for input */ *sync_dir, /* Synchronization directory */ @@ -152,15 +152,16 @@ EXP_ST u8 *in_dir, /* Input directory with test cases */ *power_name, /* Power schedule name */ *use_banner, /* Display banner */ *in_bitmap, /* Input bitmap */ - *doc_path, /* Path to documentation dir */ - *target_path, /* Path to target binary */ *file_extension, /* File extension */ *orig_cmdline; /* Original command line */ + u8 *doc_path, /* Path to documentation dir */ + *target_path, /* Path to target binary */ + *out_file; /* File to fuzz, if any */ -EXP_ST u32 exec_tmout = EXEC_TIMEOUT; /* Configurable exec timeout (ms) */ + u32 exec_tmout = EXEC_TIMEOUT; /* Configurable exec timeout (ms) */ static u32 hang_tmout = EXEC_TIMEOUT; /* Timeout used for hang det (ms) */ -EXP_ST u64 mem_limit = MEM_LIMIT; /* Memory cap for child (MB) */ + u64 mem_limit = MEM_LIMIT; /* Memory cap for child (MB) */ EXP_ST u8 cal_cycles = CAL_CYCLES; /* Calibration cycles defaults */ EXP_ST u8 cal_cycles_long = CAL_CYCLES_LONG; @@ -200,7 +201,6 @@ EXP_ST u8 skip_deterministic, /* Skip deterministic stages? */ timeout_given, /* Specific timeout given? */ not_on_tty, /* stdout is not a tty */ term_too_small, /* terminal dimensions too small */ - uses_asan, /* Target uses ASAN? */ no_forkserver, /* Disable forkserver? */ crash_mode, /* Crash mode! Yeah! */ in_place_resume, /* Attempt in-place resume? */ @@ -217,14 +217,15 @@ EXP_ST u8 skip_deterministic, /* Skip deterministic stages? */ deferred_mode, /* Deferred forkserver mode? */ fixed_seed, /* do not reseed */ fast_cal; /* Try to calibrate faster? */ + u8 uses_asan; /* Target uses ASAN? */ -static s32 out_fd, /* Persistent fd for out_file */ + s32 out_fd, /* Persistent fd for out_file */ dev_urandom_fd = -1, /* Persistent fd for /dev/urandom */ dev_null_fd = -1, /* Persistent fd for /dev/null */ fsrv_ctl_fd, /* Fork server control pipe (write) */ fsrv_st_fd; /* Fork server status pipe (read) */ -static s32 forksrv_pid, /* PID of the fork server */ + s32 forksrv_pid, /* PID of the fork server */ child_pid = -1, /* PID of the fuzzed program */ out_dir_fd = -1; /* FD of the lock file */ @@ -313,7 +314,7 @@ static s32 cpu_aff = -1; /* Selected CPU core */ #endif /* HAVE_AFFINITY */ -static FILE* plot_file; /* Gnuplot output file */ +FILE* plot_file; /* Gnuplot output file */ struct queue_entry { @@ -2308,299 +2309,6 @@ static void destroy_extras(void) { } -/* Spin up fork server (instrumented mode only). The idea is explained here: - - http://lcamtuf.blogspot.com/2014/10/fuzzing-binaries-without-execve.html - - In essence, the instrumentation allows us to skip execve(), and just keep - cloning a stopped child. So, we just execute once, and then send commands - through a pipe. The other part of this logic is in afl-as.h. */ - -EXP_ST void init_forkserver(char** argv) { - - static struct itimerval it; - int st_pipe[2], ctl_pipe[2]; - int status; - s32 rlen; - - ACTF("Spinning up the fork server..."); - - if (pipe(st_pipe) || pipe(ctl_pipe)) PFATAL("pipe() failed"); - - forksrv_pid = fork(); - - if (forksrv_pid < 0) PFATAL("fork() failed"); - - if (!forksrv_pid) { - - /* CHILD PROCESS */ - - struct rlimit r; - - /* Umpf. On OpenBSD, the default fd limit for root users is set to - soft 128. Let's try to fix that... */ - - if (!getrlimit(RLIMIT_NOFILE, &r) && r.rlim_cur < FORKSRV_FD + 2) { - - r.rlim_cur = FORKSRV_FD + 2; - setrlimit(RLIMIT_NOFILE, &r); /* Ignore errors */ - - } - - if (mem_limit) { - - r.rlim_max = r.rlim_cur = ((rlim_t)mem_limit) << 20; - -#ifdef RLIMIT_AS - - setrlimit(RLIMIT_AS, &r); /* Ignore errors */ - -#else - - /* This takes care of OpenBSD, which doesn't have RLIMIT_AS, but - according to reliable sources, RLIMIT_DATA covers anonymous - maps - so we should be getting good protection against OOM bugs. */ - - setrlimit(RLIMIT_DATA, &r); /* Ignore errors */ - -#endif /* ^RLIMIT_AS */ - - - } - - /* Dumping cores is slow and can lead to anomalies if SIGKILL is delivered - before the dump is complete. */ - - r.rlim_max = r.rlim_cur = 0; - - setrlimit(RLIMIT_CORE, &r); /* Ignore errors */ - - /* Isolate the process and configure standard descriptors. If out_file is - specified, stdin is /dev/null; otherwise, out_fd is cloned instead. */ - - setsid(); - - if (!getenv("AFL_DEBUG_CHILD_OUTPUT")) { - dup2(dev_null_fd, 1); - dup2(dev_null_fd, 2); - } - - if (out_file) { - - dup2(dev_null_fd, 0); - - } else { - - dup2(out_fd, 0); - close(out_fd); - - } - - /* Set up control and status pipes, close the unneeded original fds. */ - - if (dup2(ctl_pipe[0], FORKSRV_FD) < 0) PFATAL("dup2() failed"); - if (dup2(st_pipe[1], FORKSRV_FD + 1) < 0) PFATAL("dup2() failed"); - - close(ctl_pipe[0]); - close(ctl_pipe[1]); - close(st_pipe[0]); - close(st_pipe[1]); - - close(out_dir_fd); - close(dev_null_fd); - close(dev_urandom_fd); - close(fileno(plot_file)); - - /* This should improve performance a bit, since it stops the linker from - doing extra work post-fork(). */ - - if (!getenv("LD_BIND_LAZY")) setenv("LD_BIND_NOW", "1", 0); - - /* Set sane defaults for ASAN if nothing else specified. */ - - setenv("ASAN_OPTIONS", "abort_on_error=1:" - "detect_leaks=0:" - "symbolize=0:" - "allocator_may_return_null=1", 0); - - /* MSAN is tricky, because it doesn't support abort_on_error=1 at this - point. So, we do this in a very hacky way. */ - - setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":" - "symbolize=0:" - "abort_on_error=1:" - "allocator_may_return_null=1:" - "msan_track_origins=0", 0); - - execv(target_path, argv); - - /* Use a distinctive bitmap signature to tell the parent about execv() - falling through. */ - - *(u32*)trace_bits = EXEC_FAIL_SIG; - exit(0); - - } - - /* PARENT PROCESS */ - - /* Close the unneeded endpoints. */ - - close(ctl_pipe[0]); - close(st_pipe[1]); - - fsrv_ctl_fd = ctl_pipe[1]; - fsrv_st_fd = st_pipe[0]; - - /* Wait for the fork server to come up, but don't wait too long. */ - - it.it_value.tv_sec = ((exec_tmout * FORK_WAIT_MULT) / 1000); - it.it_value.tv_usec = ((exec_tmout * FORK_WAIT_MULT) % 1000) * 1000; - - setitimer(ITIMER_REAL, &it, NULL); - - rlen = read(fsrv_st_fd, &status, 4); - - it.it_value.tv_sec = 0; - it.it_value.tv_usec = 0; - - setitimer(ITIMER_REAL, &it, NULL); - - /* If we have a four-byte "hello" message from the server, we're all set. - Otherwise, try to figure out what went wrong. */ - - if (rlen == 4) { - OKF("All right - fork server is up."); - return; - } - - if (child_timed_out) - FATAL("Timeout while initializing fork server (adjusting -t may help)"); - - if (waitpid(forksrv_pid, &status, 0) <= 0) - PFATAL("waitpid() failed"); - - if (WIFSIGNALED(status)) { - - if (mem_limit && mem_limit < 500 && uses_asan) { - - SAYF("\n" cLRD "[-] " cRST - "Whoops, the target binary crashed suddenly, before receiving any input\n" - " from the fuzzer! Since it seems to be built with ASAN and you have a\n" - " restrictive memory limit configured, this is expected; please read\n" - " %s/notes_for_asan.txt for help.\n", doc_path); - - } else if (!mem_limit) { - -#ifdef __APPLE__ -#define MSG_FORK_ON_APPLE \ - " - On MacOS X, the semantics of fork() syscalls are non-standard and may\n" \ - " break afl-fuzz performance optimizations when running platform-specific\n" \ - " targets. To fix this, set AFL_NO_FORKSRV=1 in the environment.\n\n" -#else -#define MSG_FORK_ON_APPLE "" -#endif - - SAYF("\n" cLRD "[-] " cRST - "Whoops, the target binary crashed suddenly, before receiving any input\n" - " from the fuzzer! There are several probable explanations:\n\n" - - " - The binary is just buggy and explodes entirely on its own. If so, you\n" - " need to fix the underlying problem or find a better replacement.\n\n" - - MSG_FORK_ON_APPLE - - " - Less likely, there is a horrible bug in the fuzzer. If other options\n" - " fail, poke <afl-users@googlegroups.com> for troubleshooting tips.\n"); - - } else { - -#ifdef RLIMIT_AS -#define MSG_ULIMIT_USAGE \ - " ( ulimit -Sv $[%llu << 10];" -#else -#define MSG_ULIMIT_USAGE \ - " ( ulimit -Sd $[%llu << 10];" -#endif /* ^RLIMIT_AS */ - - SAYF("\n" cLRD "[-] " cRST - "Whoops, the target binary crashed suddenly, before receiving any input\n" - " from the fuzzer! There are several probable explanations:\n\n" - - " - The current memory limit (%s) is too restrictive, causing the\n" - " target to hit an OOM condition in the dynamic linker. Try bumping up\n" - " the limit with the -m setting in the command line. A simple way confirm\n" - " this diagnosis would be:\n\n" - - MSG_ULIMIT_USAGE " /path/to/fuzzed_app )\n\n" - - " Tip: you can use http://jwilk.net/software/recidivm to quickly\n" - " estimate the required amount of virtual memory for the binary.\n\n" - - " - The binary is just buggy and explodes entirely on its own. If so, you\n" - " need to fix the underlying problem or find a better replacement.\n\n" - - MSG_FORK_ON_APPLE - - " - Less likely, there is a horrible bug in the fuzzer. If other options\n" - " fail, poke <afl-users@googlegroups.com> for troubleshooting tips.\n", - DMS(mem_limit << 20), mem_limit - 1); - - } - - - FATAL("Fork server crashed with signal %d", WTERMSIG(status)); - - } - - if (*(u32*)trace_bits == EXEC_FAIL_SIG) - FATAL("Unable to execute target application ('%s')", argv[0]); - - if (mem_limit && mem_limit < 500 && uses_asan) { - - SAYF("\n" cLRD "[-] " cRST - "Hmm, looks like the target binary terminated before we could complete a\n" - " handshake with the injected code. Since it seems to be built with ASAN and\n" - " you have a restrictive memory limit configured, this is expected; please\n" - " read %s/notes_for_asan.txt for help.\n", doc_path); - - } else if (!mem_limit) { - - SAYF("\n" cLRD "[-] " cRST - "Hmm, looks like the target binary terminated before we could complete a\n" - " handshake with the injected code. Perhaps there is a horrible bug in the\n" - " fuzzer. Poke <afl-users@googlegroups.com> for troubleshooting tips.\n"); - - } else { - - SAYF("\n" cLRD "[-] " cRST - "Hmm, looks like the target binary terminated before we could complete a\n" - " handshake with the injected code. There are %s probable explanations:\n\n" - - "%s" - " - The current memory limit (%s) is too restrictive, causing an OOM\n" - " fault in the dynamic linker. This can be fixed with the -m option. A\n" - " simple way to confirm the diagnosis may be:\n\n" - - MSG_ULIMIT_USAGE " /path/to/fuzzed_app )\n\n" - - " Tip: you can use http://jwilk.net/software/recidivm to quickly\n" - " estimate the required amount of virtual memory for the binary.\n\n" - - " - Less likely, there is a horrible bug in the fuzzer. If other options\n" - " fail, poke <afl-users@googlegroups.com> for troubleshooting tips.\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); - - } - - FATAL("Fork server handshake failed"); - -} - /* Execute target application, monitoring for timeouts. Return status information. The called program will update trace_bits[]. */ @@ -5165,6 +4873,12 @@ static u32 calculate_score(struct queue_entry* q) { global average. Multiplier ranges from 0.1x to 3x. Fast inputs are less expensive to fuzz, so we're giving them more air time. */ + // TODO BUG FIXME: is this really a good idea? + // This sounds like looking for lost keys under a street light just because + // the light is better there. + // Longer execution time means longer work on the input, the deeper in + // coverage, the better the fuzzing, right? -mh + if (q->exec_us * 0.1 > avg_exec_us) perf_score = 10; else if (q->exec_us * 0.25 > avg_exec_us) perf_score = 25; else if (q->exec_us * 0.5 > avg_exec_us) perf_score = 50; @@ -5188,15 +4902,11 @@ static u32 calculate_score(struct queue_entry* q) { for a bit longer until they catch up with the rest. */ if (q->handicap >= 4) { - perf_score *= 4; q->handicap -= 4; - } else if (q->handicap) { - perf_score *= 2; --q->handicap; - } /* Final adjustment based on input depth, under the assumption that fuzzing @@ -11041,24 +10751,6 @@ static void handle_skipreq(int sig) { } -/* Handle timeout (SIGALRM). */ - -static void handle_timeout(int sig) { - - if (child_pid > 0) { - - child_timed_out = 1; - kill(child_pid, SIGKILL); - - } else if (child_pid == -1 && forksrv_pid > 0) { - - child_timed_out = 1; - kill(forksrv_pid, SIGKILL); - - } - -} - /* Do a PATH search and find target binary to see that it exists and isn't a shell script - a common and painful mistake. We also check for @@ -12443,9 +12135,7 @@ int main(int argc, char** argv) { #ifdef USE_PYTHON if (init_py()) FATAL("Failed to initialize Python module"); - u8 with_python_support = 1; #else - if (getenv("AFL_PYTHON_MODULE")) FATAL("Your AFL binary was built without Python support"); #endif diff --git a/sharedmem.c b/afl-sharedmem.c index 3fd38444..400a0a46 100644 --- a/sharedmem.c +++ b/afl-sharedmem.c @@ -9,7 +9,7 @@ #include "debug.h" #include "alloc-inl.h" #include "hash.h" -#include "sharedmem.h" +#include "afl-sharedmem.h" #include <stdio.h> #include <unistd.h> diff --git a/sharedmem.h b/afl-sharedmem.h index 53a85fcb..9aa44d0e 100644 --- a/sharedmem.h +++ b/afl-sharedmem.h @@ -1,6 +1,7 @@ -#ifndef __SHAREDMEM_H -#define __SHAREDMEM_H +#ifndef __AFL_SHAREDMEM_H +#define __AFL_SHAREDMEM_H void setup_shm(unsigned char dumb_mode); void remove_shm(void); + #endif diff --git a/afl-showmap.c b/afl-showmap.c index af3b36ee..96b7b5e0 100644 --- a/afl-showmap.c +++ b/afl-showmap.c @@ -28,7 +28,7 @@ #include "debug.h" #include "alloc-inl.h" #include "hash.h" -#include "sharedmem.h" +#include "afl-sharedmem.h" #include "afl-common.h" #include <stdio.h> diff --git a/afl-tmin.c b/afl-tmin.c index 09ce8c62..e83b217d 100644 --- a/afl-tmin.c +++ b/afl-tmin.c @@ -21,12 +21,14 @@ #define AFL_MAIN + #include "config.h" #include "types.h" #include "debug.h" #include "alloc-inl.h" #include "hash.h" -#include "sharedmem.h" +#include "afl-forkserver.h" +#include "afl-sharedmem.h" #include "afl-common.h" #include <stdio.h> @@ -46,22 +48,22 @@ #include <sys/types.h> #include <sys/resource.h> -static s32 forksrv_pid, /* PID of the fork server */ - child_pid; /* PID of the tested program */ +s32 forksrv_pid, /* PID of the fork server */ + child_pid; /* PID of the tested program */ -static s32 fsrv_ctl_fd, /* Fork server control pipe (write) */ - fsrv_st_fd; /* Fork server status pipe (read) */ +s32 fsrv_ctl_fd, /* Fork server control pipe (write) */ + fsrv_st_fd; /* Fork server status pipe (read) */ u8 *trace_bits; /* SHM with instrumentation bitmap */ static u8 *mask_bitmap; /* Mask for trace bits (-B) */ -static u8 *in_file, /* Minimizer input test case */ - *out_file, /* Minimizer output file */ - *prog_in, /* Targeted program input file */ + u8 *in_file, /* Minimizer input test case */ + *output_file, /* Minimizer output file */ + *out_file, /* Targeted program input file */ *target_path, /* Path to target binary */ *doc_path; /* Path to docs */ -static s32 prog_in_fd; /* Persistent fd for prog_in */ + s32 out_fd; /* Persistent fd for out_file */ static u8* in_data; /* Input data for trimming */ @@ -70,12 +72,12 @@ static u32 in_len, /* Input data length */ total_execs, /* Total number of execs */ missed_hangs, /* Misses due to hangs */ missed_crashes, /* Misses due to crashes */ - missed_paths, /* Misses due to exec path diffs */ - exec_tmout = EXEC_TIMEOUT; /* Exec timeout (ms) */ + missed_paths; /* Misses due to exec path diffs */ + u32 exec_tmout = EXEC_TIMEOUT; /* Exec timeout (ms) */ -static u64 mem_limit = MEM_LIMIT; /* Memory limit (MB) */ + u64 mem_limit = MEM_LIMIT; /* Memory limit (MB) */ -static s32 dev_null_fd = -1; /* FD to /dev/null */ + s32 dev_null_fd = -1; /* FD to /dev/null */ static u8 crash_mode, /* Crash-centric mode? */ exit_crash, /* Treat non-zero exit as crash? */ @@ -84,8 +86,19 @@ static u8 crash_mode, /* Crash-centric mode? */ use_stdin = 1; /* Use stdin for program input? */ static volatile u8 - stop_soon, /* Ctrl-C pressed? */ - child_timed_out; /* Child timed out? */ + stop_soon; /* Ctrl-C pressed? */ + +/* + * forkserver section + */ + +/* we only need this to use afl-forkserver */ +FILE *plot_file; +u8 uses_asan; +s32 out_fd = -1, out_dir_fd = -1, dev_urandom_fd = -1; + +/* we import this as we need this information */ +extern u8 child_timed_out; /* Classify tuple counts. This is a slow & naive version, but good enough here. */ @@ -163,7 +176,7 @@ static inline u8 anything_set(void) { /* Get rid of temp files (atexit handler). */ static void at_exit_handler(void) { - if (prog_in) unlink(prog_in); /* Ignore errors */ + if (out_file) unlink(out_file); /* Ignore errors */ } /* Read initial file. */ @@ -214,24 +227,24 @@ static s32 write_to_file(u8* path, u8* mem, u32 len) { } /* Write modified data to file for testing. If use_stdin is clear, the old file - is unlinked and a new one is created. Otherwise, prog_in_fd is rewound and + is unlinked and a new one is created. Otherwise, out_fd is rewound and truncated. */ static void write_to_testcase(void* mem, u32 len) { - s32 fd = prog_in_fd; + s32 fd = out_fd; if (!use_stdin) { - unlink(prog_in); /* Ignore errors. */ + unlink(out_file); /* Ignore errors. */ - fd = open(prog_in, O_WRONLY | O_CREAT | O_EXCL, 0600); + fd = open(out_file, O_WRONLY | O_CREAT | O_EXCL, 0600); - if (fd < 0) PFATAL("Unable to create '%s'", prog_in); + if (fd < 0) PFATAL("Unable to create '%s'", out_file); } else lseek(fd, 0, SEEK_SET); - ck_write(fd, mem, len, prog_in); + ck_write(fd, mem, len, out_file); if (use_stdin) { @@ -245,7 +258,7 @@ static void write_to_testcase(void* mem, u32 len) { /* Handle timeout signal. */ - +/* static void handle_timeout(int sig) { if (child_pid > 0) { @@ -261,8 +274,10 @@ static void handle_timeout(int sig) { } } +*/ /* start the app and it's forkserver */ +/* static void init_forkserver(char **argv) { static struct itimerval it; int st_pipe[2], ctl_pipe[2]; @@ -280,7 +295,7 @@ static void init_forkserver(char **argv) { struct rlimit r; - if (dup2(use_stdin ? prog_in_fd : dev_null_fd, 0) < 0 || + if (dup2(use_stdin ? out_fd : dev_null_fd, 0) < 0 || dup2(dev_null_fd, 1) < 0 || dup2(dev_null_fd, 2) < 0) { @@ -290,7 +305,7 @@ static void init_forkserver(char **argv) { } close(dev_null_fd); - close(prog_in_fd); + close(out_fd); setsid(); @@ -300,20 +315,20 @@ static void init_forkserver(char **argv) { #ifdef RLIMIT_AS - setrlimit(RLIMIT_AS, &r); /* Ignore errors */ + setrlimit(RLIMIT_AS, &r); // Ignore errors #else - setrlimit(RLIMIT_DATA, &r); /* Ignore errors */ + setrlimit(RLIMIT_DATA, &r); // Ignore errors -#endif /* ^RLIMIT_AS */ +#endif // ^RLIMIT_AS } r.rlim_max = r.rlim_cur = 0; - setrlimit(RLIMIT_CORE, &r); /* Ignore errors */ + setrlimit(RLIMIT_CORE, &r); // Ignore errors - /* Set up control and status pipes, close the unneeded original fds. */ + // Set up control and status pipes, close the unneeded original fds. if (dup2(ctl_pipe[0], FORKSRV_FD) < 0) PFATAL("dup2() failed"); if (dup2(st_pipe[1], FORKSRV_FD + 1) < 0) PFATAL("dup2() failed"); @@ -330,7 +345,7 @@ static void init_forkserver(char **argv) { } - /* Close the unneeded endpoints. */ + // Close the unneeded endpoints. close(ctl_pipe[0]); close(st_pipe[1]); @@ -338,7 +353,7 @@ static void init_forkserver(char **argv) { fsrv_ctl_fd = ctl_pipe[1]; fsrv_st_fd = st_pipe[0]; - /* Configure timeout, wait for child, cancel timeout. */ + // Configure timeout, wait for child, cancel timeout. if (exec_tmout) { @@ -356,8 +371,8 @@ static void init_forkserver(char **argv) { it.it_value.tv_usec = 0; setitimer(ITIMER_REAL, &it, NULL); - /* If we have a four-byte "hello" message from the server, we're all set. - Otherwise, try to figure out what went wrong. */ + // If we have a four-byte "hello" message from the server, we're all set. + // Otherwise, try to figure out what went wrong. if (rlen == 4) { ACTF("All right - fork server is up."); @@ -380,7 +395,7 @@ static void init_forkserver(char **argv) { SAYF(cLRD "\n+++ Program killed by signal %u +++\n" cRST, WTERMSIG(status)); } - +*/ /* Execute target application. Returns 0 if the changes are a dud, or 1 if they should be kept. */ @@ -422,11 +437,8 @@ static u8 run_target(char** argv, u8* mem, u32 len, u8 first_run) { /* Configure timeout, wait for child, cancel timeout. */ if (exec_tmout) { - - child_timed_out = 0; - it.it_value.tv_sec = (exec_tmout / 1000); - it.it_value.tv_usec = (exec_tmout % 1000) * 1000; - + it.it_value.tv_sec = (exec_tmout / 1000); + it.it_value.tv_usec = (exec_tmout % 1000) * 1000; } setitimer(ITIMER_REAL, &it, NULL); @@ -458,7 +470,7 @@ static u8 run_target(char** argv, u8* mem, u32 len, u8 first_run) { if (stop_soon) { SAYF(cRST cLRD "\n+++ Minimization aborted by user +++\n" cRST); - close(write_to_file(out_file, in_data, in_len)); + close(write_to_file(output_file, in_data, in_len)); exit(1); } @@ -787,7 +799,7 @@ static void set_up_environment(void) { dev_null_fd = open("/dev/null", O_RDWR); if (dev_null_fd < 0) PFATAL("Unable to open /dev/null"); - if (!prog_in) { + if (!out_file) { u8* use_dir = "."; @@ -798,15 +810,15 @@ static void set_up_environment(void) { } - prog_in = alloc_printf("%s/.afl-tmin-temp-%u", use_dir, getpid()); + out_file = alloc_printf("%s/.afl-tmin-temp-%u", use_dir, getpid()); } - unlink(prog_in); + unlink(out_file); - prog_in_fd = open(prog_in, O_RDWR | O_CREAT | O_EXCL, 0600); + out_fd = open(out_file, O_RDWR | O_CREAT | O_EXCL, 0600); - if (prog_in_fd < 0) PFATAL("Unable to create '%s'", prog_in); + if (out_fd < 0) PFATAL("Unable to create '%s'", out_file); /* Set sane defaults... */ @@ -1067,15 +1079,15 @@ int main(int argc, char** argv) { case 'o': - if (out_file) FATAL("Multiple -o options not supported"); - out_file = optarg; + if (output_file) FATAL("Multiple -o options not supported"); + output_file = optarg; break; case 'f': - if (prog_in) FATAL("Multiple -f options not supported"); + if (out_file) FATAL("Multiple -f options not supported"); use_stdin = 0; - prog_in = optarg; + out_file = optarg; break; case 'e': @@ -1181,7 +1193,7 @@ int main(int argc, char** argv) { } - if (optind == argc || !in_file || !out_file) usage(argv[0]); + if (optind == argc || !in_file || !output_file) usage(argv[0]); setup_shm(0); atexit(at_exit_handler); @@ -1190,7 +1202,7 @@ int main(int argc, char** argv) { set_up_environment(); find_binary(argv[optind]); - detect_file_args(argv + optind, prog_in); + detect_file_args(argv + optind, out_file); if (qemu_mode) use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind); @@ -1229,12 +1241,12 @@ int main(int argc, char** argv) { minimize(use_argv); - ACTF("Writing output to '%s'...", out_file); + ACTF("Writing output to '%s'...", output_file); - unlink(prog_in); - prog_in = NULL; + unlink(out_file); + out_file = NULL; - close(write_to_file(out_file, in_data, in_len)); + close(write_to_file(output_file, in_data, in_len)); OKF("We're done here. Have a nice day!\n"); diff --git a/docs/ChangeLog b/docs/ChangeLog index dfebb68a..ed8e0022 100644 --- a/docs/ChangeLog +++ b/docs/ChangeLog @@ -20,12 +20,13 @@ Version ++2.53d (dev): - llvm 9 is now supported (still needs testing) - fix building qemu on some Ubuntus (thanks to floyd!) - custom mutator by a loaded library is now supported (thanks to kyakdan!) + - more support for *BSD (thanks to devnexen!) + - fix building on *BSD (thanks to tobias.kortkamp for the patch) - fix for a few features to support different map sized than 2^16 - afl-showmap: new option -r now shows the real values in the buckets (stock afl never did), plus shows tuple content summary information now - - fix building on *BSD (thanks to tobias.kortkamp for the patch) + - the forkserver is now in its own C file to be easily integratable - small docu updates - - ... your patch? :) -------------------------- |