aboutsummaryrefslogtreecommitdiff
path: root/src/afl-forkserver.c
diff options
context:
space:
mode:
authorKhaled Yakdan <yakdan@code-intelligence.de>2019-09-04 23:20:18 +0200
committerKhaled Yakdan <yakdan@code-intelligence.de>2019-09-04 23:20:18 +0200
commitb31dff6beec6a7aa17da6f7f8a2eef198c263ccc (patch)
treec039aeed3572b171c2b7108cd650a0ee53c1b0f6 /src/afl-forkserver.c
parent1b3f9713309d27c49b153f9b3af12d208076e93c (diff)
parentabf61ecc8f1b4ea3de59f818d859139637b29f32 (diff)
downloadafl++-b31dff6beec6a7aa17da6f7f8a2eef198c263ccc.tar.gz
Merge branch 'master-upstream' into custom_mutator_docs
# Conflicts: # afl-fuzz.c
Diffstat (limited to 'src/afl-forkserver.c')
-rw-r--r--src/afl-forkserver.c456
1 files changed, 456 insertions, 0 deletions
diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c
new file mode 100644
index 00000000..f2f3c0f0
--- /dev/null
+++ b/src/afl-forkserver.c
@@ -0,0 +1,456 @@
+/*
+ american fuzzy lop++ - forkserver code
+ --------------------------------------
+
+ Originally written by Michal Zalewski <lcamtuf@google.com>
+
+ Forkserver design by Jann Horn <jannhorn@googlemail.com>
+
+ Now maintained by by Marc Heuse <mh@mh-sec.de>,
+ Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
+ Andrea Fioraldi <andreafioraldi@gmail.com>
+
+ Copyright 2016, 2017 Google Inc. All rights reserved.
+ Copyright 2019 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 "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");
+
+}
+