diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/afl-common.c | 20 | ||||
-rw-r--r-- | src/afl-forkserver.c | 93 | ||||
-rw-r--r-- | src/afl-fuzz-run.c | 279 | ||||
-rw-r--r-- | src/afl-fuzz-stats.c | 14 | ||||
-rw-r--r-- | src/afl-fuzz.c | 2 |
5 files changed, 168 insertions, 240 deletions
diff --git a/src/afl-common.c b/src/afl-common.c index 1bd28ee6..c0f65ace 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -42,15 +42,12 @@ extern u8 be_quiet; void detect_file_args(char **argv, u8 *prog_in, u8 *use_stdin) { u32 i = 0; - u8 cwd[PATH_MAX]; - if (getcwd(cwd, (size_t)sizeof(cwd)) == NULL) { - - PFATAL("getcwd() failed"); - - } + u8 cwd[PATH_MAX]; + if (getcwd(cwd, (size_t)sizeof(cwd)) == NULL) { PFATAL("getcwd() failed"); } /* we are working with libc-heap-allocated argvs. So do not mix them with - * other allocation APIs like ck_alloc. That would disturb the free() calls. */ + * other allocation APIs like ck_alloc. That would disturb the free() calls. + */ while (argv[i]) { u8 *aa_loc = strstr(argv[i], "@@"); @@ -72,10 +69,15 @@ void detect_file_args(char **argv, u8 *prog_in, u8 *use_stdin) { /* Construct a replacement argv value. */ if (prog_in[0] == '/') { + n_arg = alloc_printf("%s%s%s", argv[i], prog_in, aa_loc + 2); + } else { + n_arg = alloc_printf("%s%s/%s%s", argv[i], cwd, prog_in, aa_loc + 2); - } + + } + ck_free(argv[i]); argv[i] = n_arg; @@ -86,7 +88,9 @@ void detect_file_args(char **argv, u8 *prog_in, u8 *use_stdin) { i++; } + /* argvs are automatically freed at exit. */ + } /* duplicate the system argv so that diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 7583b045..c7a3475f 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -43,6 +43,10 @@ #include <sys/resource.h> #include <sys/select.h> +/** + * The correct fds for reading and writing pipes + */ + /* Describe integer as memory size. */ extern u8 *doc_path; @@ -151,10 +155,87 @@ void afl_fsrv_init(afl_forkserver_t *fsrv) { fsrv->child_pid = -1; fsrv->out_dir_fd = -1; + fsrv->use_fauxsrv = 0; + list_append(&fsrv_list, fsrv); } +/* Internal forkserver for dumb_mode=1 and non-forkserver mode runs. + It execvs for each fork, forwarding exit codes and child pids to afl. */ + +static void afl_fauxsrv_execv(afl_forkserver_t *fsrv, char **argv) { + + static unsigned char tmp[4] = {0}; + pid_t child_pid = -1; + + /* 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. */ + + if (write(FORKSRV_FD + 1, tmp, 4) != 4) abort(); // TODO: Abort? + + void (*old_sigchld_handler)(int) = signal(SIGCHLD, SIG_DFL); + + while (1) { + + uint32_t was_killed; + int status; + + /* Wait for parent by reading from the pipe. Exit if read fails. */ + + if (read(FORKSRV_FD, &was_killed, 4) != 4) exit(0); + + /* Create a clone of our process. */ + + child_pid = fork(); + + if (child_pid < 0) PFATAL("Fork failed"); + + /* In child process: close fds, resume execution. */ + + if (!child_pid) { // New child + + signal(SIGCHLD, old_sigchld_handler); + // FORKSRV_FD is for communication with AFL, we don't need it in the + // child. + close(FORKSRV_FD); + close(FORKSRV_FD + 1); + + // TODO: exec... + + execv(fsrv->target_path, argv); + + /* Use a distinctive bitmap signature to tell the parent about execv() + falling through. */ + + *(u32 *)fsrv->trace_bits = EXEC_FAIL_SIG; + + PFATAL("Execv failed in fauxserver."); + + } + + /* In parent process: write PID to AFL. */ + + if (write(FORKSRV_FD + 1, &child_pid, 4) != 4) exit(0); + + /* after child exited, get and relay exit status to parent through waitpid. + */ + + if (waitpid(child_pid, &status, 0) < 0) { + + // Zombie Child could not be collected. Scary! + PFATAL("Fauxserver could not determin child's exit code. "); + + } + + /* Relay wait status to AFL pipe, then loop back. */ + + if (write(FORKSRV_FD + 1, &status, 4) != 4) exit(0); + + } + +} + /* Spins up fork server (instrumented mode only). The idea is explained here: http://lcamtuf.blogspot.com/2014/10/fuzzing-binaries-without-execve.html @@ -170,6 +251,8 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv) { int status; s32 rlen; + if (fsrv->use_fauxsrv) ACTF("Using Fauxserver:"); + if (!getenv("AFL_QUIET")) ACTF("Spinning up the fork server..."); if (pipe(st_pipe) || pipe(ctl_pipe)) PFATAL("pipe() failed"); @@ -284,7 +367,15 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv) { "msan_track_origins=0", 0); - execv(fsrv->target_path, argv); + if (fsrv->use_fauxsrv) { + + afl_fauxsrv_execv(fsrv, argv); + + } else { + + execv(fsrv->target_path, argv); + + } /* Use a distinctive bitmap signature to tell the parent about execv() falling through. */ diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index fa28a4ab..f85b5859 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -39,16 +39,16 @@ void timeout_handle(union sigval timer_data) { u8 run_target(afl_state_t *afl, u32 timeout) { - struct sigevent timer_signal_event; - static timer_t timer; - static struct itimerspec timer_period; - static struct timeval it; - static u32 prev_timed_out = 0; - static u64 exec_ms = 0; + s32 res; + int sret; + + fd_set readfds; + + static struct timeval it; + static u32 prev_timed_out = 0; int status = 0; u32 tb4; - int timer_status; afl->fsrv.child_timed_out = 0; @@ -57,245 +57,77 @@ u8 run_target(afl_state_t *afl, u32 timeout) { territory. */ memset(afl->fsrv.trace_bits, 0, MAP_SIZE); - memset(&timer_signal_event, 0, sizeof(struct sigevent)); - - timer_signal_event.sigev_notify = SIGEV_THREAD; - timer_signal_event.sigev_notify_function = timeout_handle; MEM_BARRIER(); - /* If we're running in "dumb" mode, we can't rely on the fork server - logic compiled into the target program, so we will just keep calling - execve(). There is a bit of code duplication between here and - init_forkserver(), but c'est la vie. */ - - if (afl->dumb_mode == 1 || afl->no_forkserver) { - - afl->fsrv.child_pid = fork(); - - if (afl->fsrv.child_pid < 0) PFATAL("fork() failed"); - - if (!afl->fsrv.child_pid) { - - struct rlimit r; - - if (afl->fsrv.mem_limit) { - - r.rlim_max = r.rlim_cur = ((rlim_t)afl->fsrv.mem_limit) << 20; - -#ifdef RLIMIT_AS - - setrlimit(RLIMIT_AS, &r); /* Ignore errors */ - -#else - - setrlimit(RLIMIT_DATA, &r); /* Ignore errors */ - -#endif /* ^RLIMIT_AS */ - - } - - r.rlim_max = r.rlim_cur = 0; - - setrlimit(RLIMIT_CORE, &r); /* Ignore errors */ - - /* Isolate the process and configure standard descriptors. If - afl->fsrv.out_file is specified, stdin is /dev/null; otherwise, - afl->fsrv.out_fd is cloned instead. */ - - setsid(); - - dup2(afl->fsrv.dev_null_fd, 1); - dup2(afl->fsrv.dev_null_fd, 2); - - if (afl->fsrv.out_file) { - - dup2(afl->fsrv.dev_null_fd, 0); - - } else { - - dup2(afl->fsrv.out_fd, 0); - close(afl->fsrv.out_fd); - - } - - /* On Linux, would be faster to use O_CLOEXEC. Maybe TODO. */ - - close(afl->fsrv.dev_null_fd); - close(afl->fsrv.out_dir_fd); -#ifndef HAVE_ARC4RANDOM - close(afl->fsrv.dev_urandom_fd); -#endif - close(fileno(afl->fsrv.plot_file)); - - /* 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); - - setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":" - "symbolize=0:" - "msan_track_origins=0", 0); - - execv(afl->fsrv.target_path, afl->argv); - - /* Use a distinctive bitmap value to tell the parent about execv() - falling through. */ - - *(u32 *)afl->fsrv.trace_bits = EXEC_FAIL_SIG; - exit(0); - - } - - /* Configure timeout using POSIX timers in dumb-mode, - as requested by user, then wait for child to terminate. - */ - - timer_signal_event.sigev_value.sival_int = afl->fsrv.child_pid; - timer_status = timer_create(CLOCK_MONOTONIC, &timer_signal_event, &timer); + /* we have the fork server (or faux server) up and running, so simply + tell it to have at it, and then read back PID. */ - if (timer_status == -1) { FATAL("Failed to create Timer"); } + if ((res = write(afl->fsrv.fsrv_ctl_fd, &prev_timed_out, 4)) != 4) { - timer_period.it_value.tv_sec = (timeout / 1000); - timer_period.it_value.tv_nsec = (timeout % 1000) * 1000000; - timer_period.it_interval.tv_sec = 0; - timer_period.it_interval.tv_nsec = 0; - - timer_status = timer_settime(timer, 0, &timer_period, NULL); - - if (timer_status == -1) { - - timer_delete(timer); - if (errno == EINVAL) { - - FATAL("Failed to set the timer. The timeout given is invalid."); - - } else { - - FATAL("Failed to set the timer to the given timeout"); - - } - - } - - } else { - - /* In non-dumb mode, we have the fork server up and running, so simply - tell it to have at it, and then read back PID. */ - - int res; - - if ((res = write(afl->fsrv.fsrv_ctl_fd, &prev_timed_out, 4)) != 4) { - - if (afl->stop_soon) return 0; - RPFATAL(res, "Unable to request new process from fork server (OOM?)"); - - } - - if ((res = read(afl->fsrv.fsrv_st_fd, &afl->fsrv.child_pid, 4)) != 4) { - - if (afl->stop_soon) return 0; - RPFATAL(res, "Unable to request new process from fork server (OOM?)"); - - } - - if (afl->fsrv.child_pid <= 0) FATAL("Fork server is misbehaving (OOM?)"); + if (afl->stop_soon) return 0; + RPFATAL(res, "Unable to request new process from fork server (OOM?)"); } - if (afl->dumb_mode == 1 || afl->no_forkserver) { - - if (waitpid(afl->fsrv.child_pid, &status, 0) <= 0) { - - timer_delete(timer); - PFATAL("waitpid() failed"); - - } + if ((res = read(afl->fsrv.fsrv_st_fd, &afl->fsrv.child_pid, 4)) != 4) { - timer_gettime(timer, &timer_period); - exec_ms = (u64)timeout - (timer_period.it_value.tv_sec * 1000 + - timer_period.it_value.tv_nsec / 1000000); - timer_period.it_value.tv_sec = 0; - timer_period.it_value.tv_nsec = 0; - - timer_status = timer_settime(timer, 0, &timer_period, NULL); - - if (timer_status == -1) { - - timer_delete(timer); - FATAL("Failed to reset the timer."); - - } + if (afl->stop_soon) return 0; + RPFATAL(res, "Unable to request new process from fork server (OOM?)"); - timer_delete(timer); - - } else { - - /* In non-dumb mode, use select to monitor the forkserver for timeouts. - */ - - s32 res; - int sret; + } - fd_set readfds; - FD_ZERO(&readfds); - FD_SET(afl->fsrv.fsrv_st_fd, &readfds); - it.tv_sec = ((timeout) / 1000); - it.tv_usec = ((timeout) % 1000) * 1000; + if (afl->fsrv.child_pid <= 0) FATAL("Fork server is misbehaving (OOM?)"); - sret = select(afl->fsrv.fsrv_st_fd + 1, &readfds, NULL, NULL, &it); + /* use select to monitor the forkserver for timeouts. */ - if (sret == 0) { + FD_ZERO(&readfds); + FD_SET(afl->fsrv.fsrv_st_fd, &readfds); + it.tv_sec = ((timeout) / 1000); + it.tv_usec = ((timeout) % 1000) * 1000; - kill(afl->fsrv.child_pid, SIGKILL); + sret = select(afl->fsrv.fsrv_st_fd + 1, &readfds, NULL, NULL, &it); - } else { + if (sret == 0) { - if ((res = read(afl->fsrv.fsrv_st_fd, &status, 4)) != 4) { - - if (afl->stop_soon) return 0; - SAYF( - "\n" cLRD "[-] " cRST - "Unable to communicate with fork server. Some possible reasons:\n\n" - " - You've run out of memory. Use -m to increase the the memory " - "limit\n" - " to something higher than %lld.\n" - " - The binary or one of the libraries it uses manages to " - "create\n" - " threads before the forkserver initializes.\n" - " - The binary, at least in some circumstances, exits in a way " - "that\n" - " also kills the parent process - raise() could be the " - "culprit.\n" - " - If using persistent mode with QEMU, " - "AFL_QEMU_PERSISTENT_ADDR " - "is\n" - " probably not valid (hint: add the base address in case of " - "PIE)" - "\n\n" - "If all else fails you can disable the fork server via " - "AFL_NO_FORKSRV=1.\n", - afl->fsrv.mem_limit); - RPFATAL(res, "Unable to communicate with fork server"); + /* If there was no response from forkserver after timeout seconds, + we kill the child. The forkserver should inform us afterwards */ - } + kill(afl->fsrv.child_pid, SIGKILL); + afl->fsrv.child_timed_out = 1; - } + } - exec_ms = (u64)timeout - (it.tv_sec * 1000 + it.tv_usec / 1000); - it.tv_sec = 0; - it.tv_usec = 0; + if ((res = read(afl->fsrv.fsrv_st_fd, &status, 4)) != 4) { + + if (afl->stop_soon) return 0; + SAYF("\n" cLRD "[-] " cRST + "Unable to communicate with fork server. Some possible reasons:\n\n" + " - You've run out of memory. Use -m to increase the the memory " + "limit\n" + " to something higher than %lld.\n" + " - The binary or one of the libraries it uses manages to " + "create\n" + " threads before the forkserver initializes.\n" + " - The binary, at least in some circumstances, exits in a way " + "that\n" + " also kills the parent process - raise() could be the " + "culprit.\n" + " - If using persistent mode with QEMU, " + "AFL_QEMU_PERSISTENT_ADDR " + "is\n" + " probably not valid (hint: add the base address in case of " + "PIE)" + "\n\n" + "If all else fails you can disable the fork server via " + "AFL_NO_FORKSRV=1.\n", + afl->fsrv.mem_limit); + RPFATAL(res, "Unable to communicate with fork server"); } if (!WIFSTOPPED(status)) afl->fsrv.child_pid = 0; - if (exec_ms >= timeout) { afl->fsrv.child_timed_out = 1; } - ++afl->total_execs; /* Any subsequent operations on afl->fsrv.trace_bits must not be moved by the @@ -494,8 +326,7 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem, /* Make sure the forkserver is up before we do anything, and let's not count its spin-up time toward binary calibration. */ - if (afl->dumb_mode != 1 && !afl->no_forkserver && !afl->fsrv.fsrv_pid) - afl_fsrv_start(&afl->fsrv, afl->argv); + if (!afl->fsrv.fsrv_pid) afl_fsrv_start(&afl->fsrv, afl->argv); if (afl->dumb_mode != 1 && !afl->no_forkserver && !afl->cmplog_fsrv_pid && afl->shm.cmplog_mode) init_cmplog_forkserver(afl); diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index f2f6efb9..e03018a1 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -347,9 +347,9 @@ void show_stats(afl_state_t *afl) { /* Lord, forgive me this. */ - SAYF(SET_G1 bSTG bLT bH bSTOP cCYA + SAYF(SET_G1 bSTG bLT bH bSTOP cCYA " process timing " bSTG bH30 bH5 bH bHB bH bSTOP cCYA - " overall results " bSTG bH2 bH2 bRT "\n"); + " overall results " bSTG bH2 bH2 bRT "\n"); if (afl->dumb_mode) { @@ -429,9 +429,9 @@ void show_stats(afl_state_t *afl) { " uniq hangs : " cRST "%-6s" bSTG bV "\n", DTD(cur_ms, afl->last_hang_time), tmp); - SAYF(bVR bH bSTOP cCYA + SAYF(bVR bH bSTOP cCYA " cycle progress " bSTG bH10 bH5 bH2 bH2 bHB bH bSTOP cCYA - " map coverage " bSTG bH bHT bH20 bH2 bVL "\n"); + " map coverage " bSTG bH bHT bH20 bH2 bVL "\n"); /* This gets funny because we want to print several variable-length variables together, but then cram them into a fixed-width field - so we need to @@ -460,9 +460,9 @@ void show_stats(afl_state_t *afl) { SAYF(bSTOP " count coverage : " cRST "%-21s" bSTG bV "\n", tmp); - SAYF(bVR bH bSTOP cCYA + SAYF(bVR bH bSTOP cCYA " stage progress " bSTG bH10 bH5 bH2 bH2 bX bH bSTOP cCYA - " findings in depth " bSTG bH10 bH5 bH2 bH2 bVL "\n"); + " findings in depth " bSTG bH10 bH5 bH2 bH2 bVL "\n"); sprintf(tmp, "%s (%0.02f%%)", DI(afl->queued_favored), ((double)afl->queued_favored) * 100 / afl->queued_paths); @@ -533,7 +533,7 @@ void show_stats(afl_state_t *afl) { /* Aaaalmost there... hold on! */ - SAYF(bVR bH cCYA bSTOP + SAYF(bVR bH cCYA bSTOP " fuzzing strategy yields " bSTG bH10 bHT bH10 bH5 bHB bH bSTOP cCYA " path geometry " bSTG bH5 bH2 bVL "\n"); diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 6cac5184..63a0af0f 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -785,6 +785,8 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->dumb_mode == 2 && afl->no_forkserver) FATAL("AFL_DUMB_FORKSRV and AFL_NO_FORKSRV are mutually exclusive"); + afl->fsrv.use_fauxsrv = afl->dumb_mode == 1 || afl->no_forkserver; + if (getenv("LD_PRELOAD")) WARNF( "LD_PRELOAD is set, are you sure that is what to you want to do " |