mirror of
https://github.com/AFLplusplus/AFLplusplus.git
synced 2025-06-09 16:51:34 +00:00
1374 lines
37 KiB
C
1374 lines
37 KiB
C
/*
|
|
american fuzzy lop++ - forkserver code
|
|
--------------------------------------
|
|
|
|
Originally written by Michal Zalewski
|
|
|
|
Forkserver design by Jann Horn <jannhorn@googlemail.com>
|
|
|
|
Now maintained by Marc Heuse <mh@mh-sec.de>,
|
|
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
|
Andrea Fioraldi <andreafioraldi@gmail.com> and
|
|
Dominik Maier <mail@dmnk.co>
|
|
|
|
|
|
Copyright 2016, 2017 Google Inc. All rights reserved.
|
|
Copyright 2019-2020 AFLplusplus Project. All rights reserved.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at:
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Shared code that implements a forkserver. This is used by the fuzzer
|
|
as well the other components like afl-tmin.
|
|
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "types.h"
|
|
#include "debug.h"
|
|
#include "common.h"
|
|
#include "list.h"
|
|
#include "forkserver.h"
|
|
#include "hash.h"
|
|
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
#include <sys/time.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/resource.h>
|
|
#include <sys/select.h>
|
|
#include <sys/stat.h>
|
|
|
|
/**
|
|
* The correct fds for reading and writing pipes
|
|
*/
|
|
|
|
/* Describe integer as memory size. */
|
|
|
|
static list_t fsrv_list = {.element_prealloc_count = 0};
|
|
|
|
static void fsrv_exec_child(afl_forkserver_t *fsrv, char **argv) {
|
|
|
|
if (fsrv->qemu_mode) { setenv("AFL_DISABLE_LLVM_INSTRUMENTATION", "1", 0); }
|
|
|
|
execv(fsrv->target_path, argv);
|
|
|
|
WARNF("Execv failed in forkserver.");
|
|
|
|
}
|
|
|
|
/* Initializes the struct */
|
|
|
|
void afl_fsrv_init(afl_forkserver_t *fsrv) {
|
|
|
|
// this structure needs default so we initialize it if this was not done
|
|
// already
|
|
fsrv->out_fd = -1;
|
|
fsrv->out_dir_fd = -1;
|
|
fsrv->dev_null_fd = -1;
|
|
fsrv->dev_urandom_fd = -1;
|
|
|
|
/* Settings */
|
|
fsrv->use_stdin = true;
|
|
fsrv->no_unlink = false;
|
|
fsrv->exec_tmout = EXEC_TIMEOUT;
|
|
fsrv->init_tmout = EXEC_TIMEOUT * FORK_WAIT_MULT;
|
|
fsrv->mem_limit = MEM_LIMIT;
|
|
fsrv->out_file = NULL;
|
|
fsrv->kill_signal = SIGKILL;
|
|
|
|
/* exec related stuff */
|
|
fsrv->child_pid = -1;
|
|
fsrv->map_size = get_map_size();
|
|
fsrv->use_fauxsrv = false;
|
|
fsrv->last_run_timed_out = false;
|
|
fsrv->debug = false;
|
|
fsrv->uses_crash_exitcode = false;
|
|
fsrv->uses_asan = false;
|
|
|
|
fsrv->init_child_func = fsrv_exec_child;
|
|
list_append(&fsrv_list, fsrv);
|
|
|
|
}
|
|
|
|
/* Initialize a new forkserver instance, duplicating "global" settings */
|
|
void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from) {
|
|
|
|
fsrv_to->use_stdin = from->use_stdin;
|
|
fsrv_to->dev_null_fd = from->dev_null_fd;
|
|
fsrv_to->exec_tmout = from->exec_tmout;
|
|
fsrv_to->init_tmout = from->init_tmout;
|
|
fsrv_to->mem_limit = from->mem_limit;
|
|
fsrv_to->map_size = from->map_size;
|
|
fsrv_to->support_shmem_fuzz = from->support_shmem_fuzz;
|
|
fsrv_to->out_file = from->out_file;
|
|
fsrv_to->dev_urandom_fd = from->dev_urandom_fd;
|
|
fsrv_to->out_fd = from->out_fd; // not sure this is a good idea
|
|
fsrv_to->no_unlink = from->no_unlink;
|
|
fsrv_to->uses_crash_exitcode = from->uses_crash_exitcode;
|
|
fsrv_to->crash_exitcode = from->crash_exitcode;
|
|
fsrv_to->kill_signal = from->kill_signal;
|
|
fsrv_to->debug = from->debug;
|
|
|
|
// These are forkserver specific.
|
|
fsrv_to->out_dir_fd = -1;
|
|
fsrv_to->child_pid = -1;
|
|
fsrv_to->use_fauxsrv = 0;
|
|
fsrv_to->last_run_timed_out = 0;
|
|
|
|
fsrv_to->init_child_func = from->init_child_func;
|
|
// Note: do not copy ->add_extra_func or ->persistent_record*
|
|
|
|
list_append(&fsrv_list, fsrv_to);
|
|
|
|
}
|
|
|
|
/* Wrapper for select() and read(), reading a 32 bit var.
|
|
Returns the time passed to read.
|
|
If the wait times out, returns timeout_ms + 1;
|
|
Returns 0 if an error occurred (fd closed, signal, ...); */
|
|
static u32 __attribute__((hot))
|
|
read_s32_timed(s32 fd, s32 *buf, u32 timeout_ms, volatile u8 *stop_soon_p) {
|
|
|
|
fd_set readfds;
|
|
FD_ZERO(&readfds);
|
|
FD_SET(fd, &readfds);
|
|
struct timeval timeout;
|
|
int sret;
|
|
ssize_t len_read;
|
|
|
|
timeout.tv_sec = (timeout_ms / 1000);
|
|
timeout.tv_usec = (timeout_ms % 1000) * 1000;
|
|
#if !defined(__linux__)
|
|
u32 read_start = get_cur_time_us();
|
|
#endif
|
|
|
|
/* set exceptfds as well to return when a child exited/closed the pipe. */
|
|
restart_select:
|
|
sret = select(fd + 1, &readfds, NULL, NULL, &timeout);
|
|
|
|
if (likely(sret > 0)) {
|
|
|
|
restart_read:
|
|
if (*stop_soon_p) {
|
|
|
|
// Early return - the user wants to quit.
|
|
return 0;
|
|
|
|
}
|
|
|
|
len_read = read(fd, (u8 *)buf, 4);
|
|
|
|
if (likely(len_read == 4)) { // for speed we put this first
|
|
|
|
#if defined(__linux__)
|
|
u32 exec_ms = MIN(
|
|
timeout_ms,
|
|
((u64)timeout_ms - (timeout.tv_sec * 1000 + timeout.tv_usec / 1000)));
|
|
#else
|
|
u32 exec_ms = MIN(timeout_ms, (get_cur_time_us() - read_start) / 1000);
|
|
#endif
|
|
|
|
// ensure to report 1 ms has passed (0 is an error)
|
|
return exec_ms > 0 ? exec_ms : 1;
|
|
|
|
} else if (unlikely(len_read == -1 && errno == EINTR)) {
|
|
|
|
goto restart_read;
|
|
|
|
} else if (unlikely(len_read < 4)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} else if (unlikely(!sret)) {
|
|
|
|
*buf = -1;
|
|
return timeout_ms + 1;
|
|
|
|
} else if (unlikely(sret < 0)) {
|
|
|
|
if (likely(errno == EINTR)) goto restart_select;
|
|
|
|
*buf = -1;
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 0; // not reached
|
|
|
|
}
|
|
|
|
/* Internal forkserver for non_instrumented_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) {
|
|
|
|
unsigned char tmp[4] = {0, 0, 0, 0};
|
|
pid_t child_pid;
|
|
|
|
if (!be_quiet) { ACTF("Using Fauxserver:"); }
|
|
|
|
/* 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
|
|
|
|
close(fsrv->out_dir_fd);
|
|
close(fsrv->dev_null_fd);
|
|
close(fsrv->dev_urandom_fd);
|
|
|
|
if (fsrv->plot_file != NULL) {
|
|
|
|
fclose(fsrv->plot_file);
|
|
fsrv->plot_file = NULL;
|
|
|
|
}
|
|
|
|
// enable terminating on sigpipe in the childs
|
|
struct sigaction sa;
|
|
memset((char *)&sa, 0, sizeof(sa));
|
|
sa.sa_handler = SIG_DFL;
|
|
sigaction(SIGPIPE, &sa, NULL);
|
|
|
|
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;
|
|
|
|
WARNF("Execv failed in fauxserver.");
|
|
break;
|
|
|
|
}
|
|
|
|
/* 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!
|
|
WARNF("Fauxserver could not determine child's exit code. ");
|
|
|
|
}
|
|
|
|
/* Relay wait status to AFL pipe, then loop back. */
|
|
|
|
if (write(FORKSRV_FD + 1, &status, 4) != 4) { exit(1); }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Report on the error received via the forkserver controller and exit */
|
|
static void report_error_and_exit(int error) {
|
|
|
|
switch (error) {
|
|
|
|
case FS_ERROR_MAP_SIZE:
|
|
FATAL(
|
|
"AFL_MAP_SIZE is not set and fuzzing target reports that the "
|
|
"required size is very large. Solution: Run the fuzzing target "
|
|
"stand-alone with the environment variable AFL_DEBUG=1 set and set "
|
|
"the value for __afl_final_loc in the AFL_MAP_SIZE environment "
|
|
"variable for afl-fuzz.");
|
|
break;
|
|
case FS_ERROR_MAP_ADDR:
|
|
FATAL(
|
|
"the fuzzing target reports that hardcoded map address might be the "
|
|
"reason the mmap of the shared memory failed. Solution: recompile "
|
|
"the target with either afl-clang-lto and do not set "
|
|
"AFL_LLVM_MAP_ADDR or recompile with afl-clang-fast.");
|
|
break;
|
|
case FS_ERROR_SHM_OPEN:
|
|
FATAL("the fuzzing target reports that the shm_open() call failed.");
|
|
break;
|
|
case FS_ERROR_SHMAT:
|
|
FATAL("the fuzzing target reports that the shmat() call failed.");
|
|
break;
|
|
case FS_ERROR_MMAP:
|
|
FATAL(
|
|
"the fuzzing target reports that the mmap() call to the shared "
|
|
"memory failed.");
|
|
break;
|
|
default:
|
|
FATAL("unknown error code %d from fuzzing target!", error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Spins up fork server. 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 afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
|
|
volatile u8 *stop_soon_p, u8 debug_child_output) {
|
|
|
|
int st_pipe[2], ctl_pipe[2];
|
|
s32 status;
|
|
s32 rlen;
|
|
char *ignore_autodict = getenv("AFL_NO_AUTODICT");
|
|
|
|
if (!be_quiet) { ACTF("Spinning up the fork server..."); }
|
|
|
|
#ifdef AFL_PERSISTENT_RECORD
|
|
if (unlikely(fsrv->persistent_record)) {
|
|
|
|
fsrv->persistent_record_data =
|
|
(u8 **)ck_alloc(fsrv->persistent_record * sizeof(u8 *));
|
|
fsrv->persistent_record_len =
|
|
(u32 *)ck_alloc(fsrv->persistent_record * sizeof(u32));
|
|
|
|
if (!fsrv->persistent_record_data || !fsrv->persistent_record_len) {
|
|
|
|
FATAL("Unable to allocate memory for persistent replay.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (fsrv->use_fauxsrv) {
|
|
|
|
/* TODO: Come up with some nice way to initialize this all */
|
|
|
|
if (fsrv->init_child_func != fsrv_exec_child) {
|
|
|
|
FATAL("Different forkserver not compatible with fauxserver");
|
|
|
|
}
|
|
|
|
fsrv->init_child_func = afl_fauxsrv_execv;
|
|
|
|
}
|
|
|
|
if (pipe(st_pipe) || pipe(ctl_pipe)) { PFATAL("pipe() failed"); }
|
|
|
|
fsrv->last_run_timed_out = 0;
|
|
fsrv->fsrv_pid = fork();
|
|
|
|
if (fsrv->fsrv_pid < 0) { PFATAL("fork() failed"); }
|
|
|
|
if (!fsrv->fsrv_pid) {
|
|
|
|
/* CHILD PROCESS */
|
|
|
|
// enable terminating on sigpipe in the childs
|
|
struct sigaction sa;
|
|
memset((char *)&sa, 0, sizeof(sa));
|
|
sa.sa_handler = SIG_DFL;
|
|
sigaction(SIGPIPE, &sa, NULL);
|
|
|
|
struct rlimit r;
|
|
|
|
if (!fsrv->cmplog_binary && fsrv->qemu_mode == false) {
|
|
|
|
unsetenv(CMPLOG_SHM_ENV_VAR); // we do not want that in non-cmplog fsrv
|
|
|
|
}
|
|
|
|
/* 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 (fsrv->mem_limit) {
|
|
|
|
r.rlim_max = r.rlim_cur = ((rlim_t)fsrv->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 (!(debug_child_output)) {
|
|
|
|
dup2(fsrv->dev_null_fd, 1);
|
|
dup2(fsrv->dev_null_fd, 2);
|
|
|
|
}
|
|
|
|
if (!fsrv->use_stdin) {
|
|
|
|
dup2(fsrv->dev_null_fd, 0);
|
|
|
|
} else {
|
|
|
|
dup2(fsrv->out_fd, 0);
|
|
close(fsrv->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(fsrv->out_dir_fd);
|
|
close(fsrv->dev_null_fd);
|
|
close(fsrv->dev_urandom_fd);
|
|
|
|
if (fsrv->plot_file != NULL) {
|
|
|
|
fclose(fsrv->plot_file);
|
|
fsrv->plot_file = NULL;
|
|
|
|
}
|
|
|
|
/* 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", 1); }
|
|
|
|
/* Set sane defaults for ASAN if nothing else specified. */
|
|
|
|
if (!getenv("ASAN_OPTIONS"))
|
|
setenv("ASAN_OPTIONS",
|
|
"abort_on_error=1:"
|
|
"detect_leaks=0:"
|
|
"malloc_context_size=0:"
|
|
"symbolize=0:"
|
|
"allocator_may_return_null=1:"
|
|
"detect_odr_violation=0:"
|
|
"handle_segv=0:"
|
|
"handle_sigbus=0:"
|
|
"handle_abort=0:"
|
|
"handle_sigfpe=0:"
|
|
"handle_sigill=0",
|
|
1);
|
|
|
|
/* Set sane defaults for UBSAN if nothing else specified. */
|
|
|
|
if (!getenv("UBSAN_OPTIONS"))
|
|
setenv("UBSAN_OPTIONS",
|
|
"halt_on_error=1:"
|
|
"abort_on_error=1:"
|
|
"malloc_context_size=0:"
|
|
"allocator_may_return_null=1:"
|
|
"symbolize=0:"
|
|
"handle_segv=0:"
|
|
"handle_sigbus=0:"
|
|
"handle_abort=0:"
|
|
"handle_sigfpe=0:"
|
|
"handle_sigill=0",
|
|
1);
|
|
|
|
/* Envs for QASan */
|
|
setenv("QASAN_MAX_CALL_STACK", "0", 0);
|
|
setenv("QASAN_SYMBOLIZE", "0", 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. */
|
|
|
|
if (!getenv("MSAN_OPTIONS"))
|
|
setenv("MSAN_OPTIONS",
|
|
"exit_code=" STRINGIFY(MSAN_ERROR) ":"
|
|
"symbolize=0:"
|
|
"abort_on_error=1:"
|
|
"malloc_context_size=0:"
|
|
"allocator_may_return_null=1:"
|
|
"msan_track_origins=0:"
|
|
"handle_segv=0:"
|
|
"handle_sigbus=0:"
|
|
"handle_abort=0:"
|
|
"handle_sigfpe=0:"
|
|
"handle_sigill=0",
|
|
1);
|
|
|
|
fsrv->init_child_func(fsrv, argv);
|
|
|
|
/* Use a distinctive bitmap signature to tell the parent about execv()
|
|
falling through. */
|
|
|
|
*(u32 *)fsrv->trace_bits = EXEC_FAIL_SIG;
|
|
FATAL("Error: execv to target failed\n");
|
|
|
|
}
|
|
|
|
/* PARENT PROCESS */
|
|
|
|
char pid_buf[16];
|
|
sprintf(pid_buf, "%d", fsrv->fsrv_pid);
|
|
if (fsrv->cmplog_binary)
|
|
setenv("__AFL_TARGET_PID2", pid_buf, 1);
|
|
else
|
|
setenv("__AFL_TARGET_PID1", pid_buf, 1);
|
|
|
|
/* Close the unneeded endpoints. */
|
|
|
|
close(ctl_pipe[0]);
|
|
close(st_pipe[1]);
|
|
|
|
fsrv->fsrv_ctl_fd = ctl_pipe[1];
|
|
fsrv->fsrv_st_fd = st_pipe[0];
|
|
|
|
/* Wait for the fork server to come up, but don't wait too long. */
|
|
|
|
rlen = 0;
|
|
if (fsrv->exec_tmout) {
|
|
|
|
u32 time_ms = read_s32_timed(fsrv->fsrv_st_fd, &status, fsrv->init_tmout,
|
|
stop_soon_p);
|
|
|
|
if (!time_ms) {
|
|
|
|
kill(fsrv->fsrv_pid, fsrv->kill_signal);
|
|
|
|
} else if (time_ms > fsrv->init_tmout) {
|
|
|
|
fsrv->last_run_timed_out = 1;
|
|
kill(fsrv->fsrv_pid, fsrv->kill_signal);
|
|
|
|
} else {
|
|
|
|
rlen = 4;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
rlen = read(fsrv->fsrv_st_fd, &status, 4);
|
|
|
|
}
|
|
|
|
/* 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) {
|
|
|
|
if (!be_quiet) { OKF("All right - fork server is up."); }
|
|
|
|
if (getenv("AFL_DEBUG")) {
|
|
|
|
ACTF("Extended forkserver functions received (%08x).", status);
|
|
|
|
}
|
|
|
|
if ((status & FS_OPT_ERROR) == FS_OPT_ERROR)
|
|
report_error_and_exit(FS_OPT_GET_ERROR(status));
|
|
|
|
if ((status & FS_OPT_ENABLED) == FS_OPT_ENABLED) {
|
|
|
|
// workaround for recent afl++ versions
|
|
if ((status & FS_OPT_OLD_AFLPP_WORKAROUND) == FS_OPT_OLD_AFLPP_WORKAROUND)
|
|
status = (status & 0xf0ffffff);
|
|
|
|
if ((status & FS_OPT_SNAPSHOT) == FS_OPT_SNAPSHOT) {
|
|
|
|
fsrv->snapshot = 1;
|
|
if (!be_quiet) { ACTF("Using SNAPSHOT feature."); }
|
|
|
|
}
|
|
|
|
if ((status & FS_OPT_SHDMEM_FUZZ) == FS_OPT_SHDMEM_FUZZ) {
|
|
|
|
if (fsrv->support_shmem_fuzz) {
|
|
|
|
fsrv->use_shmem_fuzz = 1;
|
|
if (!be_quiet) { ACTF("Using SHARED MEMORY FUZZING feature."); }
|
|
|
|
if ((status & FS_OPT_AUTODICT) == 0 || ignore_autodict) {
|
|
|
|
u32 send_status = (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ);
|
|
if (write(fsrv->fsrv_ctl_fd, &send_status, 4) != 4) {
|
|
|
|
FATAL("Writing to forkserver failed.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
FATAL(
|
|
"Target requested sharedmem fuzzing, but we failed to enable "
|
|
"it.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ((status & FS_OPT_MAPSIZE) == FS_OPT_MAPSIZE) {
|
|
|
|
u32 tmp_map_size = FS_OPT_GET_MAPSIZE(status);
|
|
|
|
if (!fsrv->map_size) { fsrv->map_size = MAP_SIZE; }
|
|
|
|
if (unlikely(tmp_map_size % 64)) {
|
|
|
|
// should not happen
|
|
WARNF("Target reported non-aligned map size of %u", tmp_map_size);
|
|
tmp_map_size = (((tmp_map_size + 63) >> 6) << 6);
|
|
|
|
}
|
|
|
|
if (!be_quiet) { ACTF("Target map size: %u", tmp_map_size); }
|
|
if (tmp_map_size > fsrv->map_size) {
|
|
|
|
FATAL(
|
|
"Target's coverage map size of %u is larger than the one this "
|
|
"afl++ is set with (%u). Either set AFL_MAP_SIZE=%u and restart "
|
|
" afl-fuzz, or change MAP_SIZE_POW2 in config.h and recompile "
|
|
"afl-fuzz",
|
|
tmp_map_size, fsrv->map_size, tmp_map_size);
|
|
|
|
}
|
|
|
|
fsrv->map_size = tmp_map_size;
|
|
|
|
}
|
|
|
|
if ((status & FS_OPT_AUTODICT) == FS_OPT_AUTODICT) {
|
|
|
|
if (!ignore_autodict) {
|
|
|
|
if (fsrv->add_extra_func == NULL || fsrv->afl_ptr == NULL) {
|
|
|
|
// this is not afl-fuzz - or it is cmplog - we deny and return
|
|
if (fsrv->use_shmem_fuzz) {
|
|
|
|
status = (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ);
|
|
|
|
} else {
|
|
|
|
status = (FS_OPT_ENABLED);
|
|
|
|
}
|
|
|
|
if (write(fsrv->fsrv_ctl_fd, &status, 4) != 4) {
|
|
|
|
FATAL("Writing to forkserver failed.");
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!be_quiet) { ACTF("Using AUTODICT feature."); }
|
|
|
|
if (fsrv->use_shmem_fuzz) {
|
|
|
|
status = (FS_OPT_ENABLED | FS_OPT_AUTODICT | FS_OPT_SHDMEM_FUZZ);
|
|
|
|
} else {
|
|
|
|
status = (FS_OPT_ENABLED | FS_OPT_AUTODICT);
|
|
|
|
}
|
|
|
|
if (write(fsrv->fsrv_ctl_fd, &status, 4) != 4) {
|
|
|
|
FATAL("Writing to forkserver failed.");
|
|
|
|
}
|
|
|
|
if (read(fsrv->fsrv_st_fd, &status, 4) != 4) {
|
|
|
|
FATAL("Reading from forkserver failed.");
|
|
|
|
}
|
|
|
|
if (status < 2 || (u32)status > 0xffffff) {
|
|
|
|
FATAL("Dictionary has an illegal size: %d", status);
|
|
|
|
}
|
|
|
|
u32 offset = 0, count = 0;
|
|
u32 len = status;
|
|
u8 *dict = ck_alloc(len);
|
|
if (dict == NULL) {
|
|
|
|
FATAL("Could not allocate %u bytes of autodictionary memory", len);
|
|
|
|
}
|
|
|
|
while (len != 0) {
|
|
|
|
rlen = read(fsrv->fsrv_st_fd, dict + offset, len);
|
|
if (rlen > 0) {
|
|
|
|
len -= rlen;
|
|
offset += rlen;
|
|
|
|
} else {
|
|
|
|
FATAL(
|
|
"Reading autodictionary fail at position %u with %u bytes "
|
|
"left.",
|
|
offset, len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
offset = 0;
|
|
while (offset < (u32)status &&
|
|
(u8)dict[offset] + offset < (u32)status) {
|
|
|
|
fsrv->add_extra_func(fsrv->afl_ptr, dict + offset + 1,
|
|
(u8)dict[offset]);
|
|
offset += (1 + dict[offset]);
|
|
count++;
|
|
|
|
}
|
|
|
|
if (!be_quiet) { ACTF("Loaded %u autodictionary entries", count); }
|
|
ck_free(dict);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (fsrv->last_run_timed_out) {
|
|
|
|
FATAL("Timeout while initializing fork server (adjusting -t may help)");
|
|
|
|
}
|
|
|
|
if (waitpid(fsrv->fsrv_pid, &status, 0) <= 0) { PFATAL("waitpid() failed"); }
|
|
|
|
if (WIFSIGNALED(status)) {
|
|
|
|
if (fsrv->mem_limit && fsrv->mem_limit < 500 && fsrv->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.md for help and run with '-m 0'.\n",
|
|
doc_path);
|
|
|
|
} else if (!fsrv->mem_limit) {
|
|
|
|
SAYF("\n" cLRD "[-] " cRST
|
|
"Whoops, the target binary crashed suddenly, "
|
|
"before receiving any input\n"
|
|
" from the fuzzer! You can try the following:\n\n"
|
|
|
|
" - The target binary crashes because necessary runtime "
|
|
"conditions it needs\n"
|
|
" are not met. Try to:\n"
|
|
" 1. Run again with AFL_DEBUG=1 set and check the output of "
|
|
"the target\n"
|
|
" binary for clues.\n"
|
|
" 2. Run again with AFL_DEBUG=1 and 'ulimit -c unlimited' and "
|
|
"analyze the\n"
|
|
" generated core dump.\n\n"
|
|
|
|
" - Possibly the target requires a huge coverage map and has "
|
|
"CTORS.\n"
|
|
" Retry with setting AFL_MAP_SIZE=10000000.\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 {
|
|
|
|
u8 val_buf[STRINGIFY_VAL_SIZE_MAX];
|
|
|
|
SAYF("\n" cLRD "[-] " cRST
|
|
"Whoops, the target binary crashed suddenly, "
|
|
"before receiving any input\n"
|
|
" from the fuzzer! You can try the following:\n\n"
|
|
|
|
" - The target binary crashes because necessary runtime "
|
|
"conditions it needs\n"
|
|
" are not met. Try to:\n"
|
|
" 1. Run again with AFL_DEBUG=1 set and check the output of "
|
|
"the target\n"
|
|
" binary for clues.\n"
|
|
" 2. Run again with AFL_DEBUG=1 and 'ulimit -c unlimited' and "
|
|
"analyze the\n"
|
|
" generated core dump.\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"
|
|
|
|
MSG_FORK_ON_APPLE
|
|
|
|
" - Possibly the target requires a huge coverage map and has "
|
|
"CTORS.\n"
|
|
" Retry with setting AFL_MAP_SIZE=10000000.\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",
|
|
stringify_mem_size(val_buf, sizeof(val_buf), fsrv->mem_limit << 20),
|
|
fsrv->mem_limit - 1);
|
|
|
|
}
|
|
|
|
FATAL("Fork server crashed with signal %d", WTERMSIG(status));
|
|
|
|
}
|
|
|
|
if (*(u32 *)fsrv->trace_bits == EXEC_FAIL_SIG) {
|
|
|
|
FATAL("Unable to execute target application ('%s')", argv[0]);
|
|
|
|
}
|
|
|
|
if (fsrv->mem_limit && fsrv->mem_limit < 500 && fsrv->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.md for help and run with '-m 0'.\n",
|
|
doc_path);
|
|
|
|
} else if (!fsrv->mem_limit) {
|
|
|
|
SAYF("\n" cLRD "[-] " cRST
|
|
"Hmm, looks like the target binary terminated before we could complete"
|
|
" a\n"
|
|
"handshake with the injected code. You can try the following:\n\n"
|
|
|
|
" - The target binary crashes because necessary runtime conditions "
|
|
"it needs\n"
|
|
" are not met. Try to:\n"
|
|
" 1. Run again with AFL_DEBUG=1 set and check the output of the "
|
|
"target\n"
|
|
" binary for clues.\n"
|
|
" 2. Run again with AFL_DEBUG=1 and 'ulimit -c unlimited' and "
|
|
"analyze the\n"
|
|
" generated core dump.\n\n"
|
|
|
|
" - Possibly the target requires a huge coverage map and has "
|
|
"CTORS.\n"
|
|
" Retry with setting AFL_MAP_SIZE=10000000.\n\n"
|
|
|
|
"Otherwise there is a horrible bug in the fuzzer.\n"
|
|
"Poke <afl-users@googlegroups.com> for troubleshooting tips.\n");
|
|
|
|
} else {
|
|
|
|
u8 val_buf[STRINGIFY_VAL_SIZE_MAX];
|
|
|
|
SAYF(
|
|
"\n" cLRD "[-] " cRST
|
|
"Hmm, looks like the target binary terminated "
|
|
"before we could complete a\n"
|
|
" handshake with the injected code. You can try the following:\n\n"
|
|
|
|
"%s"
|
|
|
|
" - The target binary crashes because necessary runtime conditions "
|
|
"it needs\n"
|
|
" are not met. Try to:\n"
|
|
" 1. Run again with AFL_DEBUG=1 set and check the output of the "
|
|
"target\n"
|
|
" binary for clues.\n"
|
|
" 2. Run again with AFL_DEBUG=1 and 'ulimit -c unlimited' and "
|
|
"analyze the\n"
|
|
" generated core dump.\n\n"
|
|
|
|
" - Possibly the target requires a huge coverage map and has "
|
|
"CTORS.\n"
|
|
" Retry with setting AFL_MAP_SIZE=10000000.\n\n"
|
|
|
|
" - 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"
|
|
|
|
" - The target was compiled with afl-clang-lto and a constructor "
|
|
"was\n"
|
|
" instrumented, recompiling without AFL_LLVM_MAP_ADDR might solve "
|
|
"your \n"
|
|
" problem\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)
|
|
? " - You are using deferred forkserver, but __AFL_INIT() is "
|
|
"never\n"
|
|
" reached before the program terminates.\n\n"
|
|
: "",
|
|
stringify_int(val_buf, sizeof(val_buf), fsrv->mem_limit << 20),
|
|
fsrv->mem_limit - 1);
|
|
|
|
}
|
|
|
|
FATAL("Fork server handshake failed");
|
|
|
|
}
|
|
|
|
/* Stop the forkserver and child */
|
|
|
|
void afl_fsrv_kill(afl_forkserver_t *fsrv) {
|
|
|
|
if (fsrv->child_pid > 0) { kill(fsrv->child_pid, fsrv->kill_signal); }
|
|
if (fsrv->fsrv_pid > 0) {
|
|
|
|
kill(fsrv->fsrv_pid, fsrv->kill_signal);
|
|
if (waitpid(fsrv->fsrv_pid, NULL, 0) <= 0) { WARNF("error waitpid\n"); }
|
|
|
|
}
|
|
|
|
close(fsrv->fsrv_ctl_fd);
|
|
close(fsrv->fsrv_st_fd);
|
|
fsrv->fsrv_pid = -1;
|
|
fsrv->child_pid = -1;
|
|
|
|
}
|
|
|
|
/* Get the map size from the target forkserver */
|
|
|
|
u32 afl_fsrv_get_mapsize(afl_forkserver_t *fsrv, char **argv,
|
|
volatile u8 *stop_soon_p, u8 debug_child_output) {
|
|
|
|
afl_fsrv_start(fsrv, argv, stop_soon_p, debug_child_output);
|
|
return fsrv->map_size;
|
|
|
|
}
|
|
|
|
/* Delete the current testcase and write the buf to the testcase file */
|
|
|
|
void afl_fsrv_write_to_testcase(afl_forkserver_t *fsrv, u8 *buf, size_t len) {
|
|
|
|
#ifdef AFL_PERSISTENT_RECORD
|
|
if (unlikely(fsrv->persistent_record)) {
|
|
|
|
fsrv->persistent_record_len[fsrv->persistent_record_idx] = len;
|
|
fsrv->persistent_record_data[fsrv->persistent_record_idx] = afl_realloc(
|
|
(void **)&fsrv->persistent_record_data[fsrv->persistent_record_idx],
|
|
len);
|
|
|
|
if (unlikely(!fsrv->persistent_record_data[fsrv->persistent_record_idx])) {
|
|
|
|
FATAL("allocating replay memory failed.");
|
|
|
|
}
|
|
|
|
memcpy(fsrv->persistent_record_data[fsrv->persistent_record_idx], buf, len);
|
|
|
|
if (unlikely(++fsrv->persistent_record_idx >= fsrv->persistent_record)) {
|
|
|
|
fsrv->persistent_record_idx = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (likely(fsrv->use_shmem_fuzz && fsrv->shmem_fuzz)) {
|
|
|
|
if (unlikely(len > MAX_FILE)) len = MAX_FILE;
|
|
|
|
*fsrv->shmem_fuzz_len = len;
|
|
memcpy(fsrv->shmem_fuzz, buf, len);
|
|
#ifdef _DEBUG
|
|
if (getenv("AFL_DEBUG")) {
|
|
|
|
fprintf(stderr, "FS crc: %016llx len: %u\n",
|
|
hash64(fsrv->shmem_fuzz, *fsrv->shmem_fuzz_len, 0xa5b35705),
|
|
*fsrv->shmem_fuzz_len);
|
|
fprintf(stderr, "SHM :");
|
|
for (u32 i = 0; i < *fsrv->shmem_fuzz_len; i++)
|
|
fprintf(stderr, "%02x", fsrv->shmem_fuzz[i]);
|
|
fprintf(stderr, "\nORIG:");
|
|
for (u32 i = 0; i < *fsrv->shmem_fuzz_len; i++)
|
|
fprintf(stderr, "%02x", buf[i]);
|
|
fprintf(stderr, "\n");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
s32 fd = fsrv->out_fd;
|
|
|
|
if (!fsrv->use_stdin && fsrv->out_file) {
|
|
|
|
if (unlikely(fsrv->no_unlink)) {
|
|
|
|
fd = open(fsrv->out_file, O_WRONLY | O_CREAT | O_TRUNC,
|
|
DEFAULT_PERMISSION);
|
|
|
|
} else {
|
|
|
|
unlink(fsrv->out_file); /* Ignore errors. */
|
|
fd = open(fsrv->out_file, O_WRONLY | O_CREAT | O_EXCL,
|
|
DEFAULT_PERMISSION);
|
|
|
|
}
|
|
|
|
if (fd < 0) { PFATAL("Unable to create '%s'", fsrv->out_file); }
|
|
|
|
} else if (unlikely(fd <= 0)) {
|
|
|
|
// We should have a (non-stdin) fd at this point, else we got a problem.
|
|
FATAL(
|
|
"Nowhere to write output to (neither out_fd nor out_file set (fd is "
|
|
"%d))",
|
|
fd);
|
|
|
|
} else {
|
|
|
|
lseek(fd, 0, SEEK_SET);
|
|
|
|
}
|
|
|
|
// fprintf(stderr, "WRITE %d %u\n", fd, len);
|
|
ck_write(fd, buf, len, fsrv->out_file);
|
|
|
|
if (fsrv->use_stdin) {
|
|
|
|
if (ftruncate(fd, len)) { PFATAL("ftruncate() failed"); }
|
|
lseek(fd, 0, SEEK_SET);
|
|
|
|
} else {
|
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Execute target application, monitoring for timeouts. Return status
|
|
information. The called program will update afl->fsrv->trace_bits. */
|
|
|
|
fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
|
|
volatile u8 *stop_soon_p) {
|
|
|
|
s32 res;
|
|
u32 exec_ms;
|
|
u32 write_value = fsrv->last_run_timed_out;
|
|
|
|
/* After this memset, fsrv->trace_bits[] are effectively volatile, so we
|
|
must prevent any earlier operations from venturing into that
|
|
territory. */
|
|
|
|
memset(fsrv->trace_bits, 0, fsrv->map_size);
|
|
|
|
MEM_BARRIER();
|
|
|
|
/* we have the fork server (or faux server) up and running
|
|
First, tell it if the previous run timed out. */
|
|
|
|
if ((res = write(fsrv->fsrv_ctl_fd, &write_value, 4)) != 4) {
|
|
|
|
if (*stop_soon_p) { return 0; }
|
|
RPFATAL(res, "Unable to request new process from fork server (OOM?)");
|
|
|
|
}
|
|
|
|
fsrv->last_run_timed_out = 0;
|
|
|
|
if ((res = read(fsrv->fsrv_st_fd, &fsrv->child_pid, 4)) != 4) {
|
|
|
|
if (*stop_soon_p) { return 0; }
|
|
RPFATAL(res, "Unable to request new process from fork server (OOM?)");
|
|
|
|
}
|
|
|
|
#ifdef AFL_PERSISTENT_RECORD
|
|
// end of persistent loop?
|
|
if (unlikely(fsrv->persistent_record &&
|
|
fsrv->persistent_record_pid != fsrv->child_pid)) {
|
|
|
|
fsrv->persistent_record_pid = fsrv->child_pid;
|
|
u32 idx, val;
|
|
if (unlikely(!fsrv->persistent_record_idx))
|
|
idx = fsrv->persistent_record - 1;
|
|
else
|
|
idx = fsrv->persistent_record_idx - 1;
|
|
val = fsrv->persistent_record_len[idx];
|
|
memset((void *)fsrv->persistent_record_len, 0,
|
|
fsrv->persistent_record * sizeof(u32));
|
|
fsrv->persistent_record_len[idx] = val;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (fsrv->child_pid <= 0) {
|
|
|
|
if (*stop_soon_p) { return 0; }
|
|
|
|
if ((fsrv->child_pid & FS_OPT_ERROR) &&
|
|
FS_OPT_GET_ERROR(fsrv->child_pid) == FS_ERROR_SHM_OPEN)
|
|
FATAL(
|
|
"Target reported shared memory access failed (perhaps increase "
|
|
"shared memory available).");
|
|
|
|
FATAL("Fork server is misbehaving (OOM?)");
|
|
|
|
}
|
|
|
|
exec_ms = read_s32_timed(fsrv->fsrv_st_fd, &fsrv->child_status, timeout,
|
|
stop_soon_p);
|
|
|
|
if (exec_ms > timeout) {
|
|
|
|
/* If there was no response from forkserver after timeout seconds,
|
|
we kill the child. The forkserver should inform us afterwards */
|
|
|
|
kill(fsrv->child_pid, fsrv->kill_signal);
|
|
fsrv->last_run_timed_out = 1;
|
|
if (read(fsrv->fsrv_st_fd, &fsrv->child_status, 4) < 4) { exec_ms = 0; }
|
|
|
|
}
|
|
|
|
if (!exec_ms) {
|
|
|
|
if (*stop_soon_p) { 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 %llu.\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",
|
|
fsrv->mem_limit);
|
|
RPFATAL(res, "Unable to communicate with fork server");
|
|
|
|
}
|
|
|
|
if (!WIFSTOPPED(fsrv->child_status)) { fsrv->child_pid = 0; }
|
|
|
|
fsrv->total_execs++;
|
|
|
|
/* Any subsequent operations on fsrv->trace_bits must not be moved by the
|
|
compiler below this point. Past this location, fsrv->trace_bits[]
|
|
behave very normally and do not have to be treated as volatile. */
|
|
|
|
MEM_BARRIER();
|
|
|
|
/* Report outcome to caller. */
|
|
|
|
/* Was the run unsuccessful? */
|
|
if (unlikely(*(u32 *)fsrv->trace_bits == EXEC_FAIL_SIG)) {
|
|
|
|
return FSRV_RUN_ERROR;
|
|
|
|
}
|
|
|
|
/* Did we timeout? */
|
|
if (unlikely(fsrv->last_run_timed_out)) {
|
|
|
|
fsrv->last_kill_signal = fsrv->kill_signal;
|
|
return FSRV_RUN_TMOUT;
|
|
|
|
}
|
|
|
|
/* Did we crash?
|
|
In a normal case, (abort) WIFSIGNALED(child_status) will be set.
|
|
MSAN in uses_asan mode uses a special exit code as it doesn't support
|
|
abort_on_error. On top, a user may specify a custom AFL_CRASH_EXITCODE.
|
|
Handle all three cases here. */
|
|
|
|
if (unlikely(
|
|
/* A normal crash/abort */
|
|
(WIFSIGNALED(fsrv->child_status)) ||
|
|
/* special handling for msan */
|
|
(fsrv->uses_asan && WEXITSTATUS(fsrv->child_status) == MSAN_ERROR) ||
|
|
/* the custom crash_exitcode was returned by the target */
|
|
(fsrv->uses_crash_exitcode &&
|
|
WEXITSTATUS(fsrv->child_status) == fsrv->crash_exitcode))) {
|
|
|
|
#ifdef AFL_PERSISTENT_RECORD
|
|
if (unlikely(fsrv->persistent_record)) {
|
|
|
|
char fn[PATH_MAX];
|
|
u32 i, writecnt = 0;
|
|
for (i = 0; i < fsrv->persistent_record; ++i) {
|
|
|
|
u32 entry = (i + fsrv->persistent_record_idx) % fsrv->persistent_record;
|
|
u8 *data = fsrv->persistent_record_data[entry];
|
|
u32 len = fsrv->persistent_record_len[entry];
|
|
if (likely(len && data)) {
|
|
|
|
snprintf(fn, sizeof(fn), "%s/RECORD:%06u,cnt:%06u",
|
|
fsrv->persistent_record_dir, fsrv->persistent_record_cnt,
|
|
writecnt++);
|
|
int fd = open(fn, O_CREAT | O_TRUNC | O_WRONLY, 0644);
|
|
if (fd >= 0) {
|
|
|
|
ck_write(fd, data, len, fn);
|
|
close(fd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
++fsrv->persistent_record_cnt;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
/* For a proper crash, set last_kill_signal to WTERMSIG, else set it to 0 */
|
|
fsrv->last_kill_signal =
|
|
WIFSIGNALED(fsrv->child_status) ? WTERMSIG(fsrv->child_status) : 0;
|
|
return FSRV_RUN_CRASH;
|
|
|
|
}
|
|
|
|
/* success :) */
|
|
return FSRV_RUN_OK;
|
|
|
|
}
|
|
|
|
void afl_fsrv_killall() {
|
|
|
|
LIST_FOREACH(&fsrv_list, afl_forkserver_t, {
|
|
|
|
afl_fsrv_kill(el);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
void afl_fsrv_deinit(afl_forkserver_t *fsrv) {
|
|
|
|
afl_fsrv_kill(fsrv);
|
|
list_remove(&fsrv_list, fsrv);
|
|
|
|
}
|
|
|