aboutsummaryrefslogtreecommitdiff
path: root/src/afl-forkserver.c
diff options
context:
space:
mode:
authorAndrea Fioraldi <andreafioraldi@gmail.com>2019-08-27 13:31:35 +0200
committerAndrea Fioraldi <andreafioraldi@gmail.com>2019-08-27 13:31:35 +0200
commit0e59a591693901ec6a69c7de2e9de2dcca52c101 (patch)
tree6a0a3be742f26e7cf6cbfcc1779a85e29c6d9db1 /src/afl-forkserver.c
parentd3d0682310b840b027083133837bcd9be0638281 (diff)
downloadafl++-0e59a591693901ec6a69c7de2e9de2dcca52c101.tar.gz
include and src folders
Diffstat (limited to 'src/afl-forkserver.c')
-rw-r--r--src/afl-forkserver.c401
1 files changed, 401 insertions, 0 deletions
diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c
new file mode 100644
index 00000000..0051f6b0
--- /dev/null
+++ b/src/afl-forkserver.c
@@ -0,0 +1,401 @@
+#include "config.h"
+#include "types.h"
+#include "debug.h"
+#include "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");
+}
+