diff --git a/compilers/Makefile b/compilers/Makefile new file mode 100644 index 0000000..5305c76 --- /dev/null +++ b/compilers/Makefile @@ -0,0 +1,84 @@ +# +# american fuzzy lop - makefile +# ----------------------------- +# +# Originally written and maintained by Michal Zalewski +# +# Copyright 2013, 2014, 2015, 2016, 2017 Google Inc. 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 +# +# Modified by GRIMM + +COMM_HDR = alloc-inl.h config.h debug.h types.h +PROGS = afl-gcc + + +PREFIX ?= /usr/local +BIN_PATH = $(PREFIX)/bin +HELPER_PATH = $(PREFIX)/lib/afl +DOC_PATH = $(PREFIX)/share/doc/afl + +CFLAGS ?= -O3 -funroll-loops +CFLAGS += -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign \ + -DAFL_PATH=\"$(HELPER_PATH)\" -DDOC_PATH=\"$(DOC_PATH)\" \ + -DBIN_PATH=\"$(BIN_PATH)\" + +ifeq "$(findstring clang, $(shell $(CC) --version 2>/dev/null))" "" + TEST_CC = afl-gcc +else + TEST_CC = afl-clang +endif + +all: test_x86 $(PROGS) afl-as test_build all_done + +ifndef AFL_NO_X86 +test_x86: + @echo "[*] Checking for the ability to compile x86 code..." + @echo 'main() { __asm__("xorb %al, %al"); }' | $(CC) -w -x c - -o .test || ( echo; echo "Oops, looks like your compiler can't generate x86 code."; echo; echo "Don't panic! You can use the LLVM or QEMU mode, but see docs/INSTALL first."; echo "(To ignore this error, set AFL_NO_X86=1 and try again.)"; echo; exit 1 ) + @rm -f .test + @echo "[+] Everything seems to be working, ready to compile." +else +test_x86: + @echo "[!] Note: skipping x86 compilation checks (AFL_NO_X86 set)." +endif + + +afl-gcc: afl-gcc.c $(COMM_HDR) | test_x86 + $(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS) + set -e; for i in afl-g++ afl-clang afl-clang++; do ln -sf afl-gcc $$i; done + ln -sf afl-gcc afl-clang + ln -sf afl-gcc afl-clang++ + ln -sf afl-gcc afl-g++ + +afl-as: afl-as.c afl-as.h $(COMM_HDR) | test_x86 + $(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS) + ln -sf afl-as as + +afl-showmap: afl-showmap.c hash.h $(COMM_HDR) | test_x86 + $(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS) + +ifndef AFL_NO_X86 +test_build: test-instr.c afl-gcc afl-as afl-showmap + @echo "[*] Testing the CC wrapper and instrumentation output..." + unset AFL_USE_ASAN AFL_USE_MSAN; AFL_QUIET=1 AFL_INST_RATIO=100 AFL_PATH=. ./$(TEST_CC) $(CFLAGS) test-instr.c -o test-instr $(LDFLAGS) + echo 0 | ./afl-showmap -m none -q -o .test-instr0 ./test-instr + echo 1 | ./afl-showmap -m none -q -o .test-instr1 ./test-instr + @rm -f test-instr + @cmp -s .test-instr0 .test-instr1; DR="$$?"; rm -f .test-instr0 .test-instr1; if [ "$$DR" = "0" ]; then echo; echo "Oops, the instrumentation does not seem to be behaving correctly!"; echo; echo "Please ping to troubleshoot the issue."; echo; exit 1; fi + @echo "[+] All right, the instrumentation seems to be working!" +else +test_build: afl-gcc afl-as afl-showmap + @echo "[!] Note: skipping build tests (you may need to use LLVM or QEMU mode)." +endif + +all_done: test_build + @if [ ! "`which clang 2>/dev/null`" = "" ]; then echo "[+] LLVM users: see llvm_mode/README.llvm for a faster alternative to afl-gcc."; fi + @echo "[+] All done! Be sure to review README - it's pretty short and useful." + @if [ "`uname`" = "Darwin" ]; then printf "\nWARNING: Fuzzing on MacOS X is slow because of the unusually high overhead of\nfork() on this OS. Consider using Linux or *BSD. You can also use VirtualBox\n(virtualbox.org) to put AFL inside a Linux or *BSD VM.\n\n"; fi + @! tty <&1 >/dev/null || printf "\033[0;30mNOTE: If you can read this, your terminal probably uses white background.\nThis will make the UI hard to read. See docs/status_screen.txt for advice.\033[0m\n" 2>/dev/null + diff --git a/compilers/afl-as.c b/compilers/afl-as.c new file mode 100644 index 0000000..b15e651 --- /dev/null +++ b/compilers/afl-as.c @@ -0,0 +1,549 @@ +/* + american fuzzy lop - wrapper for GNU as + --------------------------------------- + + Written and maintained by Michal Zalewski + + Copyright 2013, 2014, 2015 Google Inc. 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 + + The sole purpose of this wrapper is to preprocess assembly files generated + by GCC / clang and inject the instrumentation bits included from afl-as.h. It + is automatically invoked by the toolchain when compiling programs using + afl-gcc / afl-clang. + + Note that it's an explicit non-goal to instrument hand-written assembly, + be it in separate .s files or in __asm__ blocks. The only aspiration this + utility has right now is to be able to skip them gracefully and allow the + compilation process to continue. + + That said, see experimental/clang_asm_normalize/ for a solution that may + allow clang users to make things work even with hand-crafted assembly. Just + note that there is no equivalent for GCC. + + */ + +#define AFL_MAIN + +#include "config.h" +#include "types.h" +#include "debug.h" +#include "alloc-inl.h" + +#include "afl-as.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static u8** as_params; /* Parameters passed to the real 'as' */ + +static u8* input_file; /* Originally specified input file */ +static u8* modified_file; /* Instrumented file for the real 'as' */ + +static u8 be_quiet, /* Quiet mode (no stderr output) */ + clang_mode, /* Running in clang mode? */ + pass_thru, /* Just pass data through? */ + just_version, /* Just show version? */ + sanitizer; /* Using ASAN / MSAN */ + +static u32 inst_ratio = 100, /* Instrumentation probability (%) */ + as_par_cnt = 1; /* Number of params to 'as' */ + +/* If we don't find --32 or --64 in the command line, default to + instrumentation for whichever mode we were compiled with. This is not + perfect, but should do the trick for almost all use cases. */ + +#ifdef __x86_64__ + +static u8 use_64bit = 1; + +#else + +static u8 use_64bit = 0; + +#ifdef __APPLE__ +# error "Sorry, 32-bit Apple platforms are not supported." +#endif /* __APPLE__ */ + +#endif /* ^__x86_64__ */ + + +/* Examine and modify parameters to pass to 'as'. Note that the file name + is always the last parameter passed by GCC, so we exploit this property + to keep the code simple. */ + +static void edit_params(int argc, char** argv) { + + u8 *tmp_dir = getenv("TMPDIR"), *afl_as = getenv("AFL_AS"); + u32 i; + +#ifdef __APPLE__ + + u8 use_clang_as = 0; + + /* On MacOS X, the Xcode cctool 'as' driver is a bit stale and does not work + with the code generated by newer versions of clang that are hand-built + by the user. See the thread here: http://goo.gl/HBWDtn. + + To work around this, when using clang and running without AFL_AS + specified, we will actually call 'clang -c' instead of 'as -q' to + compile the assembly file. + + The tools aren't cmdline-compatible, but at least for now, we can + seemingly get away with this by making only very minor tweaks. Thanks + to Nico Weber for the idea. */ + + if (clang_mode && !afl_as) { + + use_clang_as = 1; + + afl_as = getenv("AFL_CC"); + if (!afl_as) afl_as = getenv("AFL_CXX"); + if (!afl_as) afl_as = "clang"; + + } + +#endif /* __APPLE__ */ + + /* Although this is not documented, GCC also uses TEMP and TMP when TMPDIR + is not set. We need to check these non-standard variables to properly + handle the pass_thru logic later on. */ + + if (!tmp_dir) tmp_dir = getenv("TEMP"); + if (!tmp_dir) tmp_dir = getenv("TMP"); + if (!tmp_dir) tmp_dir = "/tmp"; + + as_params = ck_alloc((argc + 32) * sizeof(u8*)); + + as_params[0] = afl_as ? afl_as : (u8*)"as"; + + as_params[argc] = 0; + + for (i = 1; i < argc - 1; i++) { + + if (!strcmp(argv[i], "--64")) use_64bit = 1; + else if (!strcmp(argv[i], "--32")) use_64bit = 0; + +#ifdef __APPLE__ + + /* The Apple case is a bit different... */ + + if (!strcmp(argv[i], "-arch") && i + 1 < argc) { + + if (!strcmp(argv[i + 1], "x86_64")) use_64bit = 1; + else if (!strcmp(argv[i + 1], "i386")) + FATAL("Sorry, 32-bit Apple platforms are not supported."); + + } + + /* Strip options that set the preference for a particular upstream + assembler in Xcode. */ + + if (clang_mode && (!strcmp(argv[i], "-q") || !strcmp(argv[i], "-Q"))) + continue; + +#endif /* __APPLE__ */ + + as_params[as_par_cnt++] = argv[i]; + + } + +#ifdef __APPLE__ + + /* When calling clang as the upstream assembler, append -c -x assembler + and hope for the best. */ + + if (use_clang_as) { + + as_params[as_par_cnt++] = "-c"; + as_params[as_par_cnt++] = "-x"; + as_params[as_par_cnt++] = "assembler"; + + } + +#endif /* __APPLE__ */ + + input_file = argv[argc - 1]; + + if (input_file[0] == '-') { + + if (!strcmp(input_file + 1, "-version")) { + just_version = 1; + modified_file = input_file; + goto wrap_things_up; + } + + if (input_file[1]) FATAL("Incorrect use (not called through afl-gcc?)"); + else input_file = NULL; + + } else { + + /* Check if this looks like a standard invocation as a part of an attempt + to compile a program, rather than using gcc on an ad-hoc .s file in + a format we may not understand. This works around an issue compiling + NSS. */ + + if (strncmp(input_file, tmp_dir, strlen(tmp_dir)) && + strncmp(input_file, "/var/tmp/", 9) && + strncmp(input_file, "/tmp/", 5)) pass_thru = 1; + + } + + modified_file = alloc_printf("%s/.afl-%u-%u.s", tmp_dir, getpid(), + (u32)time(NULL)); + +wrap_things_up: + + as_params[as_par_cnt++] = modified_file; + as_params[as_par_cnt] = NULL; + +} + + +/* Process input file, generate modified_file. Insert instrumentation in all + the appropriate places. */ + +static void add_instrumentation(void) { + + static u8 line[MAX_LINE]; + + FILE* inf; + FILE* outf; + s32 outfd; + u32 ins_lines = 0; + + u8 instr_ok = 0, skip_csect = 0, skip_next_label = 0, + skip_intel = 0, skip_app = 0, instrument_next = 0; + +#ifdef __APPLE__ + + u8* colon_pos; + +#endif /* __APPLE__ */ + + if (input_file) { + + inf = fopen(input_file, "r"); + if (!inf) PFATAL("Unable to read '%s'", input_file); + + } else inf = stdin; + + outfd = open(modified_file, O_WRONLY | O_EXCL | O_CREAT, 0600); + + if (outfd < 0) PFATAL("Unable to write to '%s'", modified_file); + + outf = fdopen(outfd, "w"); + + if (!outf) PFATAL("fdopen() failed"); + + while (fgets(line, MAX_LINE, inf)) { + + /* In some cases, we want to defer writing the instrumentation trampoline + until after all the labels, macros, comments, etc. If we're in this + mode, and if the line starts with a tab followed by a character, dump + the trampoline now. */ + + if (!pass_thru && !skip_intel && !skip_app && !skip_csect && instr_ok && + instrument_next && line[0] == '\t' && isalpha(line[1])) { + + fprintf(outf, use_64bit ? trampoline_fmt_64 : trampoline_fmt_32, + R(MAP_SIZE)); + + instrument_next = 0; + ins_lines++; + + } + + /* Output the actual line, call it a day in pass-thru mode. */ + + fputs(line, outf); + + if (pass_thru) continue; + + /* All right, this is where the actual fun begins. For one, we only want to + instrument the .text section. So, let's keep track of that in processed + files - and let's set instr_ok accordingly. */ + + if (line[0] == '\t' && line[1] == '.') { + + /* OpenBSD puts jump tables directly inline with the code, which is + a bit annoying. They use a specific format of p2align directives + around them, so we use that as a signal. */ + + if (!clang_mode && instr_ok && !strncmp(line + 2, "p2align ", 8) && + isdigit(line[10]) && line[11] == '\n') skip_next_label = 1; + + if (!strncmp(line + 2, "text\n", 5) || + !strncmp(line + 2, "section\t.text", 13) || + !strncmp(line + 2, "section\t__TEXT,__text", 21) || + !strncmp(line + 2, "section __TEXT,__text", 21)) { + instr_ok = 1; + continue; + } + + if (!strncmp(line + 2, "section\t", 8) || + !strncmp(line + 2, "section ", 8) || + !strncmp(line + 2, "bss\n", 4) || + !strncmp(line + 2, "data\n", 5)) { + instr_ok = 0; + continue; + } + + } + + /* Detect off-flavor assembly (rare, happens in gdb). When this is + encountered, we set skip_csect until the opposite directive is + seen, and we do not instrument. */ + + if (strstr(line, ".code")) { + + if (strstr(line, ".code32")) skip_csect = use_64bit; + if (strstr(line, ".code64")) skip_csect = !use_64bit; + + } + + /* Detect syntax changes, as could happen with hand-written assembly. + Skip Intel blocks, resume instrumentation when back to AT&T. */ + + if (strstr(line, ".intel_syntax")) skip_intel = 1; + if (strstr(line, ".att_syntax")) skip_intel = 0; + + /* Detect and skip ad-hoc __asm__ blocks, likewise skipping them. */ + + if (line[0] == '#' || line[1] == '#') { + + if (strstr(line, "#APP")) skip_app = 1; + if (strstr(line, "#NO_APP")) skip_app = 0; + + } + + /* If we're in the right mood for instrumenting, check for function + names or conditional labels. This is a bit messy, but in essence, + we want to catch: + + ^main: - function entry point (always instrumented) + ^.L0: - GCC branch label + ^.LBB0_0: - clang branch label (but only in clang mode) + ^\tjnz foo - conditional branches + + ...but not: + + ^# BB#0: - clang comments + ^ # BB#0: - ditto + ^.Ltmp0: - clang non-branch labels + ^.LC0 - GCC non-branch labels + ^.LBB0_0: - ditto (when in GCC mode) + ^\tjmp foo - non-conditional jumps + + Additionally, clang and GCC on MacOS X follow a different convention + with no leading dots on labels, hence the weird maze of #ifdefs + later on. + + */ + + if (skip_intel || skip_app || skip_csect || !instr_ok || + line[0] == '#' || line[0] == ' ') continue; + + /* Conditional branch instruction (jnz, etc). We append the instrumentation + right after the branch (to instrument the not-taken path) and at the + branch destination label (handled later on). */ + + if (line[0] == '\t') { + + if (line[1] == 'j' && line[2] != 'm' && R(100) < inst_ratio) { + + fprintf(outf, use_64bit ? trampoline_fmt_64 : trampoline_fmt_32, + R(MAP_SIZE)); + + ins_lines++; + + } + + continue; + + } + + /* Label of some sort. This may be a branch destination, but we need to + tread carefully and account for several different formatting + conventions. */ + +#ifdef __APPLE__ + + /* Apple: L: */ + + if ((colon_pos = strstr(line, ":"))) { + + if (line[0] == 'L' && isdigit(*(colon_pos - 1))) { + +#else + + /* Everybody else: .L: */ + + if (strstr(line, ":")) { + + if (line[0] == '.') { + +#endif /* __APPLE__ */ + + /* .L0: or LBB0_0: style jump destination */ + +#ifdef __APPLE__ + + /* Apple: L / LBB */ + + if ((isdigit(line[1]) || (clang_mode && !strncmp(line, "LBB", 3))) + && R(100) < inst_ratio) { + +#else + + /* Apple: .L / .LBB */ + + if ((isdigit(line[2]) || (clang_mode && !strncmp(line + 1, "LBB", 3))) + && R(100) < inst_ratio) { + +#endif /* __APPLE__ */ + + /* An optimization is possible here by adding the code only if the + label is mentioned in the code in contexts other than call / jmp. + That said, this complicates the code by requiring two-pass + processing (messy with stdin), and results in a speed gain + typically under 10%, because compilers are generally pretty good + about not generating spurious intra-function jumps. + + We use deferred output chiefly to avoid disrupting + .Lfunc_begin0-style exception handling calculations (a problem on + MacOS X). */ + + if (!skip_next_label) instrument_next = 1; else skip_next_label = 0; + + } + + } else { + + /* Function label (always instrumented, deferred mode). */ + + instrument_next = 1; + + } + + } + + } + + if (ins_lines) + fputs(use_64bit ? main_payload_64 : main_payload_32, outf); + + if (input_file) fclose(inf); + fclose(outf); + + if (!be_quiet) { + + if (!ins_lines) WARNF("No instrumentation targets found%s.", + pass_thru ? " (pass-thru mode)" : ""); + else OKF("Instrumented %u locations (%s-bit, %s mode, ratio %u%%).", + ins_lines, use_64bit ? "64" : "32", + getenv("AFL_HARDEN") ? "hardened" : + (sanitizer ? "ASAN/MSAN" : "non-hardened"), + inst_ratio); + + } + +} + + +/* Main entry point */ + +int main(int argc, char** argv) { + + s32 pid; + u32 rand_seed; + int status; + u8* inst_ratio_str = getenv("AFL_INST_RATIO"); + + struct timeval tv; + struct timezone tz; + + clang_mode = !!getenv(CLANG_ENV_VAR); + + if (isatty(2) && !getenv("AFL_QUIET")) { + + SAYF(cCYA "afl-as " cBRI VERSION cRST " by \n"); + + } else be_quiet = 1; + + if (argc < 2) { + + SAYF("\n" + "This is a helper application for afl-fuzz. It is a wrapper around GNU 'as',\n" + "executed by the toolchain whenever using afl-gcc or afl-clang. You probably\n" + "don't want to run this program directly.\n\n" + + "Rarely, when dealing with extremely complex projects, it may be advisable to\n" + "set AFL_INST_RATIO to a value less than 100 in order to reduce the odds of\n" + "instrumenting every discovered branch.\n\n"); + + exit(1); + + } + + gettimeofday(&tv, &tz); + + rand_seed = tv.tv_sec ^ tv.tv_usec ^ getpid(); + + srandom(rand_seed); + + edit_params(argc, argv); + + if (inst_ratio_str) { + + if (sscanf(inst_ratio_str, "%u", &inst_ratio) != 1 || inst_ratio > 100) + FATAL("Bad value of AFL_INST_RATIO (must be between 0 and 100)"); + + } + + if (getenv(AS_LOOP_ENV_VAR)) + FATAL("Endless loop when calling 'as' (remove '.' from your PATH)"); + + setenv(AS_LOOP_ENV_VAR, "1", 1); + + /* When compiling with ASAN, we don't have a particularly elegant way to skip + ASAN-specific branches. But we can probabilistically compensate for + that... */ + + if (getenv("AFL_USE_ASAN") || getenv("AFL_USE_MSAN")) { + sanitizer = 1; + inst_ratio /= 3; + } + + if (!just_version) add_instrumentation(); + + if (!(pid = fork())) { + + execvp(as_params[0], (char**)as_params); + FATAL("Oops, failed to execute '%s' - check your PATH", as_params[0]); + + } + + if (pid < 0) PFATAL("fork() failed"); + + if (waitpid(pid, &status, 0) <= 0) PFATAL("waitpid() failed"); + + if (!getenv("AFL_KEEP_ASSEMBLY")) unlink(modified_file); + + exit(WEXITSTATUS(status)); + +} + diff --git a/compilers/afl-as.h b/compilers/afl-as.h new file mode 100644 index 0000000..ebd5710 --- /dev/null +++ b/compilers/afl-as.h @@ -0,0 +1,719 @@ +/* + american fuzzy lop - injectable parts + ------------------------------------- + + Written and maintained by Michal Zalewski + + Forkserver design by Jann Horn + + Copyright 2013, 2014, 2015 Google Inc. 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 + + This file houses the assembly-level instrumentation injected into fuzzed + programs. The instrumentation stores XORed pairs of data: identifiers of the + currently executing branch and the one that executed immediately before. + + TL;DR: the instrumentation does shm_trace_map[cur_loc ^ prev_loc]++ + + The code is designed for 32-bit and 64-bit x86 systems. Both modes should + work everywhere except for Apple systems. Apple does relocations differently + from everybody else, so since their OSes have been 64-bit for a longer while, + I didn't go through the mental effort of porting the 32-bit code. + + In principle, similar code should be easy to inject into any well-behaved + binary-only code (e.g., using DynamoRIO). Conditional jumps offer natural + targets for instrumentation, and should offer comparable probe density. + + */ + +#ifndef _HAVE_AFL_AS_H +#define _HAVE_AFL_AS_H + +#include "config.h" +#include "types.h" + +/* + ------------------ + Performances notes + ------------------ + + Contributions to make this code faster are appreciated! Here are some + rough notes that may help with the task: + + - Only the trampoline_fmt and the non-setup __afl_maybe_log code paths are + really worth optimizing; the setup / fork server stuff matters a lot less + and should be mostly just kept readable. + + - We're aiming for modern CPUs with out-of-order execution and large + pipelines; the code is mostly follows intuitive, human-readable + instruction ordering, because "textbook" manual reorderings make no + substantial difference. + + - Interestingly, instrumented execution isn't a lot faster if we store a + variable pointer to the setup, log, or return routine and then do a reg + call from within trampoline_fmt. It does speed up non-instrumented + execution quite a bit, though, since that path just becomes + push-call-ret-pop. + + - There is also not a whole lot to be gained by doing SHM attach at a + fixed address instead of retrieving __afl_area_ptr. Although it allows us + to have a shorter log routine inserted for conditional jumps and jump + labels (for a ~10% perf gain), there is a risk of bumping into other + allocations created by the program or by tools such as ASAN. + + - popf is *awfully* slow, which is why we're doing the lahf / sahf + + overflow test trick. Unfortunately, this forces us to taint eax / rax, but + this dependency on a commonly-used register still beats the alternative of + using pushf / popf. + + One possible optimization is to avoid touching flags by using a circular + buffer that stores just a sequence of current locations, with the XOR stuff + happening offline. Alas, this doesn't seem to have a huge impact: + + https://groups.google.com/d/msg/afl-users/MsajVf4fRLo/2u6t88ntUBIJ + + - Preforking one child a bit sooner, and then waiting for the "go" command + from within the child, doesn't offer major performance gains; fork() seems + to be relatively inexpensive these days. Preforking multiple children does + help, but badly breaks the "~1 core per fuzzer" design, making it harder to + scale up. Maybe there is some middle ground. + + Perhaps of note: in the 64-bit version for all platforms except for Apple, + the instrumentation is done slightly differently than on 32-bit, with + __afl_prev_loc and __afl_area_ptr being local to the object file (.lcomm), + rather than global (.comm). This is to avoid GOTRELPC lookups in the critical + code path, which AFAICT, are otherwise unavoidable if we want gcc -shared to + work; simple relocations between .bss and .text won't work on most 64-bit + platforms in such a case. + + (Fun fact: on Apple systems, .lcomm can segfault the linker.) + + The side effect is that state transitions are measured in a somewhat + different way, with previous tuple being recorded separately within the scope + of every .c file. This should have no impact in any practical sense. + + Another side effect of this design is that getenv() will be called once per + every .o file when running in non-instrumented mode; and since getenv() tends + to be optimized in funny ways, we need to be very careful to save every + oddball register it may touch. + + */ + +static const u8* trampoline_fmt_32 = + + "\n" + "/* --- AFL TRAMPOLINE (32-BIT) --- */\n" + "\n" + ".align 4\n" + "\n" + "leal -16(%%esp), %%esp\n" + "movl %%edi, 0(%%esp)\n" + "movl %%edx, 4(%%esp)\n" + "movl %%ecx, 8(%%esp)\n" + "movl %%eax, 12(%%esp)\n" + "movl $0x%08x, %%ecx\n" + "call __afl_maybe_log\n" + "movl 12(%%esp), %%eax\n" + "movl 8(%%esp), %%ecx\n" + "movl 4(%%esp), %%edx\n" + "movl 0(%%esp), %%edi\n" + "leal 16(%%esp), %%esp\n" + "\n" + "/* --- END --- */\n" + "\n"; + +static const u8* trampoline_fmt_64 = + + "\n" + "/* --- AFL TRAMPOLINE (64-BIT) --- */\n" + "\n" + ".align 4\n" + "\n" + "leaq -(128+24)(%%rsp), %%rsp\n" + "movq %%rdx, 0(%%rsp)\n" + "movq %%rcx, 8(%%rsp)\n" + "movq %%rax, 16(%%rsp)\n" + "movq $0x%08x, %%rcx\n" + "call __afl_maybe_log\n" + "movq 16(%%rsp), %%rax\n" + "movq 8(%%rsp), %%rcx\n" + "movq 0(%%rsp), %%rdx\n" + "leaq (128+24)(%%rsp), %%rsp\n" + "\n" + "/* --- END --- */\n" + "\n"; + +static const u8* main_payload_32 = + + "\n" + "/* --- AFL MAIN PAYLOAD (32-BIT) --- */\n" + "\n" + ".text\n" + ".att_syntax\n" + ".code32\n" + ".align 8\n" + "\n" + + "__afl_maybe_log:\n" + "\n" + " lahf\n" + " seto %al\n" + "\n" + " /* Check if SHM region is already mapped. */\n" + "\n" + " movl __afl_area_ptr, %edx\n" + " testl %edx, %edx\n" + " je __afl_setup\n" + "\n" + "__afl_store:\n" + "\n" + " /* Calculate and store hit for the code location specified in ecx. There\n" + " is a double-XOR way of doing this without tainting another register,\n" + " and we use it on 64-bit systems; but it's slower for 32-bit ones. */\n" + "\n" +#ifndef COVERAGE_ONLY + " movl __afl_prev_loc, %edi\n" + " xorl %ecx, %edi\n" + " shrl $1, %ecx\n" + " movl %ecx, __afl_prev_loc\n" +#else + " movl %ecx, %edi\n" +#endif /* ^!COVERAGE_ONLY */ + "\n" +#ifdef SKIP_COUNTS + " orb $1, (%edx, %edi, 1)\n" +#else + " incb (%edx, %edi, 1)\n" +#endif /* ^SKIP_COUNTS */ + "\n" + "__afl_return:\n" + "\n" + " addb $127, %al\n" + " sahf\n" + " ret\n" + "\n" + ".align 8\n" + "\n" + "__afl_setup:\n" + "\n" + " /* Do not retry setup if we had previous failures. */\n" + "\n" + " cmpb $0, __afl_setup_failure\n" + " jne __afl_return\n" + "\n" + " /* Map SHM, jumping to __afl_setup_abort if something goes wrong.\n" + " We do not save FPU/MMX/SSE registers here, but hopefully, nobody\n" + " will notice this early in the game. */\n" + "\n" + " pushl %eax\n" + " pushl %ecx\n" + "\n" + " pushl $.AFL_SHM_ENV\n" + " call getenv\n" + " addl $4, %esp\n" + "\n" + " testl %eax, %eax\n" + " je __afl_setup_abort\n" + "\n" + " pushl %eax\n" + " call atoi\n" + " addl $4, %esp\n" + "\n" + " pushl $0 /* shmat flags */\n" + " pushl $0 /* requested addr */\n" + " pushl %eax /* SHM ID */\n" + " call shmat\n" + " addl $12, %esp\n" + "\n" + " cmpl $-1, %eax\n" + " je __afl_setup_abort\n" + "\n" + " /* Store the address of the SHM region. */\n" + "\n" + " movl %eax, __afl_area_ptr\n" + " movl %eax, %edx\n" + "\n" + " popl %ecx\n" + " popl %eax\n" + "\n" + "__afl_forkserver:\n" + "\n" + " /* Enter the fork server mode to avoid the overhead of execve() calls. */\n" + "\n" + " pushl %eax\n" + " pushl %ecx\n" + " pushl %edx\n" + "\n" + " /* Phone home and tell the parent that we're OK. (Note that signals with\n" + " no SA_RESTART will mess it up). If this fails, assume that the fd is\n" + " closed because we were execve()d from an instrumented binary, or because\n" + " the parent doesn't want to use the fork server. */\n" + "\n" + " pushl $4 /* length */\n" + " pushl $__afl_temp /* data */\n" + " pushl $" STRINGIFY((FORKSRV_FD + 1)) " /* file desc */\n" + " call write\n" + " addl $12, %esp\n" + "\n" + " cmpl $4, %eax\n" + " jne __afl_fork_resume\n" + "\n" + "__afl_fork_wait_loop:\n" + "\n" + " /* Wait for parent by reading from the pipe. Abort if read fails. */\n" + "\n" + " pushl $4 /* length */\n" + " pushl $__afl_temp /* data */\n" + " pushl $" STRINGIFY(FORKSRV_FD) " /* file desc */\n" + " call read\n" + " addl $12, %esp\n" + "\n" + " cmpl $4, %eax\n" + " jne __afl_die\n" + "\n" + " /* Once woken up, create a clone of our process. This is an excellent use\n" + " case for syscall(__NR_clone, 0, CLONE_PARENT), but glibc boneheadedly\n" + " caches getpid() results and offers no way to update the value, breaking\n" + " abort(), raise(), and a bunch of other things :-( */\n" + "\n" + " call fork\n" + "\n" + " cmpl $0, %eax\n" + " jl __afl_die\n" + " je __afl_fork_resume\n" + "\n" + " /* In parent process: write PID to pipe, then wait for child. */\n" + "\n" + " movl %eax, __afl_fork_pid\n" + "\n" + " pushl $4 /* length */\n" + " pushl $__afl_fork_pid /* data */\n" + " pushl $" STRINGIFY((FORKSRV_FD + 1)) " /* file desc */\n" + " call write\n" + " addl $12, %esp\n" + "\n" + " pushl $0 /* no flags */\n" + " pushl $__afl_temp /* status */\n" + " pushl __afl_fork_pid /* PID */\n" + " call waitpid\n" + " addl $12, %esp\n" + "\n" + " cmpl $0, %eax\n" + " jle __afl_die\n" + "\n" + " /* Relay wait status to pipe, then loop back. */\n" + "\n" + " pushl $4 /* length */\n" + " pushl $__afl_temp /* data */\n" + " pushl $" STRINGIFY((FORKSRV_FD + 1)) " /* file desc */\n" + " call write\n" + " addl $12, %esp\n" + "\n" + " jmp __afl_fork_wait_loop\n" + "\n" + "__afl_fork_resume:\n" + "\n" + " /* In child process: close fds, resume execution. */\n" + "\n" + " pushl $" STRINGIFY(FORKSRV_FD) "\n" + " call close\n" + "\n" + " pushl $" STRINGIFY((FORKSRV_FD + 1)) "\n" + " call close\n" + "\n" + " addl $8, %esp\n" + "\n" + " popl %edx\n" + " popl %ecx\n" + " popl %eax\n" + " jmp __afl_store\n" + "\n" + "__afl_die:\n" + "\n" + " xorl %eax, %eax\n" + " call _exit\n" + "\n" + "__afl_setup_abort:\n" + "\n" + " /* Record setup failure so that we don't keep calling\n" + " shmget() / shmat() over and over again. */\n" + "\n" + " incb __afl_setup_failure\n" + " popl %ecx\n" + " popl %eax\n" + " jmp __afl_return\n" + "\n" + ".AFL_VARS:\n" + "\n" + " .comm __afl_area_ptr, 4, 32\n" + " .comm __afl_setup_failure, 1, 32\n" +#ifndef COVERAGE_ONLY + " .comm __afl_prev_loc, 4, 32\n" +#endif /* !COVERAGE_ONLY */ + " .comm __afl_fork_pid, 4, 32\n" + " .comm __afl_temp, 4, 32\n" + "\n" + ".AFL_SHM_ENV:\n" + " .asciz \"" SHM_ENV_VAR "\"\n" + "\n" + "/* --- END --- */\n" + "\n"; + +/* The OpenBSD hack is due to lahf and sahf not being recognized by some + versions of binutils: http://marc.info/?l=openbsd-cvs&m=141636589924400 + + The Apple code is a bit different when calling libc functions because + they are doing relocations differently from everybody else. We also need + to work around the crash issue with .lcomm and the fact that they don't + recognize .string. */ + +#ifdef __APPLE__ +# define CALL_L64(str) "call _" str "\n" +#else +# define CALL_L64(str) "call " str "@PLT\n" +#endif /* ^__APPLE__ */ + +static const u8* main_payload_64 = + + "\n" + "/* --- AFL MAIN PAYLOAD (64-BIT) --- */\n" + "\n" + ".text\n" + ".att_syntax\n" + ".code64\n" + ".align 8\n" + "\n" + "__afl_maybe_log:\n" + "\n" +#if defined(__OpenBSD__) || (defined(__FreeBSD__) && (__FreeBSD__ < 9)) + " .byte 0x9f /* lahf */\n" +#else + " lahf\n" +#endif /* ^__OpenBSD__, etc */ + " seto %al\n" + "\n" + " /* Check if SHM region is already mapped. */\n" + "\n" + " movq __afl_area_ptr(%rip), %rdx\n" + " testq %rdx, %rdx\n" + " je __afl_setup\n" + "\n" + "__afl_store:\n" + "\n" + " /* Calculate and store hit for the code location specified in rcx. */\n" + "\n" +#ifndef COVERAGE_ONLY + " xorq __afl_prev_loc(%rip), %rcx\n" + " xorq %rcx, __afl_prev_loc(%rip)\n" + " shrq $1, __afl_prev_loc(%rip)\n" +#endif /* ^!COVERAGE_ONLY */ + "\n" +#ifdef SKIP_COUNTS + " orb $1, (%rdx, %rcx, 1)\n" +#else + " incb (%rdx, %rcx, 1)\n" +#endif /* ^SKIP_COUNTS */ + "\n" + "__afl_return:\n" + "\n" + " addb $127, %al\n" +#if defined(__OpenBSD__) || (defined(__FreeBSD__) && (__FreeBSD__ < 9)) + " .byte 0x9e /* sahf */\n" +#else + " sahf\n" +#endif /* ^__OpenBSD__, etc */ + " ret\n" + "\n" + ".align 8\n" + "\n" + "__afl_setup:\n" + "\n" + " /* Do not retry setup if we had previous failures. */\n" + "\n" + " cmpb $0, __afl_setup_failure(%rip)\n" + " jne __afl_return\n" + "\n" + " /* Check out if we have a global pointer on file. */\n" + "\n" +#ifndef __APPLE__ + " movq __afl_global_area_ptr@GOTPCREL(%rip), %rdx\n" + " movq (%rdx), %rdx\n" +#else + " movq __afl_global_area_ptr(%rip), %rdx\n" +#endif /* !^__APPLE__ */ + " testq %rdx, %rdx\n" + " je __afl_setup_first\n" + "\n" + " movq %rdx, __afl_area_ptr(%rip)\n" + " jmp __afl_store\n" + "\n" + "__afl_setup_first:\n" + "\n" + " /* Save everything that is not yet saved and that may be touched by\n" + " getenv() and several other libcalls we'll be relying on. */\n" + "\n" + " leaq -352(%rsp), %rsp\n" + "\n" + " movq %rax, 0(%rsp)\n" + " movq %rcx, 8(%rsp)\n" + " movq %rdi, 16(%rsp)\n" + " movq %rsi, 32(%rsp)\n" + " movq %r8, 40(%rsp)\n" + " movq %r9, 48(%rsp)\n" + " movq %r10, 56(%rsp)\n" + " movq %r11, 64(%rsp)\n" + "\n" + " movq %xmm0, 96(%rsp)\n" + " movq %xmm1, 112(%rsp)\n" + " movq %xmm2, 128(%rsp)\n" + " movq %xmm3, 144(%rsp)\n" + " movq %xmm4, 160(%rsp)\n" + " movq %xmm5, 176(%rsp)\n" + " movq %xmm6, 192(%rsp)\n" + " movq %xmm7, 208(%rsp)\n" + " movq %xmm8, 224(%rsp)\n" + " movq %xmm9, 240(%rsp)\n" + " movq %xmm10, 256(%rsp)\n" + " movq %xmm11, 272(%rsp)\n" + " movq %xmm12, 288(%rsp)\n" + " movq %xmm13, 304(%rsp)\n" + " movq %xmm14, 320(%rsp)\n" + " movq %xmm15, 336(%rsp)\n" + "\n" + " /* Map SHM, jumping to __afl_setup_abort if something goes wrong. */\n" + "\n" + " /* The 64-bit ABI requires 16-byte stack alignment. We'll keep the\n" + " original stack ptr in the callee-saved r12. */\n" + "\n" + " pushq %r12\n" + " movq %rsp, %r12\n" + " subq $16, %rsp\n" + " andq $0xfffffffffffffff0, %rsp\n" + "\n" + " leaq .AFL_SHM_ENV(%rip), %rdi\n" + CALL_L64("getenv") + "\n" + " testq %rax, %rax\n" + " je __afl_setup_abort\n" + "\n" + " movq %rax, %rdi\n" + CALL_L64("atoi") + "\n" + " xorq %rdx, %rdx /* shmat flags */\n" + " xorq %rsi, %rsi /* requested addr */\n" + " movq %rax, %rdi /* SHM ID */\n" + CALL_L64("shmat") + "\n" + " cmpq $-1, %rax\n" + " je __afl_setup_abort\n" + "\n" + " /* Store the address of the SHM region. */\n" + "\n" + " movq %rax, %rdx\n" + " movq %rax, __afl_area_ptr(%rip)\n" + "\n" +#ifdef __APPLE__ + " movq %rax, __afl_global_area_ptr(%rip)\n" +#else + " movq __afl_global_area_ptr@GOTPCREL(%rip), %rdx\n" + " movq %rax, (%rdx)\n" +#endif /* ^__APPLE__ */ + " movq %rax, %rdx\n" + "\n" + "__afl_forkserver:\n" + "\n" + " /* Enter the fork server mode to avoid the overhead of execve() calls. We\n" + " push rdx (area ptr) twice to keep stack alignment neat. */\n" + "\n" + " pushq %rdx\n" + " pushq %rdx\n" + "\n" + " /* Phone home and tell the parent that we're OK. (Note that signals with\n" + " no SA_RESTART will mess it up). If this fails, assume that the fd is\n" + " closed because we were execve()d from an instrumented binary, or because\n" + " the parent doesn't want to use the fork server. */\n" + "\n" + " movq $4, %rdx /* length */\n" + " leaq __afl_temp(%rip), %rsi /* data */\n" + " movq $" STRINGIFY((FORKSRV_FD + 1)) ", %rdi /* file desc */\n" + CALL_L64("write") + "\n" + " cmpq $4, %rax\n" + " jne __afl_fork_resume\n" + "\n" + "__afl_fork_wait_loop:\n" + "\n" + " /* Wait for parent by reading from the pipe. Abort if read fails. */\n" + "\n" + " movq $4, %rdx /* length */\n" + " leaq __afl_temp(%rip), %rsi /* data */\n" + " movq $" STRINGIFY(FORKSRV_FD) ", %rdi /* file desc */\n" + CALL_L64("read") + " cmpq $4, %rax\n" + " jne __afl_die\n" + "\n" + " /* Once woken up, create a clone of our process. This is an excellent use\n" + " case for syscall(__NR_clone, 0, CLONE_PARENT), but glibc boneheadedly\n" + " caches getpid() results and offers no way to update the value, breaking\n" + " abort(), raise(), and a bunch of other things :-( */\n" + "\n" + CALL_L64("fork") + " cmpq $0, %rax\n" + " jl __afl_die\n" + " je __afl_fork_resume\n" + "\n" + " /* In parent process: write PID to pipe, then wait for child. */\n" + "\n" + " movl %eax, __afl_fork_pid(%rip)\n" + "\n" + " movq $4, %rdx /* length */\n" + " leaq __afl_fork_pid(%rip), %rsi /* data */\n" + " movq $" STRINGIFY((FORKSRV_FD + 1)) ", %rdi /* file desc */\n" + CALL_L64("write") + "\n" + " movq $0, %rdx /* no flags */\n" + " leaq __afl_temp(%rip), %rsi /* status */\n" + " movq __afl_fork_pid(%rip), %rdi /* PID */\n" + CALL_L64("waitpid") + " cmpq $0, %rax\n" + " jle __afl_die\n" + "\n" + " /* Relay wait status to pipe, then loop back. */\n" + "\n" + " movq $4, %rdx /* length */\n" + " leaq __afl_temp(%rip), %rsi /* data */\n" + " movq $" STRINGIFY((FORKSRV_FD + 1)) ", %rdi /* file desc */\n" + CALL_L64("write") + "\n" + " jmp __afl_fork_wait_loop\n" + "\n" + "__afl_fork_resume:\n" + "\n" + " /* In child process: close fds, resume execution. */\n" + "\n" + " movq $" STRINGIFY(FORKSRV_FD) ", %rdi\n" + CALL_L64("close") + "\n" + " movq $" STRINGIFY((FORKSRV_FD + 1)) ", %rdi\n" + CALL_L64("close") + "\n" + " popq %rdx\n" + " popq %rdx\n" + "\n" + " movq %r12, %rsp\n" + " popq %r12\n" + "\n" + " movq 0(%rsp), %rax\n" + " movq 8(%rsp), %rcx\n" + " movq 16(%rsp), %rdi\n" + " movq 32(%rsp), %rsi\n" + " movq 40(%rsp), %r8\n" + " movq 48(%rsp), %r9\n" + " movq 56(%rsp), %r10\n" + " movq 64(%rsp), %r11\n" + "\n" + " movq 96(%rsp), %xmm0\n" + " movq 112(%rsp), %xmm1\n" + " movq 128(%rsp), %xmm2\n" + " movq 144(%rsp), %xmm3\n" + " movq 160(%rsp), %xmm4\n" + " movq 176(%rsp), %xmm5\n" + " movq 192(%rsp), %xmm6\n" + " movq 208(%rsp), %xmm7\n" + " movq 224(%rsp), %xmm8\n" + " movq 240(%rsp), %xmm9\n" + " movq 256(%rsp), %xmm10\n" + " movq 272(%rsp), %xmm11\n" + " movq 288(%rsp), %xmm12\n" + " movq 304(%rsp), %xmm13\n" + " movq 320(%rsp), %xmm14\n" + " movq 336(%rsp), %xmm15\n" + "\n" + " leaq 352(%rsp), %rsp\n" + "\n" + " jmp __afl_store\n" + "\n" + "__afl_die:\n" + "\n" + " xorq %rax, %rax\n" + CALL_L64("_exit") + "\n" + "__afl_setup_abort:\n" + "\n" + " /* Record setup failure so that we don't keep calling\n" + " shmget() / shmat() over and over again. */\n" + "\n" + " incb __afl_setup_failure(%rip)\n" + "\n" + " movq %r12, %rsp\n" + " popq %r12\n" + "\n" + " movq 0(%rsp), %rax\n" + " movq 8(%rsp), %rcx\n" + " movq 16(%rsp), %rdi\n" + " movq 32(%rsp), %rsi\n" + " movq 40(%rsp), %r8\n" + " movq 48(%rsp), %r9\n" + " movq 56(%rsp), %r10\n" + " movq 64(%rsp), %r11\n" + "\n" + " movq 96(%rsp), %xmm0\n" + " movq 112(%rsp), %xmm1\n" + " movq 128(%rsp), %xmm2\n" + " movq 144(%rsp), %xmm3\n" + " movq 160(%rsp), %xmm4\n" + " movq 176(%rsp), %xmm5\n" + " movq 192(%rsp), %xmm6\n" + " movq 208(%rsp), %xmm7\n" + " movq 224(%rsp), %xmm8\n" + " movq 240(%rsp), %xmm9\n" + " movq 256(%rsp), %xmm10\n" + " movq 272(%rsp), %xmm11\n" + " movq 288(%rsp), %xmm12\n" + " movq 304(%rsp), %xmm13\n" + " movq 320(%rsp), %xmm14\n" + " movq 336(%rsp), %xmm15\n" + "\n" + " leaq 352(%rsp), %rsp\n" + "\n" + " jmp __afl_return\n" + "\n" + ".AFL_VARS:\n" + "\n" + +#ifdef __APPLE__ + + " .comm __afl_area_ptr, 8\n" +#ifndef COVERAGE_ONLY + " .comm __afl_prev_loc, 8\n" +#endif /* !COVERAGE_ONLY */ + " .comm __afl_fork_pid, 4\n" + " .comm __afl_temp, 4\n" + " .comm __afl_setup_failure, 1\n" + +#else + + " .lcomm __afl_area_ptr, 8\n" +#ifndef COVERAGE_ONLY + " .lcomm __afl_prev_loc, 8\n" +#endif /* !COVERAGE_ONLY */ + " .lcomm __afl_fork_pid, 4\n" + " .lcomm __afl_temp, 4\n" + " .lcomm __afl_setup_failure, 1\n" + +#endif /* ^__APPLE__ */ + + " .comm __afl_global_area_ptr, 8, 8\n" + "\n" + ".AFL_SHM_ENV:\n" + " .asciz \"" SHM_ENV_VAR "\"\n" + "\n" + "/* --- END --- */\n" + "\n"; + +#endif /* !_HAVE_AFL_AS_H */ diff --git a/compilers/afl-gcc.c b/compilers/afl-gcc.c new file mode 100644 index 0000000..a2b2324 --- /dev/null +++ b/compilers/afl-gcc.c @@ -0,0 +1,338 @@ +/* + american fuzzy lop - wrapper for GCC and clang + ---------------------------------------------- + + Written and maintained by Michal Zalewski + + Copyright 2013, 2014, 2015 Google Inc. 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 + + This program is a drop-in replacement for GCC or clang. The most common way + of using it is to pass the path to afl-gcc or afl-clang via CC when invoking + ./configure. + + (Of course, use CXX and point it to afl-g++ / afl-clang++ for C++ code.) + + The wrapper needs to know the path to afl-as (renamed to 'as'). The default + is /usr/local/lib/afl/. A convenient way to specify alternative directories + would be to set AFL_PATH. + + If AFL_HARDEN is set, the wrapper will compile the target app with various + hardening options that may help detect memory management issues more + reliably. You can also specify AFL_USE_ASAN to enable ASAN. + + If you want to call a non-default compiler as a next step of the chain, + specify its location via AFL_CC or AFL_CXX. + + */ + +#define AFL_MAIN + +#include "config.h" +#include "types.h" +#include "debug.h" +#include "alloc-inl.h" + +#include +#include +#include +#include + +static u8* as_path; /* Path to the AFL 'as' wrapper */ +static u8** cc_params; /* Parameters passed to the real CC */ +static u32 cc_par_cnt = 1; /* Param count, including argv0 */ +static u8 be_quiet, /* Quiet mode */ + clang_mode; /* Invoked as afl-clang*? */ + + +/* Try to find our "fake" GNU assembler in AFL_PATH or at the location derived + from argv[0]. If that fails, abort. */ + +static void find_as(u8* argv0) { + + u8 *afl_path = getenv("AFL_PATH"); + u8 *slash, *tmp; + + if (afl_path) { + + tmp = alloc_printf("%s/as", afl_path); + + if (!access(tmp, X_OK)) { + as_path = afl_path; + ck_free(tmp); + return; + } + + ck_free(tmp); + + } + + slash = strrchr(argv0, '/'); + + if (slash) { + + u8 *dir; + + *slash = 0; + dir = ck_strdup(argv0); + *slash = '/'; + + tmp = alloc_printf("%s/afl-as", dir); + + if (!access(tmp, X_OK)) { + as_path = dir; + ck_free(tmp); + return; + } + + ck_free(tmp); + ck_free(dir); + + } + + if (!access(AFL_PATH "/as", X_OK)) { + as_path = AFL_PATH; + return; + } + + FATAL("Unable to find AFL wrapper binary for 'as'. Please set AFL_PATH"); + +} + + +/* Copy argv to cc_params, making the necessary edits. */ + +static void edit_params(u32 argc, char** argv) { + + u8 fortify_set = 0, asan_set = 0; + u8 *name; + +#if defined(__FreeBSD__) && defined(__x86_64__) + u8 m32_set = 0; +#endif + + cc_params = ck_alloc((argc + 128) * sizeof(u8*)); + + name = strrchr(argv[0], '/'); + if (!name) name = argv[0]; else name++; + + if (!strncmp(name, "afl-clang", 9)) { + + clang_mode = 1; + + setenv(CLANG_ENV_VAR, "1", 1); + + if (!strcmp(name, "afl-clang++")) { + u8* alt_cxx = getenv("AFL_CXX"); + cc_params[0] = alt_cxx ? alt_cxx : (u8*)"clang++"; + } else { + u8* alt_cc = getenv("AFL_CC"); + cc_params[0] = alt_cc ? alt_cc : (u8*)"clang"; + } + + } else { + + /* With GCJ and Eclipse installed, you can actually compile Java! The + instrumentation will work (amazingly). Alas, unhandled exceptions do + not call abort(), so afl-fuzz would need to be modified to equate + non-zero exit codes with crash conditions when working with Java + binaries. Meh. */ + +#ifdef __APPLE__ + + if (!strcmp(name, "afl-g++")) cc_params[0] = getenv("AFL_CXX"); + else if (!strcmp(name, "afl-gcj")) cc_params[0] = getenv("AFL_GCJ"); + else cc_params[0] = getenv("AFL_CC"); + + if (!cc_params[0]) { + + SAYF("\n" cLRD "[-] " cRST + "On Apple systems, 'gcc' is usually just a wrapper for clang. Please use the\n" + " 'afl-clang' utility instead of 'afl-gcc'. If you really have GCC installed,\n" + " set AFL_CC or AFL_CXX to specify the correct path to that compiler.\n"); + + FATAL("AFL_CC or AFL_CXX required on MacOS X"); + + } + +#else + + if (!strcmp(name, "afl-g++")) { + u8* alt_cxx = getenv("AFL_CXX"); + cc_params[0] = alt_cxx ? alt_cxx : (u8*)"g++"; + } else if (!strcmp(name, "afl-gcj")) { + u8* alt_cc = getenv("AFL_GCJ"); + cc_params[0] = alt_cc ? alt_cc : (u8*)"gcj"; + } else { + u8* alt_cc = getenv("AFL_CC"); + cc_params[0] = alt_cc ? alt_cc : (u8*)"gcc"; + } + +#endif /* __APPLE__ */ + + } + + while (--argc) { + u8* cur = *(++argv); + + if (!strncmp(cur, "-B", 2)) { + + if (!be_quiet) WARNF("-B is already set, overriding"); + + if (!cur[2] && argc > 1) { argc--; argv++; } + continue; + + } + + if (!strcmp(cur, "-integrated-as")) continue; + + if (!strcmp(cur, "-pipe")) continue; + +#if defined(__FreeBSD__) && defined(__x86_64__) + if (!strcmp(cur, "-m32")) m32_set = 1; +#endif + + if (!strcmp(cur, "-fsanitize=address") || + !strcmp(cur, "-fsanitize=memory")) asan_set = 1; + + if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1; + + cc_params[cc_par_cnt++] = cur; + + } + + cc_params[cc_par_cnt++] = "-B"; + cc_params[cc_par_cnt++] = as_path; + + if (clang_mode) + cc_params[cc_par_cnt++] = "-no-integrated-as"; + + if (getenv("AFL_HARDEN")) { + + cc_params[cc_par_cnt++] = "-fstack-protector-all"; + + if (!fortify_set) + cc_params[cc_par_cnt++] = "-D_FORTIFY_SOURCE=2"; + + } + + if (asan_set) { + + /* Pass this on to afl-as to adjust map density. */ + + setenv("AFL_USE_ASAN", "1", 1); + + } else if (getenv("AFL_USE_ASAN")) { + + if (getenv("AFL_USE_MSAN")) + FATAL("ASAN and MSAN are mutually exclusive"); + + if (getenv("AFL_HARDEN")) + FATAL("ASAN and AFL_HARDEN are mutually exclusive"); + + cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE"; + cc_params[cc_par_cnt++] = "-fsanitize=address"; + + } else if (getenv("AFL_USE_MSAN")) { + + if (getenv("AFL_USE_ASAN")) + FATAL("ASAN and MSAN are mutually exclusive"); + + if (getenv("AFL_HARDEN")) + FATAL("MSAN and AFL_HARDEN are mutually exclusive"); + + cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE"; + cc_params[cc_par_cnt++] = "-fsanitize=memory"; + + + } + + if (!getenv("AFL_DONT_OPTIMIZE")) { + +#if defined(__FreeBSD__) && defined(__x86_64__) + + /* On 64-bit FreeBSD systems, clang -g -m32 is broken, but -m32 itself + works OK. This has nothing to do with us, but let's avoid triggering + that bug. */ + + if (!clang_mode || !m32_set) + cc_params[cc_par_cnt++] = "-g"; + +#else + + cc_params[cc_par_cnt++] = "-g"; + +#endif + + cc_params[cc_par_cnt++] = "-O3"; + cc_params[cc_par_cnt++] = "-funroll-loops"; + + /* Two indicators that you're building for fuzzing; one of them is + AFL-specific, the other is shared with libfuzzer. */ + + cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1"; + cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1"; + + } + + if (getenv("AFL_NO_BUILTIN")) { + + cc_params[cc_par_cnt++] = "-fno-builtin-strcmp"; + cc_params[cc_par_cnt++] = "-fno-builtin-strncmp"; + cc_params[cc_par_cnt++] = "-fno-builtin-strcasecmp"; + cc_params[cc_par_cnt++] = "-fno-builtin-strncasecmp"; + cc_params[cc_par_cnt++] = "-fno-builtin-memcmp"; + cc_params[cc_par_cnt++] = "-fno-builtin-strstr"; + cc_params[cc_par_cnt++] = "-fno-builtin-strcasestr"; + + } + + cc_params[cc_par_cnt] = NULL; + +} + + +/* Main entry point */ + +int main(int argc, char** argv) { + + if (isatty(2) && !getenv("AFL_QUIET")) { + + SAYF(cCYA "afl-cc " cBRI VERSION cRST " by \n"); + + } else be_quiet = 1; + + if (argc < 2) { + + SAYF("\n" + "This is a helper application for afl-fuzz. It serves as a drop-in replacement\n" + "for gcc or clang, letting you recompile third-party code with the required\n" + "runtime instrumentation. A common use pattern would be one of the following:\n\n" + + " CC=%s/afl-gcc ./configure\n" + " CXX=%s/afl-g++ ./configure\n\n" + + "You can specify custom next-stage toolchain via AFL_CC, AFL_CXX, and AFL_AS.\n" + "Setting AFL_HARDEN enables hardening optimizations in the compiled code.\n\n", + BIN_PATH, BIN_PATH); + + exit(1); + + } + + find_as(argv[0]); + + edit_params(argc, argv); + + execvp(cc_params[0], (char**)cc_params); + + FATAL("Oops, failed to execute '%s' - check your PATH", cc_params[0]); + + return 0; + +} diff --git a/compilers/afl-showmap.c b/compilers/afl-showmap.c new file mode 100644 index 0000000..df3f7cd --- /dev/null +++ b/compilers/afl-showmap.c @@ -0,0 +1,781 @@ +/* + american fuzzy lop - map display utility + ---------------------------------------- + + Written and maintained by Michal Zalewski + + Copyright 2013, 2014, 2015, 2016, 2017 Google Inc. 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 + + A very simple tool that runs the targeted binary and displays + the contents of the trace bitmap in a human-readable form. Useful in + scripts to eliminate redundant inputs and perform other checks. + + Exit code is 2 if the target program crashes; 1 if it times out or + there is a problem executing it; or 0 if execution is successful. + + */ + +#define AFL_MAIN + +#include "config.h" +#include "types.h" +#include "debug.h" +#include "alloc-inl.h" +#include "hash.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static s32 child_pid; /* PID of the tested program */ + +static u8* trace_bits; /* SHM with instrumentation bitmap */ + +static u8 *out_file, /* Trace output file */ + *doc_path, /* Path to docs */ + *target_path, /* Path to target binary */ + *at_file; /* Substitution string for @@ */ + +static u32 exec_tmout; /* Exec timeout (ms) */ + +static u64 mem_limit = MEM_LIMIT; /* Memory limit (MB) */ + +static s32 shm_id; /* ID of the SHM region */ + +static u8 quiet_mode, /* Hide non-essential messages? */ + edges_only, /* Ignore hit counts? */ + cmin_mode, /* Generate output in afl-cmin mode? */ + binary_mode, /* Write output as a binary map */ + keep_cores; /* Allow coredumps? */ + +static volatile u8 + stop_soon, /* Ctrl-C pressed? */ + child_timed_out, /* Child timed out? */ + child_crashed; /* Child crashed? */ + +/* Classify tuple counts. Instead of mapping to individual bits, as in + afl-fuzz.c, we map to more user-friendly numbers between 1 and 8. */ + +static const u8 count_class_human[256] = { + + [0] = 0, + [1] = 1, + [2] = 2, + [3] = 3, + [4 ... 7] = 4, + [8 ... 15] = 5, + [16 ... 31] = 6, + [32 ... 127] = 7, + [128 ... 255] = 8 + +}; + +static const u8 count_class_binary[256] = { + + [0] = 0, + [1] = 1, + [2] = 2, + [3] = 4, + [4 ... 7] = 8, + [8 ... 15] = 16, + [16 ... 31] = 32, + [32 ... 127] = 64, + [128 ... 255] = 128 + +}; + +static void classify_counts(u8* mem, const u8* map) { + + u32 i = MAP_SIZE; + + if (edges_only) { + + while (i--) { + if (*mem) *mem = 1; + mem++; + } + + } else { + + while (i--) { + *mem = map[*mem]; + mem++; + } + + } + +} + + +/* Get rid of shared memory (atexit handler). */ + +static void remove_shm(void) { + + shmctl(shm_id, IPC_RMID, NULL); + +} + + +/* Configure shared memory. */ + +static void setup_shm(void) { + + u8* shm_str; + + shm_id = shmget(IPC_PRIVATE, MAP_SIZE, IPC_CREAT | IPC_EXCL | 0600); + + if (shm_id < 0) PFATAL("shmget() failed"); + + atexit(remove_shm); + + shm_str = alloc_printf("%d", shm_id); + + setenv(SHM_ENV_VAR, shm_str, 1); + + ck_free(shm_str); + + trace_bits = shmat(shm_id, NULL, 0); + + if (!trace_bits) PFATAL("shmat() failed"); + +} + +/* Write results. */ + +static u32 write_results(void) { + + s32 fd; + u32 i, ret = 0; + + u8 cco = !!getenv("AFL_CMIN_CRASHES_ONLY"), + caa = !!getenv("AFL_CMIN_ALLOW_ANY"); + + if (!strncmp(out_file, "/dev/", 5)) { + + fd = open(out_file, O_WRONLY, 0600); + if (fd < 0) PFATAL("Unable to open '%s'", out_file); + + } else if (!strcmp(out_file, "-")) { + + fd = dup(1); + if (fd < 0) PFATAL("Unable to open stdout"); + + } else { + + unlink(out_file); /* Ignore errors */ + fd = open(out_file, O_WRONLY | O_CREAT | O_EXCL, 0600); + if (fd < 0) PFATAL("Unable to create '%s'", out_file); + + } + + + if (binary_mode) { + + for (i = 0; i < MAP_SIZE; i++) + if (trace_bits[i]) ret++; + + ck_write(fd, trace_bits, MAP_SIZE, out_file); + close(fd); + + } else { + + FILE* f = fdopen(fd, "w"); + + if (!f) PFATAL("fdopen() failed"); + + for (i = 0; i < MAP_SIZE; i++) { + + if (!trace_bits[i]) continue; + ret++; + + if (cmin_mode) { + + if (child_timed_out) break; + if (!caa && child_crashed != cco) break; + + fprintf(f, "%u%u\n", trace_bits[i], i); + + } else fprintf(f, "%06u:%u\n", i, trace_bits[i]); + + } + + fclose(f); + + } + + return ret; + +} + + +/* Handle timeout signal. */ + +static void handle_timeout(int sig) { + + child_timed_out = 1; + if (child_pid > 0) kill(child_pid, SIGKILL); + +} + + +/* Execute target application. */ + +static void run_target(char** argv) { + + static struct itimerval it; + int status = 0; + + if (!quiet_mode) + SAYF("-- Program output begins --\n" cRST); + + MEM_BARRIER(); + + child_pid = fork(); + + if (child_pid < 0) PFATAL("fork() failed"); + + if (!child_pid) { + + struct rlimit r; + + if (quiet_mode) { + + s32 fd = open("/dev/null", O_RDWR); + + if (fd < 0 || dup2(fd, 1) < 0 || dup2(fd, 2) < 0) { + *(u32*)trace_bits = EXEC_FAIL_SIG; + PFATAL("Descriptor initialization failed"); + } + + close(fd); + + } + + if (mem_limit) { + + r.rlim_max = r.rlim_cur = ((rlim_t)mem_limit) << 20; + +#ifdef RLIMIT_AS + + setrlimit(RLIMIT_AS, &r); /* Ignore errors */ + +#else + + setrlimit(RLIMIT_DATA, &r); /* Ignore errors */ + +#endif /* ^RLIMIT_AS */ + + } + + if (!keep_cores) r.rlim_max = r.rlim_cur = 0; + else r.rlim_max = r.rlim_cur = RLIM_INFINITY; + + setrlimit(RLIMIT_CORE, &r); /* Ignore errors */ + + if (!getenv("LD_BIND_LAZY")) setenv("LD_BIND_NOW", "1", 0); + + setsid(); + + execv(target_path, argv); + + *(u32*)trace_bits = EXEC_FAIL_SIG; + exit(0); + + } + + /* 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; + + } + + setitimer(ITIMER_REAL, &it, NULL); + + if (waitpid(child_pid, &status, 0) <= 0) FATAL("waitpid() failed"); + + child_pid = 0; + it.it_value.tv_sec = 0; + it.it_value.tv_usec = 0; + setitimer(ITIMER_REAL, &it, NULL); + + MEM_BARRIER(); + + /* Clean up bitmap, analyze exit condition, etc. */ + + if (*(u32*)trace_bits == EXEC_FAIL_SIG) + FATAL("Unable to execute '%s'", argv[0]); + + classify_counts(trace_bits, binary_mode ? + count_class_binary : count_class_human); + + if (!quiet_mode) + SAYF(cRST "-- Program output ends --\n"); + + if (!child_timed_out && !stop_soon && WIFSIGNALED(status)) + child_crashed = 1; + + if (!quiet_mode) { + + if (child_timed_out) + SAYF(cLRD "\n+++ Program timed off +++\n" cRST); + else if (stop_soon) + SAYF(cLRD "\n+++ Program aborted by user +++\n" cRST); + else if (child_crashed) + SAYF(cLRD "\n+++ Program killed by signal %u +++\n" cRST, WTERMSIG(status)); + + } + + +} + + +/* Handle Ctrl-C and the like. */ + +static void handle_stop_sig(int sig) { + + stop_soon = 1; + + if (child_pid > 0) kill(child_pid, SIGKILL); + +} + + +/* Do basic preparations - persistent fds, filenames, etc. */ + +static void set_up_environment(void) { + + 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:" + "abort_on_error=1:" + "allocator_may_return_null=1:" + "msan_track_origins=0", 0); + + if (getenv("AFL_PRELOAD")) { + setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1); + setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1); + } + +} + + +/* Setup signal handlers, duh. */ + +static void setup_signal_handlers(void) { + + struct sigaction sa; + + sa.sa_handler = NULL; + sa.sa_flags = SA_RESTART; + sa.sa_sigaction = NULL; + + sigemptyset(&sa.sa_mask); + + /* Various ways of saying "stop". */ + + sa.sa_handler = handle_stop_sig; + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + + /* Exec timeout notifications. */ + + sa.sa_handler = handle_timeout; + sigaction(SIGALRM, &sa, NULL); + +} + + +/* Detect @@ in args. */ + +static void detect_file_args(char** argv) { + + u32 i = 0; + u8* cwd = getcwd(NULL, 0); + + if (!cwd) PFATAL("getcwd() failed"); + + while (argv[i]) { + + u8* aa_loc = strstr(argv[i], "@@"); + + if (aa_loc) { + + u8 *aa_subst, *n_arg; + + if (!at_file) FATAL("@@ syntax is not supported by this tool."); + + /* Be sure that we're always using fully-qualified paths. */ + + if (at_file[0] == '/') aa_subst = at_file; + else aa_subst = alloc_printf("%s/%s", cwd, at_file); + + /* Construct a replacement argv value. */ + + *aa_loc = 0; + n_arg = alloc_printf("%s%s%s", argv[i], aa_subst, aa_loc + 2); + argv[i] = n_arg; + *aa_loc = '@'; + + if (at_file[0] != '/') ck_free(aa_subst); + + } + + i++; + + } + + free(cwd); /* not tracked */ + +} + + +/* Show banner. */ + +static void show_banner(void) { + + SAYF(cCYA "afl-showmap " cBRI VERSION cRST " by \n"); + +} + +/* Display usage hints. */ + +static void usage(u8* argv0) { + + show_banner(); + + SAYF("\n%s [ options ] -- /path/to/target_app [ ... ]\n\n" + + "Required parameters:\n\n" + + " -o file - file to write the trace data to\n\n" + + "Execution control settings:\n\n" + + " -t msec - timeout for each run (none)\n" + " -m megs - memory limit for child process (%u MB)\n" + " -Q - use binary-only instrumentation (QEMU mode)\n\n" + + "Other settings:\n\n" + + " -q - sink program's output and don't show messages\n" + " -e - show edge coverage only, ignore hit counts\n" + " -c - allow core dumps\n\n" + + "This tool displays raw tuple data captured by AFL instrumentation.\n" + "For additional help, consult %s/README.\n\n" cRST, + + argv0, MEM_LIMIT, doc_path); + + exit(1); + +} + + +/* Find binary. */ + +static void find_binary(u8* fname) { + + u8* env_path = 0; + struct stat st; + + if (strchr(fname, '/') || !(env_path = getenv("PATH"))) { + + target_path = ck_strdup(fname); + + if (stat(target_path, &st) || !S_ISREG(st.st_mode) || + !(st.st_mode & 0111) || st.st_size < 4) + FATAL("Program '%s' not found or not executable", fname); + + } else { + + while (env_path) { + + u8 *cur_elem, *delim = strchr(env_path, ':'); + + if (delim) { + + cur_elem = ck_alloc(delim - env_path + 1); + memcpy(cur_elem, env_path, delim - env_path); + delim++; + + } else cur_elem = ck_strdup(env_path); + + env_path = delim; + + if (cur_elem[0]) + target_path = alloc_printf("%s/%s", cur_elem, fname); + else + target_path = ck_strdup(fname); + + ck_free(cur_elem); + + if (!stat(target_path, &st) && S_ISREG(st.st_mode) && + (st.st_mode & 0111) && st.st_size >= 4) break; + + ck_free(target_path); + target_path = 0; + + } + + if (!target_path) FATAL("Program '%s' not found or not executable", fname); + + } + +} + + +/* Fix up argv for QEMU. */ + +static char** get_qemu_argv(u8* own_loc, char** argv, int argc) { + + char** new_argv = ck_alloc(sizeof(char*) * (argc + 4)); + u8 *tmp, *cp, *rsl, *own_copy; + + /* Workaround for a QEMU stability glitch. */ + + setenv("QEMU_LOG", "nochain", 1); + + memcpy(new_argv + 3, argv + 1, sizeof(char*) * argc); + + new_argv[2] = target_path; + new_argv[1] = "--"; + + /* Now we need to actually find qemu for argv[0]. */ + + tmp = getenv("AFL_PATH"); + + if (tmp) { + + cp = alloc_printf("%s/afl-qemu-trace", tmp); + + if (access(cp, X_OK)) + FATAL("Unable to find '%s'", tmp); + + target_path = new_argv[0] = cp; + return new_argv; + + } + + own_copy = ck_strdup(own_loc); + rsl = strrchr(own_copy, '/'); + + if (rsl) { + + *rsl = 0; + + cp = alloc_printf("%s/afl-qemu-trace", own_copy); + ck_free(own_copy); + + if (!access(cp, X_OK)) { + + target_path = new_argv[0] = cp; + return new_argv; + + } + + } else ck_free(own_copy); + + if (!access(BIN_PATH "/afl-qemu-trace", X_OK)) { + + target_path = new_argv[0] = BIN_PATH "/afl-qemu-trace"; + return new_argv; + + } + + FATAL("Unable to find 'afl-qemu-trace'."); + +} + + +/* Main entry point */ + +int main(int argc, char** argv) { + + s32 opt; + u8 mem_limit_given = 0, timeout_given = 0, qemu_mode = 0; + u32 tcnt; + char** use_argv; + + doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH; + + while ((opt = getopt(argc,argv,"+o:m:t:A:eqZQbc")) > 0) + + switch (opt) { + + case 'o': + + if (out_file) FATAL("Multiple -o options not supported"); + out_file = optarg; + break; + + case 'm': { + + u8 suffix = 'M'; + + if (mem_limit_given) FATAL("Multiple -m options not supported"); + mem_limit_given = 1; + + if (!strcmp(optarg, "none")) { + + mem_limit = 0; + break; + + } + + if (sscanf(optarg, "%llu%c", &mem_limit, &suffix) < 1 || + optarg[0] == '-') FATAL("Bad syntax used for -m"); + + switch (suffix) { + + case 'T': mem_limit *= 1024 * 1024; break; + case 'G': mem_limit *= 1024; break; + case 'k': mem_limit /= 1024; break; + case 'M': break; + + default: FATAL("Unsupported suffix or bad syntax for -m"); + + } + + if (mem_limit < 5) FATAL("Dangerously low value of -m"); + + if (sizeof(rlim_t) == 4 && mem_limit > 2000) + FATAL("Value of -m out of range on 32-bit systems"); + + } + + break; + + case 't': + + if (timeout_given) FATAL("Multiple -t options not supported"); + timeout_given = 1; + + if (strcmp(optarg, "none")) { + exec_tmout = atoi(optarg); + + if (exec_tmout < 20 || optarg[0] == '-') + FATAL("Dangerously low value of -t"); + + } + + break; + + case 'e': + + if (edges_only) FATAL("Multiple -e options not supported"); + edges_only = 1; + break; + + case 'q': + + if (quiet_mode) FATAL("Multiple -q options not supported"); + quiet_mode = 1; + break; + + case 'Z': + + /* This is an undocumented option to write data in the syntax expected + by afl-cmin. Nobody else should have any use for this. */ + + cmin_mode = 1; + quiet_mode = 1; + break; + + case 'A': + + /* Another afl-cmin specific feature. */ + at_file = optarg; + break; + + case 'Q': + + if (qemu_mode) FATAL("Multiple -Q options not supported"); + if (!mem_limit_given) mem_limit = MEM_LIMIT_QEMU; + + qemu_mode = 1; + break; + + case 'b': + + /* Secret undocumented mode. Writes output in raw binary format + similar to that dumped by afl-fuzz in + + Copyright 2013, 2014, 2015 Google Inc. 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 + + This allocator is not designed to resist malicious attackers (the canaries + are small and predictable), but provides a robust and portable way to detect + use-after-free, off-by-one writes, stale pointers, and so on. + + */ + +#ifndef _HAVE_ALLOC_INL_H +#define _HAVE_ALLOC_INL_H + +#include +#include +#include + +#include "config.h" +#include "types.h" +#include "debug.h" + +/* User-facing macro to sprintf() to a dynamically allocated buffer. */ + +#define alloc_printf(_str...) ({ \ + u8* _tmp; \ + s32 _len = snprintf(NULL, 0, _str); \ + if (_len < 0) FATAL("Whoa, snprintf() fails?!"); \ + _tmp = ck_alloc(_len + 1); \ + snprintf((char*)_tmp, _len + 1, _str); \ + _tmp; \ + }) + +/* Macro to enforce allocation limits as a last-resort defense against + integer overflows. */ + +#define ALLOC_CHECK_SIZE(_s) do { \ + if ((_s) > MAX_ALLOC) \ + ABORT("Bad alloc request: %u bytes", (_s)); \ + } while (0) + +/* Macro to check malloc() failures and the like. */ + +#define ALLOC_CHECK_RESULT(_r, _s) do { \ + if (!(_r)) \ + ABORT("Out of memory: can't allocate %u bytes", (_s)); \ + } while (0) + +/* Magic tokens used to mark used / freed chunks. */ + +#define ALLOC_MAGIC_C1 0xFF00FF00 /* Used head (dword) */ +#define ALLOC_MAGIC_F 0xFE00FE00 /* Freed head (dword) */ +#define ALLOC_MAGIC_C2 0xF0 /* Used tail (byte) */ + +/* Positions of guard tokens in relation to the user-visible pointer. */ + +#define ALLOC_C1(_ptr) (((u32*)(_ptr))[-2]) +#define ALLOC_S(_ptr) (((u32*)(_ptr))[-1]) +#define ALLOC_C2(_ptr) (((u8*)(_ptr))[ALLOC_S(_ptr)]) + +#define ALLOC_OFF_HEAD 8 +#define ALLOC_OFF_TOTAL (ALLOC_OFF_HEAD + 1) + +/* Allocator increments for ck_realloc_block(). */ + +#define ALLOC_BLK_INC 256 + +/* Sanity-checking macros for pointers. */ + +#define CHECK_PTR(_p) do { \ + if (_p) { \ + if (ALLOC_C1(_p) ^ ALLOC_MAGIC_C1) {\ + if (ALLOC_C1(_p) == ALLOC_MAGIC_F) \ + ABORT("Use after free."); \ + else ABORT("Corrupted head alloc canary."); \ + } \ + if (ALLOC_C2(_p) ^ ALLOC_MAGIC_C2) \ + ABORT("Corrupted tail alloc canary."); \ + } \ + } while (0) + +#define CHECK_PTR_EXPR(_p) ({ \ + typeof (_p) _tmp = (_p); \ + CHECK_PTR(_tmp); \ + _tmp; \ + }) + + +/* Allocate a buffer, explicitly not zeroing it. Returns NULL for zero-sized + requests. */ + +static inline void* DFL_ck_alloc_nozero(u32 size) { + + void* ret; + + if (!size) return NULL; + + ALLOC_CHECK_SIZE(size); + ret = malloc(size + ALLOC_OFF_TOTAL); + ALLOC_CHECK_RESULT(ret, size); + + ret += ALLOC_OFF_HEAD; + + ALLOC_C1(ret) = ALLOC_MAGIC_C1; + ALLOC_S(ret) = size; + ALLOC_C2(ret) = ALLOC_MAGIC_C2; + + return ret; + +} + + +/* Allocate a buffer, returning zeroed memory. */ + +static inline void* DFL_ck_alloc(u32 size) { + + void* mem; + + if (!size) return NULL; + mem = DFL_ck_alloc_nozero(size); + + return memset(mem, 0, size); + +} + + +/* Free memory, checking for double free and corrupted heap. When DEBUG_BUILD + is set, the old memory will be also clobbered with 0xFF. */ + +static inline void DFL_ck_free(void* mem) { + + if (!mem) return; + + CHECK_PTR(mem); + +#ifdef DEBUG_BUILD + + /* Catch pointer issues sooner. */ + memset(mem, 0xFF, ALLOC_S(mem)); + +#endif /* DEBUG_BUILD */ + + ALLOC_C1(mem) = ALLOC_MAGIC_F; + + free(mem - ALLOC_OFF_HEAD); + +} + + +/* Re-allocate a buffer, checking for issues and zeroing any newly-added tail. + With DEBUG_BUILD, the buffer is always reallocated to a new addresses and the + old memory is clobbered with 0xFF. */ + +static inline void* DFL_ck_realloc(void* orig, u32 size) { + + void* ret; + u32 old_size = 0; + + if (!size) { + + DFL_ck_free(orig); + return NULL; + + } + + if (orig) { + + CHECK_PTR(orig); + +#ifndef DEBUG_BUILD + ALLOC_C1(orig) = ALLOC_MAGIC_F; +#endif /* !DEBUG_BUILD */ + + old_size = ALLOC_S(orig); + orig -= ALLOC_OFF_HEAD; + + ALLOC_CHECK_SIZE(old_size); + + } + + ALLOC_CHECK_SIZE(size); + +#ifndef DEBUG_BUILD + + ret = realloc(orig, size + ALLOC_OFF_TOTAL); + ALLOC_CHECK_RESULT(ret, size); + +#else + + /* Catch pointer issues sooner: force relocation and make sure that the + original buffer is wiped. */ + + ret = malloc(size + ALLOC_OFF_TOTAL); + ALLOC_CHECK_RESULT(ret, size); + + if (orig) { + + memcpy(ret + ALLOC_OFF_HEAD, orig + ALLOC_OFF_HEAD, MIN(size, old_size)); + memset(orig + ALLOC_OFF_HEAD, 0xFF, old_size); + + ALLOC_C1(orig + ALLOC_OFF_HEAD) = ALLOC_MAGIC_F; + + free(orig); + + } + +#endif /* ^!DEBUG_BUILD */ + + ret += ALLOC_OFF_HEAD; + + ALLOC_C1(ret) = ALLOC_MAGIC_C1; + ALLOC_S(ret) = size; + ALLOC_C2(ret) = ALLOC_MAGIC_C2; + + if (size > old_size) + memset(ret + old_size, 0, size - old_size); + + return ret; + +} + + +/* Re-allocate a buffer with ALLOC_BLK_INC increments (used to speed up + repeated small reallocs without complicating the user code). */ + +static inline void* DFL_ck_realloc_block(void* orig, u32 size) { + +#ifndef DEBUG_BUILD + + if (orig) { + + CHECK_PTR(orig); + + if (ALLOC_S(orig) >= size) return orig; + + size += ALLOC_BLK_INC; + + } + +#endif /* !DEBUG_BUILD */ + + return DFL_ck_realloc(orig, size); + +} + + +/* Create a buffer with a copy of a string. Returns NULL for NULL inputs. */ + +static inline u8* DFL_ck_strdup(u8* str) { + + void* ret; + u32 size; + + if (!str) return NULL; + + size = strlen((char*)str) + 1; + + ALLOC_CHECK_SIZE(size); + ret = malloc(size + ALLOC_OFF_TOTAL); + ALLOC_CHECK_RESULT(ret, size); + + ret += ALLOC_OFF_HEAD; + + ALLOC_C1(ret) = ALLOC_MAGIC_C1; + ALLOC_S(ret) = size; + ALLOC_C2(ret) = ALLOC_MAGIC_C2; + + return memcpy(ret, str, size); + +} + + +/* Create a buffer with a copy of a memory block. Returns NULL for zero-sized + or NULL inputs. */ + +static inline void* DFL_ck_memdup(void* mem, u32 size) { + + void* ret; + + if (!mem || !size) return NULL; + + ALLOC_CHECK_SIZE(size); + ret = malloc(size + ALLOC_OFF_TOTAL); + ALLOC_CHECK_RESULT(ret, size); + + ret += ALLOC_OFF_HEAD; + + ALLOC_C1(ret) = ALLOC_MAGIC_C1; + ALLOC_S(ret) = size; + ALLOC_C2(ret) = ALLOC_MAGIC_C2; + + return memcpy(ret, mem, size); + +} + + +/* Create a buffer with a block of text, appending a NUL terminator at the end. + Returns NULL for zero-sized or NULL inputs. */ + +static inline u8* DFL_ck_memdup_str(u8* mem, u32 size) { + + u8* ret; + + if (!mem || !size) return NULL; + + ALLOC_CHECK_SIZE(size); + ret = malloc(size + ALLOC_OFF_TOTAL + 1); + ALLOC_CHECK_RESULT(ret, size); + + ret += ALLOC_OFF_HEAD; + + ALLOC_C1(ret) = ALLOC_MAGIC_C1; + ALLOC_S(ret) = size; + ALLOC_C2(ret) = ALLOC_MAGIC_C2; + + memcpy(ret, mem, size); + ret[size] = 0; + + return ret; + +} + + +#ifndef DEBUG_BUILD + +/* In non-debug mode, we just do straightforward aliasing of the above functions + to user-visible names such as ck_alloc(). */ + +#define ck_alloc DFL_ck_alloc +#define ck_alloc_nozero DFL_ck_alloc_nozero +#define ck_realloc DFL_ck_realloc +#define ck_realloc_block DFL_ck_realloc_block +#define ck_strdup DFL_ck_strdup +#define ck_memdup DFL_ck_memdup +#define ck_memdup_str DFL_ck_memdup_str +#define ck_free DFL_ck_free + +#define alloc_report() + +#else + +/* In debugging mode, we also track allocations to detect memory leaks, and the + flow goes through one more layer of indirection. */ + +/* Alloc tracking data structures: */ + +#define ALLOC_BUCKETS 4096 + +struct TRK_obj { + void *ptr; + char *file, *func; + u32 line; +}; + +#ifdef AFL_MAIN + +struct TRK_obj* TRK[ALLOC_BUCKETS]; +u32 TRK_cnt[ALLOC_BUCKETS]; + +# define alloc_report() TRK_report() + +#else + +extern struct TRK_obj* TRK[ALLOC_BUCKETS]; +extern u32 TRK_cnt[ALLOC_BUCKETS]; + +# define alloc_report() + +#endif /* ^AFL_MAIN */ + +/* Bucket-assigning function for a given pointer: */ + +#define TRKH(_ptr) (((((u32)(_ptr)) >> 16) ^ ((u32)(_ptr))) % ALLOC_BUCKETS) + + +/* Add a new entry to the list of allocated objects. */ + +static inline void TRK_alloc_buf(void* ptr, const char* file, const char* func, + u32 line) { + + u32 i, bucket; + + if (!ptr) return; + + bucket = TRKH(ptr); + + /* Find a free slot in the list of entries for that bucket. */ + + for (i = 0; i < TRK_cnt[bucket]; i++) + + if (!TRK[bucket][i].ptr) { + + TRK[bucket][i].ptr = ptr; + TRK[bucket][i].file = (char*)file; + TRK[bucket][i].func = (char*)func; + TRK[bucket][i].line = line; + return; + + } + + /* No space available - allocate more. */ + + TRK[bucket] = DFL_ck_realloc_block(TRK[bucket], + (TRK_cnt[bucket] + 1) * sizeof(struct TRK_obj)); + + TRK[bucket][i].ptr = ptr; + TRK[bucket][i].file = (char*)file; + TRK[bucket][i].func = (char*)func; + TRK[bucket][i].line = line; + + TRK_cnt[bucket]++; + +} + + +/* Remove entry from the list of allocated objects. */ + +static inline void TRK_free_buf(void* ptr, const char* file, const char* func, + u32 line) { + + u32 i, bucket; + + if (!ptr) return; + + bucket = TRKH(ptr); + + /* Find the element on the list... */ + + for (i = 0; i < TRK_cnt[bucket]; i++) + + if (TRK[bucket][i].ptr == ptr) { + + TRK[bucket][i].ptr = 0; + return; + + } + + WARNF("ALLOC: Attempt to free non-allocated memory in %s (%s:%u)", + func, file, line); + +} + + +/* Do a final report on all non-deallocated objects. */ + +static inline void TRK_report(void) { + + u32 i, bucket; + + fflush(0); + + for (bucket = 0; bucket < ALLOC_BUCKETS; bucket++) + for (i = 0; i < TRK_cnt[bucket]; i++) + if (TRK[bucket][i].ptr) + WARNF("ALLOC: Memory never freed, created in %s (%s:%u)", + TRK[bucket][i].func, TRK[bucket][i].file, TRK[bucket][i].line); + +} + + +/* Simple wrappers for non-debugging functions: */ + +static inline void* TRK_ck_alloc(u32 size, const char* file, const char* func, + u32 line) { + + void* ret = DFL_ck_alloc(size); + TRK_alloc_buf(ret, file, func, line); + return ret; + +} + + +static inline void* TRK_ck_realloc(void* orig, u32 size, const char* file, + const char* func, u32 line) { + + void* ret = DFL_ck_realloc(orig, size); + TRK_free_buf(orig, file, func, line); + TRK_alloc_buf(ret, file, func, line); + return ret; + +} + + +static inline void* TRK_ck_realloc_block(void* orig, u32 size, const char* file, + const char* func, u32 line) { + + void* ret = DFL_ck_realloc_block(orig, size); + TRK_free_buf(orig, file, func, line); + TRK_alloc_buf(ret, file, func, line); + return ret; + +} + + +static inline void* TRK_ck_strdup(u8* str, const char* file, const char* func, + u32 line) { + + void* ret = DFL_ck_strdup(str); + TRK_alloc_buf(ret, file, func, line); + return ret; + +} + + +static inline void* TRK_ck_memdup(void* mem, u32 size, const char* file, + const char* func, u32 line) { + + void* ret = DFL_ck_memdup(mem, size); + TRK_alloc_buf(ret, file, func, line); + return ret; + +} + + +static inline void* TRK_ck_memdup_str(void* mem, u32 size, const char* file, + const char* func, u32 line) { + + void* ret = DFL_ck_memdup_str(mem, size); + TRK_alloc_buf(ret, file, func, line); + return ret; + +} + + +static inline void TRK_ck_free(void* ptr, const char* file, + const char* func, u32 line) { + + TRK_free_buf(ptr, file, func, line); + DFL_ck_free(ptr); + +} + +/* Aliasing user-facing names to tracking functions: */ + +#define ck_alloc(_p1) \ + TRK_ck_alloc(_p1, __FILE__, __FUNCTION__, __LINE__) + +#define ck_alloc_nozero(_p1) \ + TRK_ck_alloc(_p1, __FILE__, __FUNCTION__, __LINE__) + +#define ck_realloc(_p1, _p2) \ + TRK_ck_realloc(_p1, _p2, __FILE__, __FUNCTION__, __LINE__) + +#define ck_realloc_block(_p1, _p2) \ + TRK_ck_realloc_block(_p1, _p2, __FILE__, __FUNCTION__, __LINE__) + +#define ck_strdup(_p1) \ + TRK_ck_strdup(_p1, __FILE__, __FUNCTION__, __LINE__) + +#define ck_memdup(_p1, _p2) \ + TRK_ck_memdup(_p1, _p2, __FILE__, __FUNCTION__, __LINE__) + +#define ck_memdup_str(_p1, _p2) \ + TRK_ck_memdup_str(_p1, _p2, __FILE__, __FUNCTION__, __LINE__) + +#define ck_free(_p1) \ + TRK_ck_free(_p1, __FILE__, __FUNCTION__, __LINE__) + +#endif /* ^!DEBUG_BUILD */ + +#endif /* ! _HAVE_ALLOC_INL_H */ diff --git a/compilers/config.h b/compilers/config.h new file mode 100644 index 0000000..e74b3b3 --- /dev/null +++ b/compilers/config.h @@ -0,0 +1,350 @@ +/* + american fuzzy lop - vaguely configurable bits + ---------------------------------------------- + + Written and maintained by Michal Zalewski + + Copyright 2013, 2014, 2015, 2016 Google Inc. 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 + + */ + +#ifndef _HAVE_CONFIG_H +#define _HAVE_CONFIG_H + +#include "types.h" + +/* Version string: */ + +#define VERSION "2.52b" + +/****************************************************** + * * + * Settings that may be of interest to power users: * + * * + ******************************************************/ + +/* Comment out to disable terminal colors (note that this makes afl-analyze + a lot less nice): */ + +#define USE_COLOR + +/* Comment out to disable fancy ANSI boxes and use poor man's 7-bit UI: */ + +#define FANCY_BOXES + +/* Default timeout for fuzzed code (milliseconds). This is the upper bound, + also used for detecting hangs; the actual value is auto-scaled: */ + +#define EXEC_TIMEOUT 1000 + +/* Timeout rounding factor when auto-scaling (milliseconds): */ + +#define EXEC_TM_ROUND 20 + +/* Default memory limit for child process (MB): */ + +#ifndef __x86_64__ +# define MEM_LIMIT 25 +#else +# define MEM_LIMIT 50 +#endif /* ^!__x86_64__ */ + +/* Default memory limit when running in QEMU mode (MB): */ + +#define MEM_LIMIT_QEMU 200 + +/* Number of calibration cycles per every new test case (and for test + cases that show variable behavior): */ + +#define CAL_CYCLES 8 +#define CAL_CYCLES_LONG 40 + +/* Number of subsequent timeouts before abandoning an input file: */ + +#define TMOUT_LIMIT 250 + +/* Maximum number of unique hangs or crashes to record: */ + +#define KEEP_UNIQUE_HANG 500 +#define KEEP_UNIQUE_CRASH 5000 + +/* Baseline number of random tweaks during a single 'havoc' stage: */ + +#define HAVOC_CYCLES 256 +#define HAVOC_CYCLES_INIT 1024 + +/* Maximum multiplier for the above (should be a power of two, beware + of 32-bit int overflows): */ + +#define HAVOC_MAX_MULT 16 + +/* Absolute minimum number of havoc cycles (after all adjustments): */ + +#define HAVOC_MIN 16 + +/* Maximum stacking for havoc-stage tweaks. The actual value is calculated + like this: + + n = random between 1 and HAVOC_STACK_POW2 + stacking = 2^n + + In other words, the default (n = 7) produces 2, 4, 8, 16, 32, 64, or + 128 stacked tweaks: */ + +#define HAVOC_STACK_POW2 7 + +/* Caps on block sizes for cloning and deletion operations. Each of these + ranges has a 33% probability of getting picked, except for the first + two cycles where smaller blocks are favored: */ + +#define HAVOC_BLK_SMALL 32 +#define HAVOC_BLK_MEDIUM 128 +#define HAVOC_BLK_LARGE 1500 + +/* Extra-large blocks, selected very rarely (<5% of the time): */ + +#define HAVOC_BLK_XL 32768 + +/* Probabilities of skipping non-favored entries in the queue, expressed as + percentages: */ + +#define SKIP_TO_NEW_PROB 99 /* ...when there are new, pending favorites */ +#define SKIP_NFAV_OLD_PROB 95 /* ...no new favs, cur entry already fuzzed */ +#define SKIP_NFAV_NEW_PROB 75 /* ...no new favs, cur entry not fuzzed yet */ + +/* Splicing cycle count: */ + +#define SPLICE_CYCLES 15 + +/* Nominal per-splice havoc cycle length: */ + +#define SPLICE_HAVOC 32 + +/* Maximum offset for integer addition / subtraction stages: */ + +#define ARITH_MAX 35 + +/* Limits for the test case trimmer. The absolute minimum chunk size; and + the starting and ending divisors for chopping up the input file: */ + +#define TRIM_MIN_BYTES 4 +#define TRIM_START_STEPS 16 +#define TRIM_END_STEPS 1024 + +/* Maximum size of input file, in bytes (keep under 100MB): */ + +#define MAX_FILE (1 * 1024 * 1024) + +/* The same, for the test case minimizer: */ + +#define TMIN_MAX_FILE (10 * 1024 * 1024) + +/* Block normalization steps for afl-tmin: */ + +#define TMIN_SET_MIN_SIZE 4 +#define TMIN_SET_STEPS 128 + +/* Maximum dictionary token size (-x), in bytes: */ + +#define MAX_DICT_FILE 128 + +/* Length limits for auto-detected dictionary tokens: */ + +#define MIN_AUTO_EXTRA 3 +#define MAX_AUTO_EXTRA 32 + +/* Maximum number of user-specified dictionary tokens to use in deterministic + steps; past this point, the "extras/user" step will be still carried out, + but with proportionally lower odds: */ + +#define MAX_DET_EXTRAS 200 + +/* Maximum number of auto-extracted dictionary tokens to actually use in fuzzing + (first value), and to keep in memory as candidates. The latter should be much + higher than the former. */ + +#define USE_AUTO_EXTRAS 50 +#define MAX_AUTO_EXTRAS (USE_AUTO_EXTRAS * 10) + +/* Scaling factor for the effector map used to skip some of the more + expensive deterministic steps. The actual divisor is set to + 2^EFF_MAP_SCALE2 bytes: */ + +#define EFF_MAP_SCALE2 3 + +/* Minimum input file length at which the effector logic kicks in: */ + +#define EFF_MIN_LEN 128 + +/* Maximum effector density past which everything is just fuzzed + unconditionally (%): */ + +#define EFF_MAX_PERC 90 + +/* UI refresh frequency (Hz): */ + +#define UI_TARGET_HZ 5 + +/* Fuzzer stats file and plot update intervals (sec): */ + +#define STATS_UPDATE_SEC 60 +#define PLOT_UPDATE_SEC 5 + +/* Smoothing divisor for CPU load and exec speed stats (1 - no smoothing). */ + +#define AVG_SMOOTHING 16 + +/* Sync interval (every n havoc cycles): */ + +#define SYNC_INTERVAL 5 + +/* Output directory reuse grace period (minutes): */ + +#define OUTPUT_GRACE 25 + +/* Uncomment to use simple file names (id_NNNNNN): */ + +// #define SIMPLE_FILES + +/* List of interesting values to use in fuzzing. */ + +#define INTERESTING_8 \ + -128, /* Overflow signed 8-bit when decremented */ \ + -1, /* */ \ + 0, /* */ \ + 1, /* */ \ + 16, /* One-off with common buffer size */ \ + 32, /* One-off with common buffer size */ \ + 64, /* One-off with common buffer size */ \ + 100, /* One-off with common buffer size */ \ + 127 /* Overflow signed 8-bit when incremented */ + +#define INTERESTING_16 \ + -32768, /* Overflow signed 16-bit when decremented */ \ + -129, /* Overflow signed 8-bit */ \ + 128, /* Overflow signed 8-bit */ \ + 255, /* Overflow unsig 8-bit when incremented */ \ + 256, /* Overflow unsig 8-bit */ \ + 512, /* One-off with common buffer size */ \ + 1000, /* One-off with common buffer size */ \ + 1024, /* One-off with common buffer size */ \ + 4096, /* One-off with common buffer size */ \ + 32767 /* Overflow signed 16-bit when incremented */ + +#define INTERESTING_32 \ + -2147483648LL, /* Overflow signed 32-bit when decremented */ \ + -100663046, /* Large negative number (endian-agnostic) */ \ + -32769, /* Overflow signed 16-bit */ \ + 32768, /* Overflow signed 16-bit */ \ + 65535, /* Overflow unsig 16-bit when incremented */ \ + 65536, /* Overflow unsig 16 bit */ \ + 100663045, /* Large positive number (endian-agnostic) */ \ + 2147483647 /* Overflow signed 32-bit when incremented */ + +/*********************************************************** + * * + * Really exotic stuff you probably don't want to touch: * + * * + ***********************************************************/ + +/* Call count interval between reseeding the libc PRNG from /dev/urandom: */ + +#define RESEED_RNG 10000 + +/* Maximum line length passed from GCC to 'as' and used for parsing + configuration files: */ + +#define MAX_LINE 8192 + +/* Environment variable used to pass SHM ID to the called program. */ + +#define SHM_ENV_VAR "__AFL_SHM_ID" + +/* Other less interesting, internal-only variables. */ + +#define CLANG_ENV_VAR "__AFL_CLANG_MODE" +#define AS_LOOP_ENV_VAR "__AFL_AS_LOOPCHECK" +#define PERSIST_ENV_VAR "__AFL_PERSISTENT" +#define DEFER_ENV_VAR "__AFL_DEFER_FORKSRV" + +/* In-code signatures for deferred and persistent mode. */ + +#define PERSIST_SIG "##SIG_AFL_PERSISTENT##" +#define DEFER_SIG "##SIG_AFL_DEFER_FORKSRV##" + +/* Distinctive bitmap signature used to indicate failed execution: */ + +#define EXEC_FAIL_SIG 0xfee1dead + +/* Distinctive exit code used to indicate MSAN trip condition: */ + +#define MSAN_ERROR 86 + +/* Designated file descriptors for forkserver commands (the application will + use FORKSRV_FD and FORKSRV_FD + 1): */ + +#define FORKSRV_FD 198 + +/* Fork server init timeout multiplier: we'll wait the user-selected + timeout plus this much for the fork server to spin up. */ + +#define FORK_WAIT_MULT 10 + +/* Calibration timeout adjustments, to be a bit more generous when resuming + fuzzing sessions or trying to calibrate already-added internal finds. + The first value is a percentage, the other is in milliseconds: */ + +#define CAL_TMOUT_PERC 125 +#define CAL_TMOUT_ADD 50 + +/* Number of chances to calibrate a case before giving up: */ + +#define CAL_CHANCES 3 + +/* Map size for the traced binary (2^MAP_SIZE_POW2). Must be greater than + 2; you probably want to keep it under 18 or so for performance reasons + (adjusting AFL_INST_RATIO when compiling is probably a better way to solve + problems with complex programs). You need to recompile the target binary + after changing this - otherwise, SEGVs may ensue. */ + +#define MAP_SIZE_POW2 16 +#define MAP_SIZE (1 << MAP_SIZE_POW2) + +/* Maximum allocator request size (keep well under INT_MAX): */ + +#define MAX_ALLOC 0x40000000 + +/* A made-up hashing seed: */ + +#define HASH_CONST 0xa5b35705 + +/* Constants for afl-gotcpu to control busy loop timing: */ + +#define CTEST_TARGET_MS 5000 +#define CTEST_CORE_TRG_MS 1000 +#define CTEST_BUSY_CYCLES (10 * 1000 * 1000) + +/* Uncomment this to use inferior block-coverage-based instrumentation. Note + that you need to recompile the target binary for this to have any effect: */ + +// #define COVERAGE_ONLY + +/* Uncomment this to ignore hit counts and output just one bit per tuple. + As with the previous setting, you will need to recompile the target + binary: */ + +// #define SKIP_COUNTS + +/* Uncomment this to use instrumentation data to record newly discovered paths, + but do not use them as seeds for fuzzing. This is useful for conveniently + measuring coverage that could be attained by a "dumb" fuzzing algorithm: */ + +// #define IGNORE_FINDS + +#endif /* ! _HAVE_CONFIG_H */ diff --git a/compilers/debug.h b/compilers/debug.h new file mode 100644 index 0000000..a943a57 --- /dev/null +++ b/compilers/debug.h @@ -0,0 +1,251 @@ +/* + american fuzzy lop - debug / error handling macros + -------------------------------------------------- + + Written and maintained by Michal Zalewski + + Copyright 2013, 2014, 2015, 2016 Google Inc. 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 + + */ + +#ifndef _HAVE_DEBUG_H +#define _HAVE_DEBUG_H + +#include + +#include "types.h" +#include "config.h" + +/******************* + * Terminal colors * + *******************/ + +#ifdef USE_COLOR + +# define cBLK "\x1b[0;30m" +# define cRED "\x1b[0;31m" +# define cGRN "\x1b[0;32m" +# define cBRN "\x1b[0;33m" +# define cBLU "\x1b[0;34m" +# define cMGN "\x1b[0;35m" +# define cCYA "\x1b[0;36m" +# define cLGR "\x1b[0;37m" +# define cGRA "\x1b[1;90m" +# define cLRD "\x1b[1;91m" +# define cLGN "\x1b[1;92m" +# define cYEL "\x1b[1;93m" +# define cLBL "\x1b[1;94m" +# define cPIN "\x1b[1;95m" +# define cLCY "\x1b[1;96m" +# define cBRI "\x1b[1;97m" +# define cRST "\x1b[0m" + +# define bgBLK "\x1b[40m" +# define bgRED "\x1b[41m" +# define bgGRN "\x1b[42m" +# define bgBRN "\x1b[43m" +# define bgBLU "\x1b[44m" +# define bgMGN "\x1b[45m" +# define bgCYA "\x1b[46m" +# define bgLGR "\x1b[47m" +# define bgGRA "\x1b[100m" +# define bgLRD "\x1b[101m" +# define bgLGN "\x1b[102m" +# define bgYEL "\x1b[103m" +# define bgLBL "\x1b[104m" +# define bgPIN "\x1b[105m" +# define bgLCY "\x1b[106m" +# define bgBRI "\x1b[107m" + +#else + +# define cBLK "" +# define cRED "" +# define cGRN "" +# define cBRN "" +# define cBLU "" +# define cMGN "" +# define cCYA "" +# define cLGR "" +# define cGRA "" +# define cLRD "" +# define cLGN "" +# define cYEL "" +# define cLBL "" +# define cPIN "" +# define cLCY "" +# define cBRI "" +# define cRST "" + +# define bgBLK "" +# define bgRED "" +# define bgGRN "" +# define bgBRN "" +# define bgBLU "" +# define bgMGN "" +# define bgCYA "" +# define bgLGR "" +# define bgGRA "" +# define bgLRD "" +# define bgLGN "" +# define bgYEL "" +# define bgLBL "" +# define bgPIN "" +# define bgLCY "" +# define bgBRI "" + +#endif /* ^USE_COLOR */ + +/************************* + * Box drawing sequences * + *************************/ + +#ifdef FANCY_BOXES + +# define SET_G1 "\x1b)0" /* Set G1 for box drawing */ +# define RESET_G1 "\x1b)B" /* Reset G1 to ASCII */ +# define bSTART "\x0e" /* Enter G1 drawing mode */ +# define bSTOP "\x0f" /* Leave G1 drawing mode */ +# define bH "q" /* Horizontal line */ +# define bV "x" /* Vertical line */ +# define bLT "l" /* Left top corner */ +# define bRT "k" /* Right top corner */ +# define bLB "m" /* Left bottom corner */ +# define bRB "j" /* Right bottom corner */ +# define bX "n" /* Cross */ +# define bVR "t" /* Vertical, branch right */ +# define bVL "u" /* Vertical, branch left */ +# define bHT "v" /* Horizontal, branch top */ +# define bHB "w" /* Horizontal, branch bottom */ + +#else + +# define SET_G1 "" +# define RESET_G1 "" +# define bSTART "" +# define bSTOP "" +# define bH "-" +# define bV "|" +# define bLT "+" +# define bRT "+" +# define bLB "+" +# define bRB "+" +# define bX "+" +# define bVR "+" +# define bVL "+" +# define bHT "+" +# define bHB "+" + +#endif /* ^FANCY_BOXES */ + +/*********************** + * Misc terminal codes * + ***********************/ + +#define TERM_HOME "\x1b[H" +#define TERM_CLEAR TERM_HOME "\x1b[2J" +#define cEOL "\x1b[0K" +#define CURSOR_HIDE "\x1b[?25l" +#define CURSOR_SHOW "\x1b[?25h" + +/************************ + * Debug & error macros * + ************************/ + +/* Just print stuff to the appropriate stream. */ + +#ifdef MESSAGES_TO_STDOUT +# define SAYF(x...) printf(x) +#else +# define SAYF(x...) fprintf(stderr, x) +#endif /* ^MESSAGES_TO_STDOUT */ + +/* Show a prefixed warning. */ + +#define WARNF(x...) do { \ + SAYF(cYEL "[!] " cBRI "WARNING: " cRST x); \ + SAYF(cRST "\n"); \ + } while (0) + +/* Show a prefixed "doing something" message. */ + +#define ACTF(x...) do { \ + SAYF(cLBL "[*] " cRST x); \ + SAYF(cRST "\n"); \ + } while (0) + +/* Show a prefixed "success" message. */ + +#define OKF(x...) do { \ + SAYF(cLGN "[+] " cRST x); \ + SAYF(cRST "\n"); \ + } while (0) + +/* Show a prefixed fatal error message (not used in afl). */ + +#define BADF(x...) do { \ + SAYF(cLRD "\n[-] " cRST x); \ + SAYF(cRST "\n"); \ + } while (0) + +/* Die with a verbose non-OS fatal error message. */ + +#define FATAL(x...) do { \ + SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD "\n[-] PROGRAM ABORT : " \ + cBRI x); \ + SAYF(cLRD "\n Location : " cRST "%s(), %s:%u\n\n", \ + __FUNCTION__, __FILE__, __LINE__); \ + exit(1); \ + } while (0) + +/* Die by calling abort() to provide a core dump. */ + +#define ABORT(x...) do { \ + SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD "\n[-] PROGRAM ABORT : " \ + cBRI x); \ + SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n\n", \ + __FUNCTION__, __FILE__, __LINE__); \ + abort(); \ + } while (0) + +/* Die while also including the output of perror(). */ + +#define PFATAL(x...) do { \ + fflush(stdout); \ + SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD "\n[-] SYSTEM ERROR : " \ + cBRI x); \ + SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n", \ + __FUNCTION__, __FILE__, __LINE__); \ + SAYF(cLRD " OS message : " cRST "%s\n", strerror(errno)); \ + exit(1); \ + } while (0) + +/* Die with FAULT() or PFAULT() depending on the value of res (used to + interpret different failure modes for read(), write(), etc). */ + +#define RPFATAL(res, x...) do { \ + if (res < 0) PFATAL(x); else FATAL(x); \ + } while (0) + +/* Error-checking versions of read() and write() that call RPFATAL() as + appropriate. */ + +#define ck_write(fd, buf, len, fn) do { \ + u32 _len = (len); \ + s32 _res = write(fd, buf, _len); \ + if (_res != _len) RPFATAL(_res, "Short write to %s", fn); \ + } while (0) + +#define ck_read(fd, buf, len, fn) do { \ + u32 _len = (len); \ + s32 _res = read(fd, buf, _len); \ + if (_res != _len) RPFATAL(_res, "Short read from %s", fn); \ + } while (0) + +#endif /* ! _HAVE_DEBUG_H */ diff --git a/compilers/hash.h b/compilers/hash.h new file mode 100644 index 0000000..f39a825 --- /dev/null +++ b/compilers/hash.h @@ -0,0 +1,104 @@ +/* + american fuzzy lop - hashing function + ------------------------------------- + + The hash32() function is a variant of MurmurHash3, a good + non-cryptosafe hashing function developed by Austin Appleby. + + For simplicity, this variant does *NOT* accept buffer lengths + that are not divisible by 8 bytes. The 32-bit version is otherwise + similar to the original; the 64-bit one is a custom hack with + mostly-unproven properties. + + Austin's original code is public domain. + + Other code written and maintained by Michal Zalewski + + Copyright 2016 Google Inc. 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 + + */ + +#ifndef _HAVE_HASH_H +#define _HAVE_HASH_H + +#include "types.h" + +#ifdef __x86_64__ + +#define ROL64(_x, _r) ((((u64)(_x)) << (_r)) | (((u64)(_x)) >> (64 - (_r)))) + +static inline u32 hash32(const void* key, u32 len, u32 seed) { + + const u64* data = (u64*)key; + u64 h1 = seed ^ len; + + len >>= 3; + + while (len--) { + + u64 k1 = *data++; + + k1 *= 0x87c37b91114253d5ULL; + k1 = ROL64(k1, 31); + k1 *= 0x4cf5ad432745937fULL; + + h1 ^= k1; + h1 = ROL64(h1, 27); + h1 = h1 * 5 + 0x52dce729; + + } + + h1 ^= h1 >> 33; + h1 *= 0xff51afd7ed558ccdULL; + h1 ^= h1 >> 33; + h1 *= 0xc4ceb9fe1a85ec53ULL; + h1 ^= h1 >> 33; + + return h1; + +} + +#else + +#define ROL32(_x, _r) ((((u32)(_x)) << (_r)) | (((u32)(_x)) >> (32 - (_r)))) + +static inline u32 hash32(const void* key, u32 len, u32 seed) { + + const u32* data = (u32*)key; + u32 h1 = seed ^ len; + + len >>= 2; + + while (len--) { + + u32 k1 = *data++; + + k1 *= 0xcc9e2d51; + k1 = ROL32(k1, 15); + k1 *= 0x1b873593; + + h1 ^= k1; + h1 = ROL32(h1, 13); + h1 = h1 * 5 + 0xe6546b64; + + } + + h1 ^= h1 >> 16; + h1 *= 0x85ebca6b; + h1 ^= h1 >> 13; + h1 *= 0xc2b2ae35; + h1 ^= h1 >> 16; + + return h1; + +} + +#endif /* ^__x86_64__ */ + +#endif /* !_HAVE_HASH_H */ diff --git a/compilers/test-instr.c b/compilers/test-instr.c new file mode 100644 index 0000000..09a1c9f --- /dev/null +++ b/compilers/test-instr.c @@ -0,0 +1,37 @@ +/* + american fuzzy lop - a trivial program to test the build + -------------------------------------------------------- + + Written and maintained by Michal Zalewski + + Copyright 2014 Google Inc. 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 + + */ + +#include +#include +#include + +int main(int argc, char** argv) { + + char buf[8]; + + if (read(0, buf, 8) < 1) { + printf("Hum?\n"); + exit(1); + } + + if (buf[0] == '0') + printf("Looks like a zero to me!\n"); + else + printf("A non-zero value? How quaint!\n"); + + exit(0); + +} diff --git a/compilers/types.h b/compilers/types.h new file mode 100644 index 0000000..784d3a7 --- /dev/null +++ b/compilers/types.h @@ -0,0 +1,86 @@ +/* + american fuzzy lop - type definitions and minor macros + ------------------------------------------------------ + + Written and maintained by Michal Zalewski + + Copyright 2013, 2014, 2015 Google Inc. 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 + + */ + +#ifndef _HAVE_TYPES_H +#define _HAVE_TYPES_H + +#include +#include + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; + +/* + + Ugh. There is an unintended compiler / glibc #include glitch caused by + combining the u64 type an %llu in format strings, necessitating a workaround. + + In essence, the compiler is always looking for 'unsigned long long' for %llu. + On 32-bit systems, the u64 type (aliased to uint64_t) is expanded to + 'unsigned long long' in , so everything checks out. + + But on 64-bit systems, it is #ifdef'ed in the same file as 'unsigned long'. + Now, it only happens in circumstances where the type happens to have the + expected bit width, *but* the compiler does not know that... and complains + about 'unsigned long' being unsafe to pass to %llu. + + */ + +#ifdef __x86_64__ +typedef unsigned long long u64; +#else +typedef uint64_t u64; +#endif /* ^__x86_64__ */ + +typedef int8_t s8; +typedef int16_t s16; +typedef int32_t s32; +typedef int64_t s64; + +#ifndef MIN +# define MIN(_a,_b) ((_a) > (_b) ? (_b) : (_a)) +# define MAX(_a,_b) ((_a) > (_b) ? (_a) : (_b)) +#endif /* !MIN */ + +#define SWAP16(_x) ({ \ + u16 _ret = (_x); \ + (u16)((_ret << 8) | (_ret >> 8)); \ + }) + +#define SWAP32(_x) ({ \ + u32 _ret = (_x); \ + (u32)((_ret << 24) | (_ret >> 24) | \ + ((_ret << 8) & 0x00FF0000) | \ + ((_ret >> 8) & 0x0000FF00)); \ + }) + +#ifdef AFL_LLVM_PASS +# define AFL_R(x) (random() % (x)) +#else +# define R(x) (random() % (x)) +#endif /* ^AFL_LLVM_PASS */ + +#define STRINGIFY_INTERNAL(x) #x +#define STRINGIFY(x) STRINGIFY_INTERNAL(x) + +#define MEM_BARRIER() \ + asm volatile("" ::: "memory") + +#define likely(_x) __builtin_expect(!!(_x), 1) +#define unlikely(_x) __builtin_expect(!!(_x), 0) + +#endif /* ! _HAVE_TYPES_H */ diff --git a/docs/BUILD.md b/docs/BUILD.md index 4a6c3af..c358fe5 100644 --- a/docs/BUILD.md +++ b/docs/BUILD.md @@ -89,6 +89,11 @@ be found in folders named after the architecture (e.g. x64) and build type To build Killerbeez on Linux/Mac you will need a compiler (gcc or clang), make, and cmake. +For Ubuntu, it's as simple as: +``` +sudo apt install build-essential cmake +``` + ### Installation Clone the killerbeez, killerbeez-mutators and killerbeez-utils repos. diff --git a/docs/Server.md b/docs/Server.md index 78d1095..851d896 100644 --- a/docs/Server.md +++ b/docs/Server.md @@ -23,37 +23,37 @@ ``` 3. Build code ``` - cd killerbeez/server/boinc - ./_autosetup - ./configure --disable-client --disable-manager - make + cd killerbeez/server/boinc + ./_autosetup + ./configure --disable-client --disable-manager + make ``` 4. User permissions ``` - sudo useradd -m -s /bin/bash boincadm - sudo usermod -a -G boincadm www-data - sudo -u boincadm sh -c 'echo umask 0007 >> /home/boincadm/.bashrc' - sudo sh -c 'echo umask 0007 >> /etc/apache2/envvars' - sudo chgrp boincadm /usr/local/killerbeez - sudo chmod g+w /usr/local/killerbeez + sudo useradd -m -s /bin/bash boincadm + sudo usermod -a -G boincadm www-data + sudo -u boincadm sh -c 'echo umask 0007 >> /home/boincadm/.bashrc' + sudo sh -c 'echo umask 0007 >> /etc/apache2/envvars' + sudo chgrp boincadm /usr/local/killerbeez + sudo chmod g+w /usr/local/killerbeez ``` * Note: the new user doesn't have sudo access, so continue using your normal account for the remaining instructions except when indicated. 5. MySQL setup (make sure to select your own password) - ``` - mysql -u root -p - mysql> CREATE USER 'killerbeez'@'localhost' IDENTIFIED BY ''; - mysql> GRANT ALL ON killerbeez.* to 'killerbeez'@'localhost'; - ``` - ``` - sudo sh -c 'echo sql_mode="ONLY_FULL_GROUP_BY,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION" >> /etc/mysql/mysql.conf.d/mysqld.cnf' - sudo systemctl restart mysql - ``` + ``` + mysql -u root -p + mysql> CREATE USER 'killerbeez'@'localhost' IDENTIFIED BY ''; + mysql> GRANT ALL ON killerbeez.* to 'killerbeez'@'localhost'; + ``` + ``` + sudo sh -c 'echo sql_mode="ONLY_FULL_GROUP_BY,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION" >> /etc/mysql/mysql.conf.d/mysqld.cnf' + sudo systemctl restart mysql + ``` 6. Apache setup - ``` - sudo a2enmod cgi - sudo systemctl restart apache2 - ``` + ``` + sudo a2enmod cgi + sudo systemctl restart apache2 + ``` 7. Project setup (run as the boincadm user) 1. First, open a shell as the boincadm user for the rest of this step @@ -65,7 +65,7 @@ will be a public instance, take a look at [these guidelines](https://boinc.berkeley.edu/trac/wiki/MasterUrl#ChoosingaprojectURL). * `export BOINC_URL=` - 3. Create the BOINC project for Killerbeez + 3. Create the BOINC project for Killerbeez (using the passord from step 5 above) ``` cd /usr/local/killerbeez/killerbeez/server/boinc @@ -74,7 +74,7 @@ ``` * Enter Y at the prompt. 4. Set up cron, load platforms into the database, and set a password to - access the admin UI + access the admin UI (admin username does not need to be boincadm) ``` cd ~/projects/killerbeez crontab killerbeez.cronjob @@ -88,7 +88,11 @@ cp /usr/local/killerbeez/killerbeez/server/*.py bin cp -r /usr/local/killerbeez/killerbeez/server/skel . ``` - 7. Download the latest + 7. Killerbeez does not use the BOINC API, however BOINC has a wrapper which + wraps an executable and deals with all the BOINC-specific stuff. This + allows any application to be leveraged by BOINC. We need this wrapper + program for all platforms which we intend to support. For Windows, + download the latest [killerbeez-x64.zip](https://github.com/grimm-co/killerbeez/releases) and place it in the `skel/windows_x86_64` directory 8. Extract the wrapper binary (or see the [build instructions](#wrapper) to @@ -97,11 +101,14 @@ unzip -j -d skel/windows_x86_64 skel/windows_x86_64/killerbeez-x64.zip \ '*wrapper_26014_windows_x86_64.exe' ``` - 6. Enable the project + 9. If you want to support Linux or Mac, get the approripriate wrappers + from the [BOINC wiki](https://boinc.berkeley.edu/trac/wiki/WrapperApp) + and put the executables in the appropriate place in the skel/ directory. + 10. Enable the project ``` bin/start ``` - 7. Done running as boincadm + 11. Done running as boincadm ``` exit @@ -130,22 +137,34 @@ This will start up the server on port 5000. In the instructions below, we will call the URL pointing to this port `$API_URL`. -### Client - Windows 10 x64 only for now +### Client +Next we need to set up at least one client. Follow these instructions first, +then the operating system specific instructions below. If there aren't any +instructions for your operating system, you're on your own (when if you figure +it out, we accept pull requests for improved documentation :-)). + 1. Create an account via BOINC webpage (`$BOINC_URL/killerbeez/create_account_form.php`) + +#### Linux (Ubuntu) +1. sudo apt install boinc-client +2. Get an account key using: `boinccmd --lookup_account $BOINC_URL/killerbeez/ ` +3. `boinccmd --project_attach $BOINC_URL/killerbeez/ ` + +#### Windows +Note: only Windows 10 x64 is currently supported + +1. Log into the BOINC webpage (`$BOINC_URL/killerbeez`) 2. GUI instructions to add project: - 1. Go to Project > Join on website - 2. If client not installed, install it - 3. Select "Add project" in GUI - 4. Enter project URL from webpage - 5. Enter email address and password of the account you registered in step 1 -3. Command-line instructions to add project: - 1. Go to Project > Join on website - 2. If client not installed, install it (e.g., `apt install boinc-client`) - 3. `boinccmd --lookup_account ` - * Returns account key - 4. `boinccmd --project_attach ` + 1. Go to Project > Join on website + 2. If client not installed, install it + 3. Select "Add project" in GUI + 4. Enter project URL from webpage + 5. Enter email address and password of the account you registered in step 1 ## Administration +All administration of the BOINC server which is done from the command line +should be done as the "boincadm" user. You can use `sudo -i -u boincadm` to +drop to an interactive shell with this user. ### Add a target Killerbeez jobs have a "target", which represents a target program running on a @@ -158,18 +177,31 @@ Player. 1. Create the target +A list of platforms (operating/cpu architecture) can be found on the [BOINC +Wiki](https://boinc.berkeley.edu/trac/wiki/BoincPlatforms). + ``` + cd ~/projects/killerbeez/ bin/add_target.py [ ...] ``` * Example: `bin/add_target.py wmp windows_x86_64` 2. If you need any additional files in this app, put them in the app dir - (`apps/_/1/`) + (`apps/_/1/`). At a minimum, you will probably + want to put the executable there (unless the assumption is that it is already + on the clients' computers). 3. Finalize the app creation ``` bin/update_versions ``` +Supporting a new platform is kind of a big deal. If you want to take a stab at +it, take a look at the comments in add_target.py to get started. You will +probably want to have the [BOINC wiki](https://boinc.berkeley.edu/trac/wiki) +loaded up, especially if you're not already familiar with how the implementation +details of BOINC. + + ### Submit job Customize the [boinc_submit.py](../server/boinc_submit.py) script for your desired job by changing the constants at the top, and the job parameters in the diff --git a/docs/api/api_instrumentation.tex b/docs/api/api_instrumentation.tex index 5af211b..e42dac1 100644 --- a/docs/api/api_instrumentation.tex +++ b/docs/api/api_instrumentation.tex @@ -11,7 +11,15 @@ \par Anything which is instrumentation specific will only be used within the instrumentation functions. All other components will treat these items as -opaque strings/blobs. +opaque strings/blobs. The typical order of calling instrumentation functions +is create(), enable(), is_process_done(), get_fuzz_result(). + +\par +As an implementation note, all of the instrumentation modules can not have +the same name for functions like ``create'' or there will be a naming +collision. This is currently handled in instrumentation_factory.c by setting +an instrumentation structure which maps the names listed here to the actual +functions which back them for the selected instrumentation. \api{void * create(char * options, char * state) }{ @@ -39,7 +47,7 @@ referenced afterwards } -\api{char * get\_state(void * instrumentation\_state, int *out\_length) +\api{char * get\_state(void * instrumentation\_state) }{ This function will return the state information holding the previous execution path info. The returned value can later be passed to the instrumentation @@ -49,8 +57,6 @@ }{ \item instrumentation\_state - an instrumentation specific structure previously created by the create function -\item out\_length - this pointer will be filled with the length of the returned -state buffer \item return value - a buffer that holds information about the previous execution paths as a JSON char array. } @@ -99,7 +105,7 @@ coverage information from both of the instrumentation states or NULL on failure } -\api{int enable(void * instrumentation\_state, HANDLE * process, char * cmd\_line, +\api{int enable(void * instrumentation\_state, void * process, char * cmd\_line, char * input, size\_t input\_length) }{ This function will enable the instrumentation module for a specific process and @@ -108,7 +114,8 @@ \item instrumentation\_state - an instrumentation specific structure previously created by the create() function \item process - a pointer to a handle for the process on which the -instrumentation was enabled +instrumentation was enabled. On Windows, this is a HANDLE*, on UNIX-based +systems it is a pid_t* \item cmd\_line - the command line of the fuzzed process on which to enable instrumentation \item input - pointer to the buffer containing the input data that should be @@ -136,7 +143,8 @@ after the process has finished processing the tested input, i.e. after a successful \texttt{is\_process\_done}. If \texttt{get\_fuzz\_result} is called prior to \texttt{enable}, it will return failure, as the fuzzing of processes -has not been started yet. +has not been started yet. Because this is only called once the fuzzing is +done, it will generally clean up resources such as reaping the target process. }{ \item instrumentation\_state - an instrumentation specific structure previously created by the create() function diff --git a/driver/driver.c b/driver/driver.c index 4b789d1..6c59a28 100755 --- a/driver/driver.c +++ b/driver/driver.c @@ -75,8 +75,11 @@ int generic_wait_for_process_completion(pid_t process, int timeout, instrumentat int generic_test_next_input(void * state, mutator_t * mutator, void * mutator_state, char * buffer, size_t buffer_length, int (*test_input_func)(void * driver_state, char * buffer, size_t length), int * mutate_last_size) { - if (!mutator) + if (!mutator) { + ERROR_MSG("Mutator module missing!"); return -1; + } + DEBUG_MSG("Mutating input..."); *mutate_last_size = mutator->mutate(mutator_state, buffer, buffer_length); if (*mutate_last_size < 0) return -1; diff --git a/driver/file_driver.c b/driver/file_driver.c index 770200a..229084b 100755 --- a/driver/file_driver.c +++ b/driver/file_driver.c @@ -189,9 +189,11 @@ int file_test_input(void * driver_state, char * input, size_t length) file_state_t * state = (file_state_t *)driver_state; //Write the input to disk + DEBUG_MSG("Writing input to disk..."); write_buffer_to_file(state->test_filename, input, length); //Start the process and give it our input + DEBUG_MSG("Enabling instrumentation module..."); if(state->instrumentation->enable(state->instrumentation_state, &state->process, state->cmd_line, NULL, 0)) return FUZZ_ERROR; diff --git a/fuzzer/main.c b/fuzzer/main.c index fa990c5..c72674b 100755 --- a/fuzzer/main.c +++ b/fuzzer/main.c @@ -390,22 +390,22 @@ int main(int argc, char ** argv) } directory = NULL; - if (fuzz_result == FUZZ_CRASH) + if (fuzz_result == FUZZ_CRASH) { directory = "crashes"; - else if (fuzz_result == FUZZ_HANG) + CRITICAL_MSG("Found %s", directory); + } else if (fuzz_result == FUZZ_HANG) { directory = "hangs"; - else if (new_path > 0) + ERROR_MSG("Found %s", directory); + } else if (new_path > 0) { directory = "new_paths"; + INFO_MSG("Found %s", directory); + } - if (directory != NULL) - { - CRITICAL_MSG("Found %s", directory); - + if (directory != NULL) { mutate_buffer = driver->get_last_input(driver->state, &mutate_length); - if (!mutate_buffer) + if (!mutate_buffer) { ERROR_MSG("Unable to dump mutate buffer\n"); - else - { + } else { if (output_directory) { md5((uint8_t *)mutate_buffer, mutate_length, filehash, sizeof(filehash)); snprintf(filename, MAX_PATH, "%s/%s/%s", output_directory, directory, filehash); diff --git a/instrumentation/CMakeLists.txt b/instrumentation/CMakeLists.txt index 2c8aa2c..678d629 100644 --- a/instrumentation/CMakeLists.txt +++ b/instrumentation/CMakeLists.txt @@ -16,6 +16,7 @@ else () set(INSTRUMENTATION_SRC ${INSTRUMENTATION_SRC} ${PROJECT_SOURCE_DIR}/return_code_instrumentation.c + ${PROJECT_SOURCE_DIR}/afl_instrumentation.c ) if (NOT APPLE) diff --git a/instrumentation/afl_instrumentation.c b/instrumentation/afl_instrumentation.c new file mode 100644 index 0000000..d524cde --- /dev/null +++ b/instrumentation/afl_instrumentation.c @@ -0,0 +1,535 @@ +#include // for shm functions +#include // for NULL +#include // for lseek, write, ftruncate + +#include // for FUZZ_* return values + +#include // for PARSE_OPTION_* + +#include "afl_instrumentation.h" + +// module level variables needed for callbacks +int shm_id; + +/** + * This function allocates and initializes a new instrumentation specific state + * object based on the given options. + * @param options - a JSON string that contains the instrumentation specific + * string of options + * @param state - an instrumentation specific JSON string previously returned + * from afl_get_state that should be loaded + * @return - An instrumentation specific state object on success or NULL on failure + */ +void * afl_create(char *options, char *state) { + afl_state_t *afl_state = setup_options(options); + if(!afl_state) + return NULL; + + if(state && afl_set_state(afl_state, state)) { + DEBUG_MSG("Unable to set state for afl instrumentation"); + return NULL; + } + + return afl_state; +} + +/** + * This function cleans up all resources with the passed in instrumentation state. + * @param instrumentation_state - an instrumentation specific state object + * previously created by the afl_create function + * This state object should not be referenced after + * this function returns. + */ +void afl_cleanup(void *instrumentation_state) { + afl_state_t * state = (afl_state_t *)instrumentation_state; + + free(state->target_path); + + // Cleanup the fork server if necessary + if(state->fork_server_setup) { + fork_server_exit(&state->fs); + state->fork_server_setup = 0; + } +} + +char * afl_get_state(void *instrumentation_state) { + return NULL; +} + +void afl_free_state(char *state) { +} + +int afl_set_state(void *instrumentation_state, char *state) { + return -1; +} + +void * afl_merge(void *instrumentation_state, void *other_instrumentation_state) { + return NULL; +} + +/** + * This function enables the instrumentation and runs the fuzzed process. + * @param instrumentation_state - an instrumentation specific state object + * previously created by the afl_create function + * @process - a pointer to return a pid_t to the process that the + * instrumentation was enabled on + * @cmd_line - the command line of the fuzzed process to enable instrumentation on + * @input - a buffer to the input that should be sent to the fuzzed process + * @input_length - the length of the input parameter + * returns 0 on success, -1 on failure + */ +int afl_enable(void *instrumentation_state, pid_t *process, char *cmd_line, + char *input, size_t input_length) { + afl_state_t * state = (afl_state_t *)instrumentation_state; + char ** argv; + + // If there's already a child process, get rid of it + if(state->child_pid) { + destroy_target_process(state); + } + + if(create_target_process(state, cmd_line, input, input_length)) + return -1; + state->process_finished = 0; + state->fuzz_results_set = 0; + + *process = state->child_pid; + return 0; +} + +/** + * This function determines if a new path was covered + * @param instrumentation_state - an instrumentation specific state object + * previously created by the afl_create function + * @return - 1 if the previously setup process (via the enable function) took a + * new path, 0 if it did not, or -1 on failure. + */ +int afl_is_new_path(void *instrumentation_state) { + afl_state_t * state = (afl_state_t *)instrumentation_state; + + // If we haven't set the fuzz results, do that and return the result + if(!state->fuzz_results_set) + finish_fuzz_round(state); + if(state->last_is_new_path) + return 1; + return 0; +} + +/** + * This function will return the result of the fuzz job. It should be called + * after the process has finished processing the tested input. The target + * process will also be cleaned up in the process. + * @param instrumentation_state - an instrumentation specific structure + * previously created by the afl_create function + * @return - either FUZZ_NONE, FUZZ_HANG, FUZZ_CRASH, or -1 on error. + */ +int afl_get_fuzz_result(void *instrumentation_state) { + afl_state_t * state = (afl_state_t *)instrumentation_state; + + // If we haven't set the fuzz results, do that and return the result + if(!state->fuzz_results_set) + return finish_fuzz_round(state); + + // otherwise we can just return the result + return state->last_fuzz_result; +} + +/** + * This function determines if the target process CRASHED, HUNG or EXITED + * NORMALLY, cleans up the process, and checks to see if any new code was + * executed. The assumption is that if you are calling this function, + * you're sick of waiting for the child, so if it is still executing by the + * time we get here, we're calling it a HANG. This is implemented as an + * internal function so we can use it when the caller calls any of the post- + * fuzzing functions, such as get_fuzz_result or is_new_path(). + * + * @param state - The AFL specific state structure + * @return - either FUZZ_NONE, FUZZ_HANG, FUZZ_CRASH, or -1 on error. + */ +static int finish_fuzz_round(afl_state_t *state) { + int status, rc; + + // if our process is still running, then it was a hang + if(!state->process_finished) { + destroy_target_process(state); + state->last_fuzz_result = FUZZ_HANG; +#ifdef __x86_64__ + simplify_trace((uint64_t*)state->trace_bits); +#else + simplify_trace((uint32_t*)state->trace_bits); +#endif /* ^__x86_64__ */ + state->last_is_new_path = has_new_bits(state->virgin_tmout, state->trace_bits); + DEBUG_MSG("Process hung, has_new_bits = %d", state->last_is_new_path); + state->fuzz_results_set = 1; + } else if(WIFEXITED(state->last_status)) { + state->last_fuzz_result = FUZZ_NONE; // process exited normally + state->last_is_new_path = has_new_bits(state->virgin_bits, state->trace_bits); + DEBUG_MSG("Process exited normally, has_new_bits = %d", state->last_is_new_path); + state->fuzz_results_set = 1; + } else if(WIFSIGNALED(state->last_status)) { + // process was terminated by a signal, we don't really care which one... + state->last_fuzz_result = FUZZ_CRASH; +#ifdef __x86_64__ + simplify_trace((uint64_t*)state->trace_bits); +#else + simplify_trace((uint32_t*)state->trace_bits); +#endif /* ^__x86_64__ */ + state->last_is_new_path = has_new_bits(state->virgin_crash, state->trace_bits); + DEBUG_MSG("Process crashed, has_new_bits = %d", state->last_is_new_path); + state->fuzz_results_set = 1; + } else { + // if it didn't exit normally, nor get interrupted by a signal... + // I'm not sure what happened! + return FUZZ_ERROR; + } + return state->last_fuzz_result; +} + +/** + * Checks if the target process is done fuzzing the inputs yet. + * @param instrumentation_state - The afl_state_t object containing this + * instrumentation's state + * @return - 0 if the process is not done testing the fuzzed input, + * non-zero if the process is done. + */ +int afl_is_process_done(void *instrumentation_state) { + int status, rc; + afl_state_t * state = (afl_state_t *)instrumentation_state; + + // If the state says we're done, our job is easy! + if(state->process_finished) + return 1; + + if(state->use_fork_server) { + status = fork_server_get_status(&state->fs, 0); + // if it's still alive or an error occurred and we can't tell + if(status < 0 || status == FORKSERVER_NO_RESULTS_READY) + return 0; + state->last_status = status; + state->process_finished = 1; + return 1; + } else { + // We just need to check to see if the process is still alive + DEBUG_MSG("Calling waitpid() to see if we're done..."); + rc = waitpid(state->child_pid, &status, WNOHANG); + DEBUG_MSG("status=%d ; waitpid() returned: %d", status, rc); + if(rc == 0) // child did not change state + return 0; + if(rc == state->child_pid) { + // our child changed state (exited, received a signal, etc.) + state->last_status = status; // Record it + state->child_pid = 0; // We no longer have a child process + state->process_finished = 1; // Mark that we're done + return 1; + } + if(rc == -1) // waitpid failed + return -1; + ERROR_MSG("waitpid() said pid %d changed state but our child was %d", + rc, state->child_pid); + return -1; // Some other child process changed state? + } + + ERROR_MSG("Fell through to end of afl_is_process_done()."); + return -1; +} + +int afl_help(char **help_str) { + *help_str = strdup( + "afl - AFL-based instrumentation\n" + "Options:\n" + " use_fork_server Whether to use a fork server; 1=yes, 0=no (default=1)\n" +// " persistence_max_cnt The number of executions to run in one process while\n" +// " fuzzing in persistence mode (default=1)\n" +// " qemu_mode Whether to use qemu mode; 1=yes, 0=no (default=0)\n" + "\n" + ); + if (*help_str == NULL) + return -1; + return 0; +} + +/** + * This function creates a linux_ipt_state_t object based on the given options. + * @param options - A JSON string of the options to set in the new + * afl_state_t. See the help function for more information on + * the specific options available. + * @return the afl_state_t generated from the options in the JSON options + * string, or NULL on failure + */ +static afl_state_t * setup_options(char *options) { + afl_state_t * state; + + state = malloc(sizeof(afl_state_t)); + if(!state) + return NULL; + memset(state, 0, sizeof(afl_state_t)); + state->use_fork_server = 1; // default to use the fork server + + if(options) { + DEBUG_MSG("JSON options = %s", options); + PARSE_OPTION_INT(state, options, use_fork_server, + "use_fork_server", afl_cleanup); +// PARSE_OPTION_INT(state, options, persistence_max_cnt, +// "persistence_max_cnt", afl_cleanup); + } + + return state; +} + +/** + * This function starts the fuzzed process + * @param state - The afl_state_t object containing this instrumentation's state + * @param cmd_line - the command line of the fuzzed process to start + * @param input - a buffer to the input that should be sent to the fuzzed process + * @param input_length - the length of the input parameter + * @return - zero on success, non-zero on failure. + */ +static int create_target_process(afl_state_t * state, char* cmd_line, + char * input, size_t input_length) { + char ** argv; + int i; + + DEBUG_MSG("Splitting command line arguments..."); + if(split_command_line(cmd_line, &state->target_path, &argv)) + return -1; + if(state->use_fork_server) { + DEBUG_MSG("Using fork server..."); + if(!state->fork_server_setup) { + // Set up shared memory before starting the fork server + if(setup_shm(state)) + return -1; + fork_server_init(&state->fs, state->target_path, argv, 1, + state->persistence_max_cnt, input_length != 0); + state->fork_server_setup = 1; + } + if(fork_server_run(&state->fs)) + return -1; + } else { + // Set up shared memory before executing the target program + if(setup_shm(state)) + return -1; + DEBUG_MSG("Not using fork server, executing %s", state->target_path); + state->child_pid = run_target(input_length != 0, + state->target_path, argv, + NULL, 0, NULL, NULL, 0); + DEBUG_MSG("Child process ID = %d", state->child_pid); + } + + if(state->use_fork_server) { + state->child_pid = fork_server_fork(&state->fs); + if(state->child_pid < 0) + return -1; + } + + for(i = 0; argv[i]; i++) + free(argv[i]); + free(argv); + + return 0; +} + +/** + * This function terminates the fuzzed process. + * @param state - The afl_state_t object containing this instrumentation's state + */ +static void destroy_target_process(afl_state_t * state) { + if(state->child_pid) { + DEBUG_MSG("Cleaning up old child process (pid=%d)", state->child_pid); + if(!state->persistence_max_cnt) { + kill(state->child_pid, SIGKILL); + state->child_pid = 0; + } + if(state->use_fork_server) { + state->last_status = fork_server_get_status(&state->fs, 1); + } + } +} + +/** + * This sets up the shared memory between our fuzzer and the target process + * being fuzzed. The target process will write to this as it executes and + * we will read it once the fuzzing is complete (crash, hang, or normal exit) + * @param instrumentation_state - The afl_state_t object containing this + * instrumentation's state + * @returns zero on success, non-zero on error + */ +int setup_shm(void *instrumentation_state) { + /* + This function is based on the AFL setup_shm function present in afl-fuzz.c, + available at this URL: + https://github.com/mirrorer/afl/blob/master/afl-fuzz.c#L1968. + AFL's license is as shown below: + + american fuzzy lop - fuzzer code + -------------------------------- + Written and maintained by Michal Zalewski + + Forkserver design by Jann Horn + + Copyright 2013, 2014, 2015, 2016, 2017 Google Inc. 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 + */ + + char* shm_str; + afl_state_t * state = (afl_state_t *)instrumentation_state; + + if(state->trace_bits) // if trace_bits already points at the shm + return 0; // region, we've already run this function! + + // If we loaded a saved input bitmap, do not overwrite the + // map showing what was fuzzed showing everything as untouched + if(!state->input_bitmap) + memset(state->virgin_bits, 255, MAP_SIZE); + + memset(state->virgin_tmout, 255, MAP_SIZE); + memset(state->virgin_crash, 255, MAP_SIZE); + + // Allocate shared memory; shm_id must be module level or global so + // the atexit function has access to it (as we can not pass arguments + // to the callback function) + shm_id = shmget(IPC_PRIVATE, MAP_SIZE, IPC_CREAT | IPC_EXCL | 0600); + if(shm_id < 0) { + ERROR_MSG("shmget() failed"); + return 1; + } + atexit(remove_shm); + shm_str = alloc_printf("%d", shm_id); + + // set the environment variable so the instrumented binary knows which + // shared memory ID to attach to when it goes to write the bitmap + setenv(SHM_ENV_VAR, shm_str, 1); + ck_free(shm_str); + + // Attach to shared memory region + state->trace_bits = shmat(shm_id, NULL, 0); + if(!state->trace_bits) { + ERROR_MSG("shmat() failed"); + return 1; + } + + return 0; +} + +/* Get rid of shared memory (atexit handler). */ +static void remove_shm() { + shmctl(shm_id, IPC_RMID, NULL); +} + +/** + * Check if the current execution path brings anything new to the table. + * Update virgin bits to reflect the new paths found, so subsequent calls will + * always return 0. + * + * This function is called after every exec() on a fairly large buffer, so + * it needs to be fast. We do this in 32-bit and 64-bit flavors. + * + * @param virgin_map - The map we should compare against, which will be + * virgin_{bits,tmout,crash} in practice. + * @param trace_bits - The trace for this particular run + * @returns - 1 if the only change is the hit-count for a particular tuple; + * 2 if there are new tuples seen, 0 if it is not a new path + **/ +static inline uint8_t has_new_bits(uint8_t* virgin_map, uint8_t *trace_bits) { + /* + This function is based on the AFL has_new_bits function present in afl-fuzz.c, + available at this URL: + https://github.com/mirrorer/afl/blob/master/afl-fuzz.c#L1968. + AFL's license is as shown below: + + american fuzzy lop - fuzzer code + -------------------------------- + Written and maintained by Michal Zalewski + + Forkserver design by Jann Horn + + Copyright 2013, 2014, 2015, 2016, 2017 Google Inc. 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 + */ + +#ifdef __x86_64__ + uint64_t* current = (uint64_t*)trace_bits; + uint64_t* virgin = (uint64_t*)virgin_map; + uint32_t i = (MAP_SIZE >> 3); +#else + uint32_t* current = (uint32_t*)trace_bits; + uint32_t* virgin = (uint32_t*)virgin_map; + uint32_t i = (MAP_SIZE >> 2); +#endif /* ^__x86_64__ */ + + uint8_t ret = 0; + while (i--) { + /* Optimize for (*current & *virgin) == 0 - i.e., no bits in current bitmap + that have not been already cleared from the virgin map - since this will + almost always be the case. */ + if (unlikely(*current) && unlikely(*current & *virgin)) { + if (likely(ret < 2)) { + uint8_t* cur = (uint8_t*)current; + uint8_t* vir = (uint8_t*)virgin; + + /* Looks like we have not found any new bytes yet; see if any non-zero + bytes in current[] are pristine in virgin[]. */ +#ifdef __x86_64__ + if ((cur[0] && vir[0] == 0xff) || (cur[1] && vir[1] == 0xff) || + (cur[2] && vir[2] == 0xff) || (cur[3] && vir[3] == 0xff) || + (cur[4] && vir[4] == 0xff) || (cur[5] && vir[5] == 0xff) || + (cur[6] && vir[6] == 0xff) || (cur[7] && vir[7] == 0xff)) ret = 2; + else ret = 1; +#else + if ((cur[0] && vir[0] == 0xff) || (cur[1] && vir[1] == 0xff) || + (cur[2] && vir[2] == 0xff) || (cur[3] && vir[3] == 0xff)) ret = 2; + else ret = 1; +#endif /* ^__x86_64__ */ + } + *virgin &= ~*current; + } + current++; + virgin++; + } + return ret; +} + +#ifdef __x86_64__ +static void simplify_trace(uint64_t* mem) { + uint32_t i = MAP_SIZE >> 3; + while (i--) { + /* Optimize for sparse bitmaps. */ + if (unlikely(*mem)) { + uint8_t* mem8 = (uint8_t*)mem; + mem8[0] = simplify_lookup[mem8[0]]; + mem8[1] = simplify_lookup[mem8[1]]; + mem8[2] = simplify_lookup[mem8[2]]; + mem8[3] = simplify_lookup[mem8[3]]; + mem8[4] = simplify_lookup[mem8[4]]; + mem8[5] = simplify_lookup[mem8[5]]; + mem8[6] = simplify_lookup[mem8[6]]; + mem8[7] = simplify_lookup[mem8[7]]; + } else *mem = 0x0101010101010101ULL; + mem++; + } +} +#else +static void simplify_trace(uint32_t* mem) { + uint32_t i = MAP_SIZE >> 2; + while (i--) { + /* Optimize for sparse bitmaps. */ + if (unlikely(*mem)) { + uint8_t* mem8 = (uint8_t*)mem; + mem8[0] = simplify_lookup[mem8[0]]; + mem8[1] = simplify_lookup[mem8[1]]; + mem8[2] = simplify_lookup[mem8[2]]; + mem8[3] = simplify_lookup[mem8[3]]; + } else *mem = 0x01010101; + mem++; + } +} +#endif /* ^__x86_64__ */ diff --git a/instrumentation/afl_instrumentation.h b/instrumentation/afl_instrumentation.h new file mode 100644 index 0000000..69f75b7 --- /dev/null +++ b/instrumentation/afl_instrumentation.h @@ -0,0 +1,172 @@ +#include // fprintf +#include // for pid_t +#include // uint*_t +#include // for memset, strdup +#include // for waitpid + +#include "forkserver_internal.h" + +/**** BEGIN DEFINES TAKEN FROM AFL (APLv2 LICENSE) ****/ +// Use the standard 16-bit map size from AFL +#define MAP_SIZE_POW2 16 +#define MAP_SIZE (1 << MAP_SIZE_POW2) +/* Environment variable used to pass SHM ID to the called program. */ +#define SHM_ENV_VAR "__AFL_SHM_ID" + +#define ALLOC_OFF_HEAD 8 +#define ALLOC_OFF_TOTAL (ALLOC_OFF_HEAD + 1) + +/* Maximum allocator request size (keep well under INT_MAX): */ +#define MAX_ALLOC 0x40000000 + +/* Magic tokens used to mark used / freed chunks. */ +#define ALLOC_MAGIC_C1 0xFF00FF00 /* Used head (dword) */ +#define ALLOC_MAGIC_F 0xFE00FE00 /* Freed head (dword) */ +#define ALLOC_MAGIC_C2 0xF0 /* Used tail (byte) */ +/* Positions of guard tokens in relation to the user-visible pointer. */ +#define ALLOC_C1(_ptr) (((uint32_t*)(_ptr))[-2]) +#define ALLOC_S(_ptr) (((uint32_t*)(_ptr))[-1]) +#define ALLOC_C2(_ptr) (((unsigned char*)(_ptr))[ALLOC_S(_ptr)]) + +/* Just print stuff to the appropriate stream. */ +#ifdef MESSAGES_TO_STDOUT +# define SAYF(x...) printf(x) +#else +# define SAYF(x...) fprintf(stderr, x) +#endif /* ^MESSAGES_TO_STDOUT */ + +#define ABORT(x...) do { \ + SAYF("\n[-] PROGRAM ABORT : " x); \ + SAYF("\n Stop location : " "%s(), %s:%u\n\n", \ + __FUNCTION__, __FILE__, __LINE__); \ + abort(); \ + } while (0) + +#define ALLOC_CHECK_SIZE(_s) do { \ + if ((_s) > MAX_ALLOC) \ + ABORT("Bad alloc request: %u bytes", (_s)); \ + } while (0) + +#define ALLOC_CHECK_RESULT(_r, _s) do { \ + if (!(_r)) \ + ABORT("Out of memory: can't allocate %u bytes", (_s)); \ + } while (0) + +static inline void* DFL_ck_alloc_nozero(uint32_t size) { + void* ret; + if (!size) return NULL; + ALLOC_CHECK_SIZE(size); + ret = malloc(size + ALLOC_OFF_TOTAL); + ALLOC_CHECK_RESULT(ret, size); + ret += ALLOC_OFF_HEAD; + ALLOC_C1(ret) = ALLOC_MAGIC_C1; + ALLOC_S(ret) = size; + ALLOC_C2(ret) = ALLOC_MAGIC_C2; + return ret; +} +#define ck_alloc_nozero DFL_ck_alloc_nozero + +/* Allocate a buffer, returning zeroed memory. */ +static inline void* DFL_ck_alloc(uint32_t size) { + void* mem; + if (!size) return NULL; + mem = DFL_ck_alloc_nozero(size); + return memset(mem, 0, size); +} +#define ck_alloc DFL_ck_alloc + +#define alloc_printf(_str...) ({ \ + uint8_t* _tmp; \ + int32_t _len = snprintf(NULL, 0, _str); \ + if (_len < 0) FATAL_MSG("Whoa, snprintf() fails?!"); \ + _tmp = ck_alloc(_len + 1); \ + snprintf((char*)_tmp, _len + 1, _str); \ + _tmp; \ + }) + +#define CHECK_PTR(_p) do { \ + if (_p) { \ + if (ALLOC_C1(_p) ^ ALLOC_MAGIC_C1) {\ + if (ALLOC_C1(_p) == ALLOC_MAGIC_F) \ + ABORT("Use after free."); \ + else ABORT("Corrupted head alloc canary."); \ + } \ + if (ALLOC_C2(_p) ^ ALLOC_MAGIC_C2) \ + ABORT("Corrupted tail alloc canary."); \ + } \ + } while (0) + +static inline void DFL_ck_free(void* mem) { + if(!mem) return; + CHECK_PTR(mem); +#ifdef DEBUG_BUILD + /* Catch pointer issues sooner. */ + memset(mem, 0xFF, ALLOC_S(mem)); +#endif /* DEBUG_BUILD */ + ALLOC_C1(mem) = ALLOC_MAGIC_F; + free(mem - ALLOC_OFF_HEAD); +} + +#define ck_free DFL_ck_free + +#define likely(_x) __builtin_expect(!!(_x), 1) +#define unlikely(_x) __builtin_expect(!!(_x), 0) + +/* Destructively simplify trace by eliminating hit count information + and replacing it with 0x80 or 0x01 depending on whether the tuple + is hit or not. Called on every new crash or timeout, should be + reasonably fast. */ +static const uint8_t simplify_lookup[256] = { + [0] = 1, + [1 ... 255] = 128 +}; + +/**** END DEFINES TAKEN FROM AFL (APLv2 LICENSE) ****/ + + +struct afl_state { + char *target_path; + pid_t child_pid; + forkserver_t fs; + int process_finished; + int last_fuzz_result; + int fuzz_results_set; // have we set the fuzz results? + int last_status; // the last input did what? (CRASH, HANG, NONE, etc.) + int last_is_new_path; // did the last input hit a new code path? + int use_fork_server; + int fork_server_setup; + int persistence_max_cnt; + uint8_t *input_bitmap; + uint8_t virgin_bits[MAP_SIZE]; // Regions yet untouched by fuzzing + uint8_t virgin_tmout[MAP_SIZE]; // Bits we haven't seen in tmouts + uint8_t virgin_crash[MAP_SIZE]; // Bits we haven't seen in crashes + uint8_t *trace_bits; // SHM with instrumentation bitmap +}; +typedef struct afl_state afl_state_t; + +void * afl_create(char *options, char *state); +void afl_cleanup(void *instrumentation_state); +char * afl_get_state(void *instrumentation_state); +void afl_free_state(char *state); +int afl_set_state(void *instrumentation_state, char *state); +void * afl_merge(void *instrumentation_state, void *other_instrumentation_state); +int afl_enable(void *instrumentation_state, pid_t *process, char *cmd_line, + char *input, size_t input_length); +int afl_is_new_path(void *instrumentation_state); +int afl_get_fuzz_result(void *instrumentation_state); +int afl_is_process_done(void *instrumentation_state); +int afl_help(char **help_str); + +static afl_state_t * setup_options(char *options); +static void destroy_target_process(afl_state_t * state); +static int create_target_process(afl_state_t * state, char* cmd_line, + char * input, size_t input_length); +int setup_shm(void *instrumentation_state); +static void remove_shm(); +#ifdef __x86_64__ +static void simplify_trace(uint64_t* mem); +#else +static void simplify_trace(uint32_t* mem); +#endif /* ^__x86_64__ */ +static inline uint8_t has_new_bits(uint8_t* virgin_map, uint8_t *trace_bits); +static int finish_fuzz_round(afl_state_t *state); diff --git a/instrumentation/forkserver_internal.h b/instrumentation/forkserver_internal.h index 349988c..b27e3c7 100644 --- a/instrumentation/forkserver_internal.h +++ b/instrumentation/forkserver_internal.h @@ -29,6 +29,9 @@ struct forkserver { }; typedef struct forkserver forkserver_t; +pid_t run_target(int needs_stdin_fd, char *target_path, char **argv, + forkserver_t * fs, int use_forkserver_library, int *st_pipe, + int *ctl_pipe, int persistence_max_cnt); //These functions control all interactions with the forkserver, sending the //commands listed above void fork_server_init(forkserver_t * fs, char * target_path, char ** argv, int use_forkserver_library, diff --git a/instrumentation/instrumentation.c b/instrumentation/instrumentation.c index 665e145..4fa1a91 100644 --- a/instrumentation/instrumentation.c +++ b/instrumentation/instrumentation.c @@ -28,7 +28,7 @@ #define MSAN_ERROR 86 //The amount of time to wait before considering the fork server initialization failed -#define FORK_SERVER_STARTUP_TIME 5000 +#define FORK_SERVER_STARTUP_TIME 5 //Save a fd to the /dev/null, so we don't have to keep opening/closing it static int dev_null_fd = -1; @@ -61,51 +61,30 @@ static void find_fork_server_library(char * buffer, size_t buffer_len) ////////////////////////////////////////////////////////////// // Fork Server Initialization //////////////////////////////// ////////////////////////////////////////////////////////////// - /** - * This function starts a program with the fork server embedded in it - * @param fs - A forkserver_t structure to hold the fork server state + * + * @param needs_stdin_fd - whether we should open a library for the stdin of + * the newly created process * @param target_path - The path to the program to start * @param argv - Arguments to pass to the program - * @param use_forkserver_library - Whether or not to use LD_PRELOAD/DYLD_INSERT_LIBRARIES to inject the fork server - * library or not - * @param need_stdin_fd - whether we should open a library for the stdin of the newly created process + * @param fs - A forkserver_t structure to hold the fork server state, or NULL + * if not using a fork server + * @param use_forkserver_library - Whether or not to use + * LD_PRELOAD/DYLD_INSERT_LIBRARIES to inject + * the fork server + * @param st_pipe - pointer to an array of two status pipes for the fork server + * @param ctl_pipe - pointer to an array of two control pipes for the fork server + * @param persistence_max_cnt - if fork server is in use, and perssistent mode + * is in use, this is the number of inputs which + * will be handled by each execution of the target + * @return the process ID of spawned process */ -void fork_server_init(forkserver_t * fs, char * target_path, char ** argv, int use_forkserver_library, - int persistence_max_cnt, int needs_stdin_fd) -{ - static struct itimerval it; - int st_pipe[2], ctl_pipe[2]; - int err, status, forksrv_pid; - int rlen = -1, timed_out = 1; - char fork_server_library_path[MAX_PATH]; - char stdin_filename[100]; - char buffer[16]; - time_t start_time; - - if(dev_null_fd < 0) { - dev_null_fd = open("/dev/null", O_RDWR); - if (dev_null_fd < 0) - FATAL_MSG("Unable to open /dev/null"); - } - - fs->sent_get_status = 0; - fs->last_status = -1; - - if(needs_stdin_fd) { - strncpy(stdin_filename, "/tmp/fuzzfileXXXXXX", sizeof(stdin_filename)); - fs->target_stdin = mkstemp(stdin_filename); - if(fs->target_stdin < 0) - FATAL_MSG("Couldn't make temp file\n"); - } - else - fs->target_stdin = -1; - - DEBUG_MSG("Spinning up the fork server..."); - +pid_t run_target(int needs_stdin_fd, char *target_path, char **argv, + forkserver_t * fs, int use_forkserver_library, int *st_pipe, + int *ctl_pipe, int persistence_max_cnt) { /* - The code in the rest of this function is based on the AFL startup fork server - present in afl-fuzz.c, available at this URL: + This function is based on the AFL run_target function present in afl-fuzz.c, + available at this URL: https://github.com/mirrorer/afl/blob/master/afl-fuzz.c#L1968. AFL's license is as shown below: @@ -123,48 +102,32 @@ void fork_server_init(forkserver_t * fs, char * target_path, char ** argv, int u http://www.apache.org/licenses/LICENSE-2.0 */ + int child_pid; + DEBUG_MSG("Forking child process for target executable..."); + child_pid = fork(); + if(child_pid < 0) FATAL_MSG("fork() failed"); - - if(pipe(st_pipe) || pipe(ctl_pipe)) - FATAL_MSG("pipe() failed"); - - forksrv_pid = fork(); - if(forksrv_pid < 0) - FATAL_MSG("fork() failed"); - - //In the child process - if (!forksrv_pid) { - + if(!child_pid) { 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 < MAX_FORKSRV_FD) { - r.rlim_cur = MAX_FORKSRV_FD; 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 @@ -174,68 +137,164 @@ void fork_server_init(forkserver_t * fs, char * target_path, char ** argv, int u setrlimit(RLIMIT_CORE, &r); // Ignore errors - // Isolate the process and configure standard descriptors. + /* Isolate the process and configure standard descriptors. If out_file is + specified, stdin is /dev/null; otherwise, out_fd is cloned instead. */ setsid(); + if(dev_null_fd < 0) + dev_null_fd = open("/dev/null", O_RDWR); if(needs_stdin_fd) { dup2(fs->target_stdin, 0); close(fs->target_stdin); - } - else + } else { dup2(dev_null_fd, 0); - dup2(dev_null_fd, 1); - dup2(dev_null_fd, 2); - - // Set up control and status pipes, close the unneeded original fds. - if (dup2(ctl_pipe[0], FUZZER_TO_FORKSRV) < 0) - FATAL_MSG("dup2() failed"); - if (dup2(st_pipe[1], FORKSRV_TO_FUZZER) < 0) - FATAL_MSG("dup2() failed"); - - close(ctl_pipe[0]); - close(ctl_pipe[1]); - close(st_pipe[0]); - close(st_pipe[1]); + } + if(dup2(dev_null_fd, 1) < 0) + WARNING_MSG("Sending stdout to /dev/null failed! errno=%d", errno); + if(dup2(dev_null_fd, 2) < 0) + WARNING_MSG("Sending stderr to /dev/null failed! errno=%d", errno); + + // The forkserver requires setting up some control pipes for interaction + // between the fuzzer and forkserver (which lives in the target process) + if(fs) { + // Set up control and status pipes, close the unneeded original fds. + if(dup2(ctl_pipe[0], FUZZER_TO_FORKSRV) < 0) + FATAL_MSG("dup2() failed"); + if(dup2(st_pipe[1], FORKSRV_TO_FUZZER) < 0) + FATAL_MSG("dup2() failed"); + + close(ctl_pipe[0]); + close(ctl_pipe[1]); + close(st_pipe[0]); + close(st_pipe[1]); + } + /* On Linux, would be faster to use O_CLOEXEC. Maybe TODO. */ close(dev_null_fd); - // Preload the forkserver library - if(use_forkserver_library) { - find_fork_server_library(fork_server_library_path, sizeof(fork_server_library_path)); -#ifdef __APPLE__ - setenv("DYLD_INSERT_LIBRARIES", fork_server_library_path, 1); -#else - setenv("LD_PRELOAD", fork_server_library_path, 1); -#endif - } - - if(persistence_max_cnt) { - snprintf(buffer, sizeof(buffer),"%d",persistence_max_cnt); - setenv(PERSIST_MAX_VAR, buffer, 1); + // If we are using a forksrv, we might need to inject it dynamically if it + // is not already in the executable. We also want to make sure we set the + // environment variable which is used for persistence mode, and finally we + // add the optimization to load all the libraries once so this is only + // done on the execv, as opposed to each time the target process calls fork + if(fs) { + // Preload the forkserver library + if(use_forkserver_library) { + char fork_server_library_path[MAX_PATH]; + find_fork_server_library(fork_server_library_path, sizeof(fork_server_library_path)); + #ifdef __APPLE__ + setenv("DYLD_INSERT_LIBRARIES", fork_server_library_path, 1); + #else + setenv("LD_PRELOAD", fork_server_library_path, 1); + #endif + } + + if(persistence_max_cnt) { + char buffer[16]; + snprintf(buffer, sizeof(buffer),"%d",persistence_max_cnt); + setenv(PERSIST_MAX_VAR, buffer, 1); + } + + // 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); } - // 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); + // MSAN uses slightly different arguments when using the forkserver + if(fs) { + // 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); + } else { + setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":" + "symbolize=0:" + "msan_track_origins=0", 0); + } execv(target_path, argv); + // The only time execv() returns is if it failed + FATAL_MSG("Target executable failed to execute (execv())"); exit(1); } + return child_pid; +} + +/** + * This function starts a program with the fork server embedded in it + * @param fs - A forkserver_t structure to hold the fork server state + * @param target_path - The path to the program to start + * @param argv - Arguments to pass to the program + * @param use_forkserver_library - Whether or not to use LD_PRELOAD/DYLD_INSERT_LIBRARIES to inject the fork server + * library or not + * @param needs_stdin_fd - whether we should open a library for the stdin of the newly created process + */ +void fork_server_init(forkserver_t * fs, char * target_path, char ** argv, int use_forkserver_library, + int persistence_max_cnt, int needs_stdin_fd) +{ + static struct itimerval it; + int st_pipe[2], ctl_pipe[2]; + int err, status, forksrv_pid; + int rlen = -1, timed_out = 1; + char stdin_filename[100]; + time_t start_time; + + if(dev_null_fd < 0) { + dev_null_fd = open("/dev/null", O_RDWR); + if (dev_null_fd < 0) + FATAL_MSG("Unable to open /dev/null"); + } + + fs->sent_get_status = 0; + fs->last_status = -1; + + if(needs_stdin_fd) { + strncpy(stdin_filename, "/tmp/fuzzfileXXXXXX", sizeof(stdin_filename)); + fs->target_stdin = mkstemp(stdin_filename); + if(fs->target_stdin < 0) + FATAL_MSG("Couldn't make temp file\n"); + } + else + fs->target_stdin = -1; + + DEBUG_MSG("Spinning up the fork server..."); + +/* + The code in the rest of this function is based on the AFL startup fork server + present in afl-fuzz.c, available at this URL: + https://github.com/mirrorer/afl/blob/master/afl-fuzz.c#L1968. + AFL's license is as shown below: + + american fuzzy lop - fuzzer code + -------------------------------- + Written and maintained by Michal Zalewski + + Forkserver design by Jann Horn + + Copyright 2013, 2014, 2015, 2016, 2017 Google Inc. 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 + */ + if(pipe(st_pipe) || pipe(ctl_pipe)) + FATAL_MSG("pipe() failed"); + + forksrv_pid = run_target(needs_stdin_fd, target_path, argv, fs, use_forkserver_library, + st_pipe, ctl_pipe, persistence_max_cnt); + // Close the unneeded endpoints. close(ctl_pipe[0]); close(st_pipe[1]); diff --git a/instrumentation/instrumentation.h b/instrumentation/instrumentation.h index c4b5410..d558503 100644 --- a/instrumentation/instrumentation.h +++ b/instrumentation/instrumentation.h @@ -61,4 +61,3 @@ struct instrumentation int(*is_process_done)(void * instrumentation_state); }; typedef struct instrumentation instrumentation_t; - diff --git a/instrumentation/instrumentation_factory.c b/instrumentation/instrumentation_factory.c index 5f17f09..4450dab 100644 --- a/instrumentation/instrumentation_factory.c +++ b/instrumentation/instrumentation_factory.c @@ -4,6 +4,7 @@ #include "dynamorio_instrumentation.h" #else #include "return_code_instrumentation.h" +#include "afl_instrumentation.h" #if !__APPLE__ // Linux #include "linux_ipt_instrumentation.h" #endif @@ -68,6 +69,19 @@ instrumentation_t * instrumentation_factory(char * instrumentation_type) ret->get_fuzz_result = return_code_get_fuzz_result; ret->is_process_done = return_code_is_process_done; } + else if (!strcmp(instrumentation_type, "afl")) + { + ret->create = afl_create; + ret->cleanup = afl_cleanup; + ret->merge = afl_merge; + ret->get_state = afl_get_state; + ret->free_state = afl_free_state; + ret->set_state = afl_set_state; + ret->enable = afl_enable; + ret->is_new_path = afl_is_new_path; + ret->get_fuzz_result = afl_get_fuzz_result; + ret->is_process_done = afl_is_process_done; + } #if !__APPLE__ // Linux else if (!strcmp(instrumentation_type, "ipt")) { @@ -110,6 +124,7 @@ char * instrumentation_help(void) APPEND_HELP(text, new_text, dynamorio_help); #else APPEND_HELP(text, new_text, return_code_help); + APPEND_HELP(text, new_text, afl_help); #if !__APPLE__ // Linux APPEND_HELP(text, new_text, linux_ipt_help); #endif diff --git a/instrumentation/linux_ipt_instrumentation.c b/instrumentation/linux_ipt_instrumentation.c index 88d2df0..c783949 100644 --- a/instrumentation/linux_ipt_instrumentation.c +++ b/instrumentation/linux_ipt_instrumentation.c @@ -1058,7 +1058,7 @@ int linux_ipt_set_state(void * instrumentation_state, char * state) * @param instrumentation_state - an instrumentation specific state object previously created by the linux_ipt_create function * @process - a pointer to return a handle to the process that the instrumentation was enabled on * @cmd_line - the command line of the fuzzed process to enable instrumentation on - * @input - a buffer to the input that should be sent to the fuzzed process on stdin + * @input - a buffer to the input that should be sent to the fuzzed process * @input_length - the length of the input parameter * returns 0 on success, -1 on failure */ diff --git a/server/add_target.py b/server/add_target.py index bae852c..801fc5a 100755 --- a/server/add_target.py +++ b/server/add_target.py @@ -1,4 +1,20 @@ #!/usr/bin/env python2 +# +# This script will add a target to the BOINC system, which would normally +# require a bunch of manual work in terms of editing files, and creating +# a whole directory structure. The gist of it is this: +# - Edit project.xml if the platform is new +# - Edit config.xml to add daemons such as validators and assimulators +# - Create the follow directory structure: +# apps/$TARGET_NAME/$VERSION/$PLATFORM +# (where $VERSION is currently hard-coded to "1") +# - The contents of skel/$PLATFORM are then copied to the new app directory +# with the filenames getting the $TARGET_NAME injected into them and +# version.xml has all instances of {app} replaced with $TARGET_NAME +# - Creates templates/$TARGET_NAME_$PLATFORM_{in,out} based on files in +# skel/templates. See: https://boinc.berkeley.edu/trac/wiki/JobTemplates +# - Calls xadd, stop, and start to ensure BOINC knows about all these changes +# import argparse import fcntl