diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/README.md | 33 | ||||
-rw-r--r-- | src/afl-analyze.c | 79 | ||||
-rw-r--r-- | src/afl-as.c | 20 | ||||
-rw-r--r-- | src/afl-cc.c | 2062 | ||||
-rw-r--r-- | src/afl-common.c | 297 | ||||
-rw-r--r-- | src/afl-forkserver.c | 445 | ||||
-rw-r--r-- | src/afl-fuzz-bitmap.c | 530 | ||||
-rw-r--r-- | src/afl-fuzz-cmplog.c | 6 | ||||
-rw-r--r-- | src/afl-fuzz-extras.c | 363 | ||||
-rw-r--r-- | src/afl-fuzz-init.c | 705 | ||||
-rw-r--r-- | src/afl-fuzz-mutators.c | 146 | ||||
-rw-r--r-- | src/afl-fuzz-one.c | 1303 | ||||
-rw-r--r-- | src/afl-fuzz-python.c | 191 | ||||
-rw-r--r-- | src/afl-fuzz-queue.c | 729 | ||||
-rw-r--r-- | src/afl-fuzz-redqueen.c | 2394 | ||||
-rw-r--r-- | src/afl-fuzz-run.c | 204 | ||||
-rw-r--r-- | src/afl-fuzz-state.c | 188 | ||||
-rw-r--r-- | src/afl-fuzz-stats.c | 341 | ||||
-rw-r--r-- | src/afl-fuzz-statsd.c | 273 | ||||
-rw-r--r-- | src/afl-fuzz.c | 1022 | ||||
-rw-r--r-- | src/afl-gcc.c | 488 | ||||
-rw-r--r-- | src/afl-gotcpu.c | 4 | ||||
-rw-r--r-- | src/afl-ld-lto.c | 368 | ||||
-rw-r--r-- | src/afl-performance.c | 125 | ||||
-rw-r--r-- | src/afl-sharedmem.c | 44 | ||||
-rw-r--r-- | src/afl-showmap.c | 411 | ||||
-rw-r--r-- | src/afl-tmin.c | 189 |
27 files changed, 10244 insertions, 2716 deletions
diff --git a/src/README.md b/src/README.md index 6da534c3..35af6ab9 100644 --- a/src/README.md +++ b/src/README.md @@ -2,23 +2,28 @@ Quick explanation about the files here: -- `afl-analyze.c` - afl-analyze binary tool +- `afl-analyze.c` - afl-analyze binary tool - `afl-as.c` - afl-as binary tool -- `afl-gotcpu.c` - afl-gotcpu binary tool -- `afl-showmap.c` - afl-showmap binary tool -- `afl-tmin.c` - afl-tmin binary tool -- `afl-fuzz.c` - afl-fuzz binary tool (just main() and usage()) +- `afl-cc.c` - afl-cc binary tool +- `afl-common.c` - common functions, used by afl-analyze, afl-fuzz, afl-showmap and afl-tmin +- `afl-forkserver.c` - forkserver implementation, used by afl-fuzz afl-showmap, afl-tmin - `afl-fuzz-bitmap.c` - afl-fuzz bitmap handling +- `afl-fuzz.c` - afl-fuzz binary tool (just main() and usage()) +- `afl-fuzz-cmplog.c` - afl-fuzz cmplog functions - `afl-fuzz-extras.c` - afl-fuzz the *extra* function calls -- `afl-fuzz-state.c` - afl-fuzz state and globals -- `afl-fuzz-init.c` - afl-fuzz initialization -- `afl-fuzz-misc.c` - afl-fuzz misc functions -- `afl-fuzz-one.c` - afl-fuzz fuzzer_one big loop, this is where the mutation is happening +- `afl-fuzz-init.c` - afl-fuzz initialization +- `afl-fuzz-misc.c` - afl-fuzz misc functions +- `afl-fuzz-mutators.c` - afl-fuzz custom mutator and python support +- `afl-fuzz-one.c` - afl-fuzz fuzzer_one big loop, this is where the mutation is happening +- `afl-fuzz-performance.c` - hash64 and rand functions - `afl-fuzz-python.c` - afl-fuzz the python mutator extension - `afl-fuzz-queue.c` - afl-fuzz handling the queue -- `afl-fuzz-run.c` - afl-fuzz running the target +- `afl-fuzz-redqueen.c` - afl-fuzz redqueen implemention +- `afl-fuzz-run.c` - afl-fuzz running the target +- `afl-fuzz-state.c` - afl-fuzz state and globals - `afl-fuzz-stats.c` - afl-fuzz writing the statistics file -- `afl-gcc.c` - afl-gcc binary tool (deprecated) -- `afl-common.c` - common functions, used by afl-analyze, afl-fuzz, afl-showmap and afl-tmin -- `afl-forkserver.c` - forkserver implementation, used by afl-fuzz and afl-tmin -afl-sharedmem.c - sharedmem implementation, used by afl-fuzz and afl-tmin +- `afl-gotcpu.c` - afl-gotcpu binary tool +- `afl-ld-lto.c` - LTO linker helper +- `afl-sharedmem.c` - sharedmem implementation, used by afl-fuzz, afl-showmap, afl-tmin +- `afl-showmap.c` - afl-showmap binary tool +- `afl-tmin.c` - afl-tmin binary tool diff --git a/src/afl-analyze.c b/src/afl-analyze.c index e6dd0fca..86b0f7e9 100644 --- a/src/afl-analyze.c +++ b/src/afl-analyze.c @@ -26,9 +26,6 @@ #define AFL_MAIN -#ifdef __ANDROID__ - #include "android-ashmem.h" -#endif #include "config.h" #include "types.h" #include "debug.h" @@ -78,9 +75,9 @@ static u64 mem_limit = MEM_LIMIT; /* Memory limit (MB) */ static s32 dev_null_fd = -1; /* FD to /dev/null */ -static u8 edges_only, /* Ignore hit counts? */ +static bool edges_only, /* Ignore hit counts? */ use_hex_offsets, /* Show hex offsets? */ - use_stdin = 1; /* Use stdin for program input? */ + use_stdin = true; /* Use stdin for program input? */ static volatile u8 stop_soon, /* Ctrl-C pressed? */ child_timed_out; /* Child timed out? */ @@ -103,20 +100,31 @@ static u32 map_size = MAP_SIZE; /* Classify tuple counts. This is a slow & naive version, but good enough here. */ +#define TIMES4(x) x, x, x, x +#define TIMES8(x) TIMES4(x), TIMES4(x) +#define TIMES16(x) TIMES8(x), TIMES8(x) +#define TIMES32(x) TIMES16(x), TIMES16(x) +#define TIMES64(x) TIMES32(x), TIMES32(x) static u8 count_class_lookup[256] = { [0] = 0, [1] = 1, [2] = 2, [3] = 4, - [4 ... 7] = 8, - [8 ... 15] = 16, - [16 ... 31] = 32, - [32 ... 127] = 64, - [128 ... 255] = 128 + [4] = TIMES4(8), + [8] = TIMES8(16), + [16] = TIMES16(32), + [32] = TIMES32(64), + [128] = TIMES64(128) }; +#undef TIMES64 +#undef TIMES32 +#undef TIMES16 +#undef TIMES8 +#undef TIMES4 + static void classify_counts(u8 *mem) { u32 i = map_size; @@ -384,7 +392,7 @@ static void show_legend(void) { /* Interpret and report a pattern in the input file. */ -static void dump_hex(u8 *buf, u32 len, u8 *b_data) { +static void dump_hex(u32 len, u8 *b_data) { u32 i; @@ -678,7 +686,7 @@ static void analyze(char **argv) { } - dump_hex(in_data, in_len, b_data); + dump_hex(in_len, b_data); SAYF("\n"); @@ -700,6 +708,7 @@ static void analyze(char **argv) { static void handle_stop_sig(int sig) { + (void)sig; stop_soon = 1; if (child_pid > 0) { kill(child_pid, SIGKILL); } @@ -742,12 +751,15 @@ static void set_up_environment(void) { } - if (!strstr(x, "symbolize=0")) { +#ifndef ASAN_BUILD + if (!getenv("AFL_DEBUG") && !strstr(x, "symbolize=0")) { FATAL("Custom ASAN_OPTIONS set without symbolize=0 - please fix!"); } +#endif + } x = get_afl_env("MSAN_OPTIONS"); @@ -773,6 +785,7 @@ static void set_up_environment(void) { "abort_on_error=1:" "detect_leaks=0:" "allocator_may_return_null=1:" + "detect_odr_violation=0:" "symbolize=0:" "handle_segv=0:" "handle_sigbus=0:" @@ -809,38 +822,7 @@ static void set_up_environment(void) { if (qemu_mode) { - u8 *qemu_preload = getenv("QEMU_SET_ENV"); - u8 *afl_preload = getenv("AFL_PRELOAD"); - u8 *buf; - - s32 i, afl_preload_size = strlen(afl_preload); - for (i = 0; i < afl_preload_size; ++i) { - - if (afl_preload[i] == ',') { - - PFATAL( - "Comma (',') is not allowed in AFL_PRELOAD when -Q is " - "specified!"); - - } - - } - - if (qemu_preload) { - - buf = alloc_printf("%s,LD_PRELOAD=%s,DYLD_INSERT_LIBRARIES=%s", - qemu_preload, afl_preload, afl_preload); - - } else { - - buf = alloc_printf("LD_PRELOAD=%s,DYLD_INSERT_LIBRARIES=%s", - afl_preload, afl_preload); - - } - - setenv("QEMU_SET_ENV", buf, 1); - - ck_free(buf); + /* afl-qemu-trace takes care of converting AFL_PRELOAD. */ } else { @@ -888,8 +870,8 @@ static void usage(u8 *argv0) { "Execution control settings:\n" " -f file - input file read by the tested program (stdin)\n" - " -t msec - timeout for each run (%d ms)\n" - " -m megs - memory limit for child process (%d MB)\n" + " -t msec - timeout for each run (%u ms)\n" + " -m megs - memory limit for child process (%u MB)\n" " -Q - use binary-only instrumentation (QEMU mode)\n" " -U - use unicorn-based instrumentation (Unicorn mode)\n" " -W - use qemu-based instrumentation with Wine (Wine " @@ -921,11 +903,12 @@ static void usage(u8 *argv0) { /* Main entry point */ -int main(int argc, char **argv, char **envp) { +int main(int argc, char **argv_orig, char **envp) { s32 opt; u8 mem_limit_given = 0, timeout_given = 0, unicorn_mode = 0, use_wine = 0; char **use_argv; + char **argv = argv_cpy_dup(argc, argv_orig); doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH; diff --git a/src/afl-as.c b/src/afl-as.c index f16d6060..7de267a3 100644 --- a/src/afl-as.c +++ b/src/afl-as.c @@ -27,7 +27,7 @@ utility has right now is to be able to skip them gracefully and allow the compilation process to continue. - That said, see examples/clang_asm_normalize/ for a solution that may + That said, see utils/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. @@ -47,6 +47,7 @@ #include <stdlib.h> #include <string.h> #include <time.h> +#include <limits.h> #include <ctype.h> #include <fcntl.h> @@ -131,12 +132,17 @@ static void edit_params(int argc, char **argv) { if (!tmp_dir) { tmp_dir = "/tmp"; } as_params = ck_alloc((argc + 32) * sizeof(u8 *)); + if (unlikely((INT_MAX - 32) < argc || !as_params)) { + + FATAL("Too many parameters passed to as"); + + } as_params[0] = afl_as ? afl_as : (u8 *)"as"; as_params[argc] = 0; - for (i = 1; i < argc - 1; i++) { + for (i = 1; (s32)i < argc - 1; i++) { if (!strcmp(argv[i], "--64")) { @@ -152,7 +158,7 @@ static void edit_params(int argc, char **argv) { /* The Apple case is a bit different... */ - if (!strcmp(argv[i], "-arch") && i + 1 < argc) { + if (!strcmp(argv[i], "-arch") && i + 1 < (u32)argc) { if (!strcmp(argv[i + 1], "x86_64")) use_64bit = 1; @@ -407,7 +413,7 @@ static void add_instrumentation(void) { if (line[0] == '\t') { - if (line[1] == 'j' && line[2] != 'm' && R(100) < inst_ratio) { + if (line[1] == 'j' && line[2] != 'm' && R(100) < (long)inst_ratio) { fprintf(outf, use_64bit ? trampoline_fmt_64 : trampoline_fmt_32, R(MAP_SIZE)); @@ -449,7 +455,7 @@ static void add_instrumentation(void) { /* Apple: L<num> / LBB<num> */ if ((isdigit(line[1]) || (clang_mode && !strncmp(line, "LBB", 3))) && - R(100) < inst_ratio) { + R(100) < (long)inst_ratio) { #else @@ -457,7 +463,7 @@ static void add_instrumentation(void) { if ((isdigit(line[2]) || (clang_mode && !strncmp(line + 1, "LBB", 3))) && - R(100) < inst_ratio) { + R(100) < (long)inst_ratio) { #endif /* __APPLE__ */ @@ -591,7 +597,7 @@ int main(int argc, char **argv) { rand_seed = tv.tv_sec ^ tv.tv_usec ^ getpid(); // in fast systems where pids can repeat in the same seconds we need this - for (i = 1; i < argc; i++) + for (i = 1; (s32)i < argc; i++) for (j = 0; j < strlen(argv[i]); j++) rand_seed += argv[i][j]; diff --git a/src/afl-cc.c b/src/afl-cc.c new file mode 100644 index 00000000..e13f285d --- /dev/null +++ b/src/afl-cc.c @@ -0,0 +1,2062 @@ +/* + american fuzzy lop++ - compiler instrumentation wrapper + ------------------------------------------------------- + + Written by Michal Zalewski, Laszlo Szekeres and Marc Heuse + + Copyright 2015, 2016 Google Inc. All rights reserved. + Copyright 2019-2020 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + */ + +#define AFL_MAIN + +#include "common.h" +#include "config.h" +#include "types.h" +#include "debug.h" +#include "alloc-inl.h" +#include "llvm-alternative-coverage.h" + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <limits.h> +#include <assert.h> + +#if (LLVM_MAJOR - 0 == 0) + #undef LLVM_MAJOR +#endif +#if !defined(LLVM_MAJOR) + #define LLVM_MAJOR 0 +#endif +#if (LLVM_MINOR - 0 == 0) + #undef LLVM_MINOR +#endif +#if !defined(LLVM_MINOR) + #define LLVM_MINOR 0 +#endif + +static u8 * obj_path; /* Path to runtime libraries */ +static u8 **cc_params; /* Parameters passed to the real CC */ +static u32 cc_par_cnt = 1; /* Param count, including argv0 */ +static u8 clang_mode; /* Invoked as afl-clang*? */ +static u8 llvm_fullpath[PATH_MAX]; +static u8 instrument_mode, instrument_opt_mode, ngram_size, ctx_k, lto_mode; +static u8 compiler_mode, plusplus_mode, have_instr_env = 0; +static u8 have_gcc, have_llvm, have_gcc_plugin, have_lto, have_instr_list = 0; +static u8 * lto_flag = AFL_CLANG_FLTO, *argvnull; +static u8 debug; +static u8 cwd[4096]; +static u8 cmplog_mode; +u8 use_stdin; /* dummy */ +// static u8 *march_opt = CFLAGS_OPT; + +enum { + + INSTRUMENT_DEFAULT = 0, + INSTRUMENT_CLASSIC = 1, + INSTRUMENT_AFL = 1, + INSTRUMENT_PCGUARD = 2, + INSTRUMENT_INSTRIM = 3, + INSTRUMENT_CFG = 3, + INSTRUMENT_LTO = 4, + INSTRUMENT_LLVMNATIVE = 5, + INSTRUMENT_GCC = 6, + INSTRUMENT_CLANG = 7, + INSTRUMENT_OPT_CTX = 8, + INSTRUMENT_OPT_NGRAM = 16, + INSTRUMENT_OPT_CALLER = 32, + INSTRUMENT_OPT_CTX_K = 64, + +}; + +char instrument_mode_string[18][18] = { + + "DEFAULT", + "CLASSIC", + "PCGUARD", + "CFG", + "LTO", + "PCGUARD-NATIVE", + "GCC", + "CLANG", + "CTX", + "CALLER", + "", + "", + "", + "", + "", + "", + "NGRAM", + "" + +}; + +enum { + + UNSET = 0, + LTO = 1, + LLVM = 2, + GCC_PLUGIN = 3, + GCC = 4, + CLANG = 5 + +}; + +char compiler_mode_string[7][12] = { + + "AUTOSELECT", "LLVM-LTO", "LLVM", "GCC_PLUGIN", + "GCC", "CLANG", "" + +}; + +u8 *getthecwd() { + + if (getcwd(cwd, sizeof(cwd)) == NULL) { + + static u8 fail[] = ""; + return fail; + + } + + return cwd; + +} + +/* Try to find a specific runtime we need, returns NULL on fail. */ + +/* + in find_object() we look here: + + 1. if obj_path is already set we look there first + 2. then we check the $AFL_PATH environment variable location if set + 3. next we check argv[0] if it has path information and use it + a) we also check ../lib/afl + 4. if 3. failed we check /proc (only Linux, Android, NetBSD, DragonFly, and + FreeBSD with procfs) + a) and check here in ../lib/afl too + 5. we look into the AFL_PATH define (usually /usr/local/lib/afl) + 6. we finally try the current directory + + if all these attempts fail - we return NULL and the caller has to decide + what to do. +*/ + +static u8 *find_object(u8 *obj, u8 *argv0) { + + u8 *afl_path = getenv("AFL_PATH"); + u8 *slash = NULL, *tmp; + + if (afl_path) { + + tmp = alloc_printf("%s/%s", afl_path, obj); + + if (debug) DEBUGF("Trying %s\n", tmp); + + if (!access(tmp, R_OK)) { + + obj_path = afl_path; + return tmp; + + } + + ck_free(tmp); + + } + + if (argv0) { + + slash = strrchr(argv0, '/'); + + if (slash) { + + u8 *dir = ck_strdup(argv0); + + slash = strrchr(dir, '/'); + *slash = 0; + + tmp = alloc_printf("%s/%s", dir, obj); + + if (debug) DEBUGF("Trying %s\n", tmp); + + if (!access(tmp, R_OK)) { + + obj_path = dir; + return tmp; + + } + + ck_free(tmp); + tmp = alloc_printf("%s/../lib/afl/%s", dir, obj); + + if (debug) DEBUGF("Trying %s\n", tmp); + + if (!access(tmp, R_OK)) { + + u8 *dir2 = alloc_printf("%s/../lib/afl", dir); + obj_path = dir2; + ck_free(dir); + return tmp; + + } + + ck_free(tmp); + ck_free(dir); + + } + +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__linux__) || \ + defined(__ANDROID__) || defined(__NetBSD__) + #define HAS_PROC_FS 1 +#endif +#ifdef HAS_PROC_FS + else { + + char *procname = NULL; + #if defined(__FreeBSD__) || defined(__DragonFly__) + procname = "/proc/curproc/file"; + #elif defined(__linux__) || defined(__ANDROID__) + procname = "/proc/self/exe"; + #elif defined(__NetBSD__) + procname = "/proc/curproc/exe"; + #endif + if (procname) { + + char exepath[PATH_MAX]; + ssize_t exepath_len = readlink(procname, exepath, sizeof(exepath)); + if (exepath_len > 0 && exepath_len < PATH_MAX) { + + exepath[exepath_len] = 0; + slash = strrchr(exepath, '/'); + + if (slash) { + + *slash = 0; + tmp = alloc_printf("%s/%s", exepath, obj); + + if (!access(tmp, R_OK)) { + + u8 *dir = alloc_printf("%s", exepath); + obj_path = dir; + return tmp; + + } + + ck_free(tmp); + tmp = alloc_printf("%s/../lib/afl/%s", exepath, obj); + + if (debug) DEBUGF("Trying %s\n", tmp); + + if (!access(tmp, R_OK)) { + + u8 *dir = alloc_printf("%s/../lib/afl/", exepath); + obj_path = dir; + return tmp; + + } + + } + + } + + } + + } + +#endif +#undef HAS_PROC_FS + + } + + tmp = alloc_printf("%s/%s", AFL_PATH, obj); + + if (debug) DEBUGF("Trying %s\n", tmp); + + if (!access(tmp, R_OK)) { + + obj_path = AFL_PATH; + return tmp; + + } + + ck_free(tmp); + + tmp = alloc_printf("./%s", obj); + + if (debug) DEBUGF("Trying %s\n", tmp); + + if (!access(tmp, R_OK)) { + + obj_path = "."; + return tmp; + + } + + ck_free(tmp); + + if (debug) DEBUGF("Trying ... giving up\n"); + + return NULL; + +} + +/* Copy argv to cc_params, making the necessary edits. */ + +static void edit_params(u32 argc, char **argv, char **envp) { + + u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0, shared_linking = 0, + preprocessor_only = 0, have_unroll = 0, have_o = 0, have_pic = 0, + have_c = 0; + + cc_params = ck_alloc((argc + 128) * sizeof(u8 *)); + + if (lto_mode) { + + if (lto_flag[0] != '-') + FATAL( + "Using afl-clang-lto is not possible because Makefile magic did not " + "identify the correct -flto flag"); + else + compiler_mode = LTO; + + } + + if (plusplus_mode) { + + u8 *alt_cxx = getenv("AFL_CXX"); + + if (!alt_cxx) { + + if (compiler_mode >= GCC_PLUGIN) { + + if (compiler_mode == GCC) { + + alt_cxx = clang_mode ? "clang++" : "g++"; + + } else if (compiler_mode == CLANG) { + + alt_cxx = "clang++"; + + } else { + + alt_cxx = "g++"; + + } + + } else { + + if (USE_BINDIR) + snprintf(llvm_fullpath, sizeof(llvm_fullpath), "%s/clang++", + LLVM_BINDIR); + else + snprintf(llvm_fullpath, sizeof(llvm_fullpath), CLANGPP_BIN); + alt_cxx = llvm_fullpath; + + } + + } + + cc_params[0] = alt_cxx; + + } else { + + u8 *alt_cc = getenv("AFL_CC"); + + if (!alt_cc) { + + if (compiler_mode >= GCC_PLUGIN) { + + if (compiler_mode == GCC) { + + alt_cc = clang_mode ? "clang" : "gcc"; + + } else if (compiler_mode == CLANG) { + + alt_cc = "clang"; + + } else { + + alt_cc = "gcc"; + + } + + } else { + + if (USE_BINDIR) + snprintf(llvm_fullpath, sizeof(llvm_fullpath), "%s/clang", + LLVM_BINDIR); + else + snprintf(llvm_fullpath, sizeof(llvm_fullpath), CLANGPP_BIN); + alt_cc = llvm_fullpath; + + } + + } + + cc_params[0] = alt_cc; + + } + + if (compiler_mode == GCC || compiler_mode == CLANG) { + + cc_params[cc_par_cnt++] = "-B"; + cc_params[cc_par_cnt++] = obj_path; + + if (clang_mode || compiler_mode == CLANG) { + + cc_params[cc_par_cnt++] = "-no-integrated-as"; + + } + + } + + if (compiler_mode == GCC_PLUGIN) { + + char *fplugin_arg = alloc_printf("-fplugin=%s/afl-gcc-pass.so", obj_path); + cc_params[cc_par_cnt++] = fplugin_arg; + + } + + if (compiler_mode == LLVM || compiler_mode == LTO) { + + cc_params[cc_par_cnt++] = "-Wno-unused-command-line-argument"; + + if (lto_mode && plusplus_mode) + cc_params[cc_par_cnt++] = "-lc++"; // needed by fuzzbench, early + + if (lto_mode && have_instr_env) { + + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/afl-llvm-lto-instrumentlist.so", obj_path); + + } + + if (getenv("AFL_LLVM_DICT2FILE")) { + + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/afl-llvm-dict2file.so", obj_path); + + } + + // laf + if (getenv("LAF_SPLIT_SWITCHES") || getenv("AFL_LLVM_LAF_SPLIT_SWITCHES")) { + + if (lto_mode && !have_c) { + + cc_params[cc_par_cnt++] = alloc_printf( + "-Wl,-mllvm=-load=%s/split-switches-pass.so", obj_path); + + } else { + + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/split-switches-pass.so", obj_path); + + } + + } + + if (getenv("LAF_TRANSFORM_COMPARES") || + getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES")) { + + if (lto_mode && !have_c) { + + cc_params[cc_par_cnt++] = alloc_printf( + "-Wl,-mllvm=-load=%s/compare-transform-pass.so", obj_path); + + } else { + + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/compare-transform-pass.so", obj_path); + + } + + } + + if (getenv("LAF_SPLIT_COMPARES") || getenv("AFL_LLVM_LAF_SPLIT_COMPARES") || + getenv("AFL_LLVM_LAF_SPLIT_FLOATS")) { + + if (lto_mode && !have_c) { + + cc_params[cc_par_cnt++] = alloc_printf( + "-Wl,-mllvm=-load=%s/split-compares-pass.so", obj_path); + + } else { + + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/split-compares-pass.so", obj_path); + + } + + } + + // /laf + + unsetenv("AFL_LD"); + unsetenv("AFL_LD_CALLER"); + if (cmplog_mode) { + + if (lto_mode && !have_c) { + + cc_params[cc_par_cnt++] = alloc_printf( + "-Wl,-mllvm=-load=%s/cmplog-routines-pass.so", obj_path); + cc_params[cc_par_cnt++] = alloc_printf( + "-Wl,-mllvm=-load=%s/cmplog-instructions-pass.so", obj_path); + cc_params[cc_par_cnt++] = alloc_printf( + "-Wl,-mllvm=-load=%s/split-switches-pass.so", obj_path); + + } else { + + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/cmplog-routines-pass.so", obj_path); + + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/cmplog-instructions-pass.so", obj_path); + + // reuse split switches from laf + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/split-switches-pass.so", obj_path); + + } + + cc_params[cc_par_cnt++] = "-fno-inline"; + + } + +#if LLVM_MAJOR >= 13 + // fuck you llvm 13 + cc_params[cc_par_cnt++] = "-fno-experimental-new-pass-manager"; +#endif + + if (lto_mode && !have_c) { + + u8 *ld_path = strdup(AFL_REAL_LD); + if (!*ld_path) ld_path = "ld.lld"; +#if defined(AFL_CLANG_LDPATH) && LLVM_MAJOR >= 12 + cc_params[cc_par_cnt++] = alloc_printf("--ld-path=%s", ld_path); +#else + cc_params[cc_par_cnt++] = alloc_printf("-fuse-ld=%s", ld_path); +#endif + + cc_params[cc_par_cnt++] = "-Wl,--allow-multiple-definition"; + + if (instrument_mode == INSTRUMENT_CFG || + instrument_mode == INSTRUMENT_PCGUARD) + cc_params[cc_par_cnt++] = alloc_printf( + "-Wl,-mllvm=-load=%s/SanitizerCoverageLTO.so", obj_path); + else + + cc_params[cc_par_cnt++] = alloc_printf( + "-Wl,-mllvm=-load=%s/afl-llvm-lto-instrumentation.so", obj_path); + cc_params[cc_par_cnt++] = lto_flag; + + } else { + + if (instrument_mode == INSTRUMENT_PCGUARD) { + +#if LLVM_MAJOR > 10 || (LLVM_MAJOR == 10 && LLVM_MINOR > 0) + #ifdef __ANDROID__ + cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard"; + #else + if (have_instr_list) { + + if (!be_quiet) + SAYF( + "Using unoptimized trace-pc-guard, due usage of " + "-fsanitize-coverage-allow/denylist, you can use " + "AFL_LLVM_ALLOWLIST/AFL_LLMV_DENYLIST instead.\n"); + cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard"; + + } else { + + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/SanitizerCoveragePCGUARD.so", obj_path); + + } + + #endif +#else + #if LLVM_MAJOR >= 4 + if (!be_quiet) + SAYF( + "Using unoptimized trace-pc-guard, upgrade to llvm 10.0.1+ for " + "enhanced version.\n"); + cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard"; + #else + FATAL("pcguard instrumentation requires llvm 4.0.1+"); + #endif +#endif + + } else if (instrument_mode == INSTRUMENT_LLVMNATIVE) { + +#if LLVM_MAJOR >= 4 + cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard"; +#else + FATAL("pcguard instrumentation requires llvm 4.0.1+"); +#endif + + } else { + + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + if (instrument_mode == INSTRUMENT_CFG) + cc_params[cc_par_cnt++] = + alloc_printf("%s/libLLVMInsTrim.so", obj_path); + else + cc_params[cc_par_cnt++] = + alloc_printf("%s/afl-llvm-pass.so", obj_path); + + } + + } + + // cc_params[cc_par_cnt++] = "-Qunused-arguments"; + + // in case LLVM is installed not via a package manager or "make install" + // e.g. compiled download or compiled from github then its ./lib directory + // might not be in the search path. Add it if so. + u8 *libdir = strdup(LLVM_LIBDIR); + if (plusplus_mode && strlen(libdir) && strncmp(libdir, "/usr", 4) && + strncmp(libdir, "/lib", 4)) { + + cc_params[cc_par_cnt++] = "-rpath"; + cc_params[cc_par_cnt++] = libdir; + + } else { + + free(libdir); + + } + + if (lto_mode && argc > 1) { + + u32 idx; + for (idx = 1; idx < argc; idx++) { + + if (!strncasecmp(argv[idx], "-fpic", 5)) have_pic = 1; + + } + + if (!have_pic) cc_params[cc_par_cnt++] = "-fPIC"; + + } + + } + + /* Detect stray -v calls from ./configure scripts. */ + + u8 skip_next = 0; + while (--argc) { + + u8 *cur = *(++argv); + + if (skip_next) { + + skip_next = 0; + continue; + + } + + if (!strncmp(cur, "--afl", 5)) continue; + if (lto_mode && !strncmp(cur, "-fuse-ld=", 9)) continue; + if (lto_mode && !strncmp(cur, "--ld-path=", 10)) continue; + if (!strncmp(cur, "-fno-unroll", 11)) continue; + if (strstr(cur, "afl-compiler-rt") || strstr(cur, "afl-llvm-rt")) continue; + if (!strcmp(cur, "-Wl,-z,defs") || !strcmp(cur, "-Wl,--no-undefined") || + !strcmp(cur, "--no-undefined")) { + + continue; + + } + + if (!strcmp(cur, "-z")) { + + u8 *param = *(argv + 1); + if (!strcmp(param, "defs")) { + + skip_next = 1; + continue; + + } + + } + + if (!strncmp(cur, "-fsanitize=fuzzer-", strlen("-fsanitize=fuzzer-")) || + !strncmp(cur, "-fsanitize-coverage", strlen("-fsanitize-coverage"))) { + + if (!be_quiet) { WARNF("Found '%s' - stripping!", cur); } + continue; + + } + + if (!strcmp(cur, "-fsanitize=fuzzer")) { + + u8 *afllib = find_object("libAFLDriver.a", argv[0]); + + if (!be_quiet) + WARNF( + "Found erroneous '-fsanitize=fuzzer', trying to replace with " + "libAFLDriver.a"); + + if (!afllib) { + + WARNF( + "Cannot find 'libAFLDriver.a' to replace a wrong " + "'-fsanitize=fuzzer' in the flags - this will fail!"); + + } else { + + cc_params[cc_par_cnt++] = afllib; + + } + + continue; + + } + + if (!strcmp(cur, "-m32")) bit_mode = 32; + if (!strcmp(cur, "armv7a-linux-androideabi")) bit_mode = 32; + if (!strcmp(cur, "-m64")) bit_mode = 64; + + if (!strncmp(cur, "-fsanitize-coverage-", 20) && strstr(cur, "list=")) + have_instr_list = 1; + + if (!strcmp(cur, "-fsanitize=address") || !strcmp(cur, "-fsanitize=memory")) + asan_set = 1; + + if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1; + + if (!strcmp(cur, "-x")) x_set = 1; + if (!strcmp(cur, "-E")) preprocessor_only = 1; + if (!strcmp(cur, "-shared")) shared_linking = 1; + if (!strcmp(cur, "-c")) have_c = 1; + + if (!strncmp(cur, "-O", 2)) have_o = 1; + if (!strncmp(cur, "-funroll-loop", 13)) have_unroll = 1; + + cc_params[cc_par_cnt++] = cur; + + } + + 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) { + + 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_USE_UBSAN")) { + + cc_params[cc_par_cnt++] = "-fsanitize=undefined"; + cc_params[cc_par_cnt++] = "-fsanitize-undefined-trap-on-error"; + cc_params[cc_par_cnt++] = "-fno-sanitize-recover=all"; + + } + + if (getenv("AFL_USE_CFISAN")) { + + if (!lto_mode) { + + uint32_t i = 0, found = 0; + while (envp[i] != NULL && !found) + if (strncmp("-flto", envp[i++], 5) == 0) found = 1; + if (!found) cc_params[cc_par_cnt++] = "-flto"; + + } + + cc_params[cc_par_cnt++] = "-fsanitize=cfi"; + cc_params[cc_par_cnt++] = "-fvisibility=hidden"; + + } + + if (!getenv("AFL_DONT_OPTIMIZE")) { + + cc_params[cc_par_cnt++] = "-g"; + if (!have_o) cc_params[cc_par_cnt++] = "-O3"; + if (!have_unroll) cc_params[cc_par_cnt++] = "-funroll-loops"; + // if (strlen(march_opt) > 1 && march_opt[0] == '-') + // cc_params[cc_par_cnt++] = march_opt; + + } + + if (getenv("AFL_NO_BUILTIN") || getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES") || + getenv("LAF_TRANSFORM_COMPARES") || getenv("AFL_LLVM_LAF_ALL") || + lto_mode) { + + 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-bcmp"; + cc_params[cc_par_cnt++] = "-fno-builtin-strstr"; + cc_params[cc_par_cnt++] = "-fno-builtin-strcasestr"; + + } + +#if defined(USEMMAP) && !defined(__HAIKU__) + if (!have_c) cc_params[cc_par_cnt++] = "-lrt"; +#endif + + cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_CONTROL=1"; + cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1"; + cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1"; + + /* When the user tries to use persistent or deferred forkserver modes by + appending a single line to the program, we want to reliably inject a + signature into the binary (to be picked up by afl-fuzz) and we want + to call a function from the runtime .o file. This is unnecessarily + painful for three reasons: + + 1) We need to convince the compiler not to optimize out the signature. + This is done with __attribute__((used)). + + 2) We need to convince the linker, when called with -Wl,--gc-sections, + not to do the same. This is done by forcing an assignment to a + 'volatile' pointer. + + 3) We need to declare __afl_persistent_loop() in the global namespace, + but doing this within a method in a class is hard - :: and extern "C" + are forbidden and __attribute__((alias(...))) doesn't work. Hence the + __asm__ aliasing trick. + + */ + + cc_params[cc_par_cnt++] = + "-D__AFL_FUZZ_INIT()=" + "int __afl_sharedmem_fuzzing = 1;" + "extern unsigned int *__afl_fuzz_len;" + "extern unsigned char *__afl_fuzz_ptr;" + "unsigned char __afl_fuzz_alt[1048576];" + "unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;"; + + if (plusplus_mode) { + + cc_params[cc_par_cnt++] = + "-D__AFL_COVERAGE()=int __afl_selective_coverage = 1;" + "extern \"C\" void __afl_coverage_discard();" + "extern \"C\" void __afl_coverage_skip();" + "extern \"C\" void __afl_coverage_on();" + "extern \"C\" void __afl_coverage_off();"; + + } else { + + cc_params[cc_par_cnt++] = + "-D__AFL_COVERAGE()=int __afl_selective_coverage = 1;" + "void __afl_coverage_discard();" + "void __afl_coverage_skip();" + "void __afl_coverage_on();" + "void __afl_coverage_off();"; + + } + + cc_params[cc_par_cnt++] = + "-D__AFL_COVERAGE_START_OFF()=int __afl_selective_coverage_start_off = " + "1;"; + cc_params[cc_par_cnt++] = "-D__AFL_COVERAGE_ON()=__afl_coverage_on()"; + cc_params[cc_par_cnt++] = "-D__AFL_COVERAGE_OFF()=__afl_coverage_off()"; + cc_params[cc_par_cnt++] = + "-D__AFL_COVERAGE_DISCARD()=__afl_coverage_discard()"; + cc_params[cc_par_cnt++] = "-D__AFL_COVERAGE_SKIP()=__afl_coverage_skip()"; + cc_params[cc_par_cnt++] = + "-D__AFL_FUZZ_TESTCASE_BUF=(__afl_fuzz_ptr ? __afl_fuzz_ptr : " + "__afl_fuzz_alt_ptr)"; + cc_params[cc_par_cnt++] = + "-D__AFL_FUZZ_TESTCASE_LEN=(__afl_fuzz_ptr ? *__afl_fuzz_len : " + "(*__afl_fuzz_len = read(0, __afl_fuzz_alt_ptr, 1048576)) == 0xffffffff " + "? 0 : *__afl_fuzz_len)"; + + cc_params[cc_par_cnt++] = + "-D__AFL_LOOP(_A)=" + "({ static volatile char *_B __attribute__((used)); " + " _B = (char*)\"" PERSIST_SIG + "\"; " +#ifdef __APPLE__ + "__attribute__((visibility(\"default\"))) " + "int _L(unsigned int) __asm__(\"___afl_persistent_loop\"); " +#else + "__attribute__((visibility(\"default\"))) " + "int _L(unsigned int) __asm__(\"__afl_persistent_loop\"); " +#endif /* ^__APPLE__ */ + "_L(_A); })"; + + cc_params[cc_par_cnt++] = + "-D__AFL_INIT()=" + "do { static volatile char *_A __attribute__((used)); " + " _A = (char*)\"" DEFER_SIG + "\"; " +#ifdef __APPLE__ + "__attribute__((visibility(\"default\"))) " + "void _I(void) __asm__(\"___afl_manual_init\"); " +#else + "__attribute__((visibility(\"default\"))) " + "void _I(void) __asm__(\"__afl_manual_init\"); " +#endif /* ^__APPLE__ */ + "_I(); } while (0)"; + + if (x_set) { + + cc_params[cc_par_cnt++] = "-x"; + cc_params[cc_par_cnt++] = "none"; + + } + + // prevent unnecessary build errors + cc_params[cc_par_cnt++] = "-Wno-unused-command-line-argument"; + + if (preprocessor_only || have_c) { + + /* In the preprocessor_only case (-E), we are not actually compiling at + all but requesting the compiler to output preprocessed sources only. + We must not add the runtime in this case because the compiler will + simply output its binary content back on stdout, breaking any build + systems that rely on a separate source preprocessing step. */ + cc_params[cc_par_cnt] = NULL; + return; + + } + +#ifndef __ANDROID__ + + if (compiler_mode != GCC && compiler_mode != CLANG) { + + switch (bit_mode) { + + case 0: + if (!shared_linking) + cc_params[cc_par_cnt++] = + alloc_printf("%s/afl-compiler-rt.o", obj_path); + if (lto_mode) + cc_params[cc_par_cnt++] = + alloc_printf("%s/afl-llvm-rt-lto.o", obj_path); + break; + + case 32: + if (!shared_linking) { + + cc_params[cc_par_cnt++] = + alloc_printf("%s/afl-compiler-rt-32.o", obj_path); + if (access(cc_params[cc_par_cnt - 1], R_OK)) + FATAL("-m32 is not supported by your compiler"); + + } + + if (lto_mode) { + + cc_params[cc_par_cnt++] = + alloc_printf("%s/afl-llvm-rt-lto-32.o", obj_path); + if (access(cc_params[cc_par_cnt - 1], R_OK)) + FATAL("-m32 is not supported by your compiler"); + + } + + break; + + case 64: + if (!shared_linking) { + + cc_params[cc_par_cnt++] = + alloc_printf("%s/afl-compiler-rt-64.o", obj_path); + if (access(cc_params[cc_par_cnt - 1], R_OK)) + FATAL("-m64 is not supported by your compiler"); + + } + + if (lto_mode) { + + cc_params[cc_par_cnt++] = + alloc_printf("%s/afl-llvm-rt-lto-64.o", obj_path); + if (access(cc_params[cc_par_cnt - 1], R_OK)) + FATAL("-m64 is not supported by your compiler"); + + } + + break; + + } + + #if !defined(__APPLE__) && !defined(__sun) + if (shared_linking) + cc_params[cc_par_cnt++] = + alloc_printf("-Wl,--dynamic-list=%s/dynamic_list.txt", obj_path); + #endif + + } + + #if defined(USEMMAP) && !defined(__HAIKU__) + cc_params[cc_par_cnt++] = "-lrt"; + #endif + +#endif + + cc_params[cc_par_cnt] = NULL; + +} + +/* Main entry point */ + +int main(int argc, char **argv, char **envp) { + + int i, passthrough = 0; + char *callname = argv[0], *ptr = NULL; + + if (getenv("AFL_DEBUG")) { + + debug = 1; + if (strcmp(getenv("AFL_DEBUG"), "0") == 0) unsetenv("AFL_DEBUG"); + + } else if (getenv("AFL_QUIET")) + + be_quiet = 1; + + if (getenv("AFL_LLVM_INSTRUMENT_FILE") || getenv("AFL_LLVM_WHITELIST") || + getenv("AFL_LLVM_ALLOWLIST") || getenv("AFL_LLVM_DENYLIST") || + getenv("AFL_LLVM_BLOCKLIST")) { + + have_instr_env = 1; + + } + + if (getenv("AFL_PASSTHROUGH") || getenv("AFL_NOOPT")) { + + passthrough = 1; + if (!debug) { be_quiet = 1; } + + } + + if ((ptr = strrchr(callname, '/')) != NULL) callname = ptr + 1; + argvnull = (u8 *)argv[0]; + check_environment_vars(envp); + + if ((ptr = find_object("as", argv[0])) != NULL) { + + have_gcc = 1; + ck_free(ptr); + + } + +#if (LLVM_MAJOR > 2) + + if ((ptr = find_object("SanitizerCoverageLTO.so", argv[0])) != NULL) { + + have_lto = 1; + ck_free(ptr); + + } + + if ((ptr = find_object("cmplog-routines-pass.so", argv[0])) != NULL) { + + have_llvm = 1; + ck_free(ptr); + + } + +#endif + +#ifdef __ANDROID__ + have_llvm = 1; +#endif + + if ((ptr = find_object("afl-gcc-pass.so", argv[0])) != NULL) { + + have_gcc_plugin = 1; + ck_free(ptr); + + } + +#if (LLVM_MAJOR > 2) + + if (strncmp(callname, "afl-clang-fast", 14) == 0) { + + compiler_mode = LLVM; + + } else if (strncmp(callname, "afl-clang-lto", 13) == 0 || + + strncmp(callname, "afl-lto", 7) == 0) { + + compiler_mode = LTO; + + } else + +#endif + if (strncmp(callname, "afl-gcc-fast", 12) == 0 || + + strncmp(callname, "afl-g++-fast", 12) == 0) { + + compiler_mode = GCC_PLUGIN; + + } else if (strncmp(callname, "afl-gcc", 7) == 0 || + + strncmp(callname, "afl-g++", 7) == 0) { + + compiler_mode = GCC; + + } else if (strcmp(callname, "afl-clang") == 0 || + + strcmp(callname, "afl-clang++") == 0) { + + compiler_mode = CLANG; + + } + + if ((ptr = getenv("AFL_CC_COMPILER"))) { + + if (compiler_mode) { + + WARNF( + "\"AFL_CC_COMPILER\" is set but a specific compiler was already " + "selected by command line parameter or symlink, ignoring the " + "environment variable!"); + + } else { + + if (strncasecmp(ptr, "LTO", 3) == 0) { + + compiler_mode = LTO; + + } else if (strncasecmp(ptr, "LLVM", 4) == 0) { + + compiler_mode = LLVM; + + } else if (strncasecmp(ptr, "GCC_P", 5) == 0 || + + strncasecmp(ptr, "GCC-P", 5) == 0 || + strncasecmp(ptr, "GCCP", 4) == 0) { + + compiler_mode = GCC_PLUGIN; + + } else if (strcasecmp(ptr, "GCC") == 0) { + + compiler_mode = GCC; + + } else + + FATAL("Unknown AFL_CC_COMPILER mode: %s\n", ptr); + + } + + } + + if (strcmp(callname, "afl-clang") == 0 || + strcmp(callname, "afl-clang++") == 0) { + + clang_mode = 1; + compiler_mode = CLANG; + + if (strcmp(callname, "afl-clang++") == 0) { plusplus_mode = 1; } + + } + + for (i = 1; i < argc; i++) { + + if (strncmp(argv[i], "--afl", 5) == 0) { + + if (compiler_mode) + WARNF( + "--afl-... compiler mode supersedes the AFL_CC_COMPILER and " + "symlink compiler selection!"); + + ptr = argv[i]; + ptr += 5; + while (*ptr == '-') + ptr++; + + if (strncasecmp(ptr, "LTO", 3) == 0) { + + compiler_mode = LTO; + + } else if (strncasecmp(ptr, "LLVM", 4) == 0) { + + compiler_mode = LLVM; + + } else if (strncasecmp(ptr, "PCGUARD", 7) == 0 || + + strncasecmp(ptr, "PC-GUARD", 8) == 0) { + + compiler_mode = LLVM; + instrument_mode = INSTRUMENT_PCGUARD; + + } else if (strcasecmp(ptr, "INSTRIM") == 0 || + + strcasecmp(ptr, "CFG") == 0) { + + compiler_mode = LLVM; + instrument_mode = INSTRUMENT_CFG; + + } else if (strcasecmp(ptr, "AFL") == 0 || + + strcasecmp(ptr, "CLASSIC") == 0) { + + compiler_mode = LLVM; + instrument_mode = INSTRUMENT_CLASSIC; + + } else if (strcasecmp(ptr, "LLVMNATIVE") == 0 || + + strcasecmp(ptr, "LLVM-NATIVE") == 0) { + + compiler_mode = LLVM; + instrument_mode = INSTRUMENT_LLVMNATIVE; + + } else if (strncasecmp(ptr, "GCC_P", 5) == 0 || + + strncasecmp(ptr, "GCC-P", 5) == 0 || + strncasecmp(ptr, "GCCP", 4) == 0) { + + compiler_mode = GCC_PLUGIN; + + } else if (strcasecmp(ptr, "GCC") == 0) { + + compiler_mode = GCC; + + } else if (strncasecmp(ptr, "CLANG", 5) == 0) { + + compiler_mode = CLANG; + + } else + + FATAL("Unknown --afl-... compiler mode: %s\n", argv[i]); + + } + + } + + if (strlen(callname) > 2 && + (strncmp(callname + strlen(callname) - 2, "++", 2) == 0 || + strstr(callname, "-g++") != NULL)) + plusplus_mode = 1; + + if (getenv("USE_TRACE_PC") || getenv("AFL_USE_TRACE_PC") || + getenv("AFL_LLVM_USE_TRACE_PC") || getenv("AFL_TRACE_PC")) { + + if (instrument_mode == 0) + instrument_mode = INSTRUMENT_PCGUARD; + else if (instrument_mode != INSTRUMENT_PCGUARD) + FATAL("you cannot set AFL_LLVM_INSTRUMENT and AFL_TRACE_PC together"); + + } + + if (have_instr_env && getenv("AFL_DONT_OPTIMIZE")) { + + WARNF( + "AFL_LLVM_ALLOWLIST/DENYLIST and AFL_DONT_OPTIMIZE cannot be combined " + "for file matching, only function matching!"); + + } + + if (getenv("AFL_LLVM_INSTRIM") || getenv("INSTRIM") || + getenv("INSTRIM_LIB")) { + + if (instrument_mode == 0) + instrument_mode = INSTRUMENT_CFG; + else if (instrument_mode != INSTRUMENT_CFG) + FATAL("you cannot set AFL_LLVM_INSTRUMENT and AFL_LLVM_INSTRIM together"); + + } + + if (getenv("AFL_LLVM_CTX")) instrument_opt_mode |= INSTRUMENT_OPT_CTX; + if (getenv("AFL_LLVM_CALLER")) instrument_opt_mode |= INSTRUMENT_OPT_CALLER; + + if (getenv("AFL_LLVM_NGRAM_SIZE")) { + + instrument_opt_mode |= INSTRUMENT_OPT_NGRAM; + ngram_size = atoi(getenv("AFL_LLVM_NGRAM_SIZE")); + if (ngram_size < 2 || ngram_size > NGRAM_SIZE_MAX) + FATAL( + "NGRAM instrumentation mode must be between 2 and NGRAM_SIZE_MAX " + "(%u)", + NGRAM_SIZE_MAX); + + } + + if (getenv("AFL_LLVM_CTX_K")) { + + ctx_k = atoi(getenv("AFL_LLVM_CTX_K")); + if (ctx_k < 1 || ctx_k > CTX_MAX_K) + FATAL("K-CTX instrumentation mode must be between 1 and CTX_MAX_K (%u)", + CTX_MAX_K); + if (ctx_k == 1) { + + setenv("AFL_LLVM_CALLER", "1", 1); + unsetenv("AFL_LLVM_CTX_K"); + instrument_opt_mode |= INSTRUMENT_OPT_CALLER; + + } else { + + instrument_opt_mode |= INSTRUMENT_OPT_CTX_K; + + } + + } + + if (getenv("AFL_LLVM_INSTRUMENT")) { + + u8 *ptr2 = strtok(getenv("AFL_LLVM_INSTRUMENT"), ":,;"); + + while (ptr2) { + + if (strncasecmp(ptr2, "afl", strlen("afl")) == 0 || + strncasecmp(ptr2, "classic", strlen("classic")) == 0) { + + if (instrument_mode == INSTRUMENT_LTO) { + + instrument_mode = INSTRUMENT_CLASSIC; + lto_mode = 1; + + } else if (!instrument_mode || instrument_mode == INSTRUMENT_AFL) + + instrument_mode = INSTRUMENT_AFL; + else + FATAL("main instrumentation mode already set with %s", + instrument_mode_string[instrument_mode]); + + } + + if (strncasecmp(ptr2, "pc-guard", strlen("pc-guard")) == 0 || + strncasecmp(ptr2, "pcguard", strlen("pcguard")) == 0) { + + if (!instrument_mode || instrument_mode == INSTRUMENT_PCGUARD) + instrument_mode = INSTRUMENT_PCGUARD; + else + FATAL("main instrumentation mode already set with %s", + instrument_mode_string[instrument_mode]); + + } + + if (strncasecmp(ptr2, "llvmnative", strlen("llvmnative")) == 0 || + strncasecmp(ptr2, "llvm-native", strlen("llvm-native")) == 0) { + + if (!instrument_mode || instrument_mode == INSTRUMENT_LLVMNATIVE) + instrument_mode = INSTRUMENT_LLVMNATIVE; + else + FATAL("main instrumentation mode already set with %s", + instrument_mode_string[instrument_mode]); + + } + + if (strncasecmp(ptr2, "cfg", strlen("cfg")) == 0 || + strncasecmp(ptr2, "instrim", strlen("instrim")) == 0) { + + if (instrument_mode == INSTRUMENT_LTO) { + + instrument_mode = INSTRUMENT_CFG; + lto_mode = 1; + + } else if (!instrument_mode || instrument_mode == INSTRUMENT_CFG) + + instrument_mode = INSTRUMENT_CFG; + else + FATAL("main instrumentation mode already set with %s", + instrument_mode_string[instrument_mode]); + + } + + if (strncasecmp(ptr2, "lto", strlen("lto")) == 0) { + + lto_mode = 1; + if (!instrument_mode || instrument_mode == INSTRUMENT_LTO) + instrument_mode = INSTRUMENT_LTO; + else if (instrument_mode != INSTRUMENT_CFG) + FATAL("main instrumentation mode already set with %s", + instrument_mode_string[instrument_mode]); + + } + + if (strcasecmp(ptr2, "gcc") == 0) { + + if (!instrument_mode || instrument_mode == INSTRUMENT_GCC) + instrument_mode = INSTRUMENT_GCC; + else if (instrument_mode != INSTRUMENT_GCC) + FATAL("main instrumentation mode already set with %s", + instrument_mode_string[instrument_mode]); + compiler_mode = GCC; + + } + + if (strcasecmp(ptr2, "clang") == 0) { + + if (!instrument_mode || instrument_mode == INSTRUMENT_CLANG) + instrument_mode = INSTRUMENT_CLANG; + else if (instrument_mode != INSTRUMENT_CLANG) + FATAL("main instrumentation mode already set with %s", + instrument_mode_string[instrument_mode]); + compiler_mode = CLANG; + + } + + if (strncasecmp(ptr2, "ctx-", strlen("ctx-")) == 0) { + + u8 *ptr3 = ptr2 + strlen("ctx-"); + while (*ptr3 && (*ptr3 < '0' || *ptr3 > '9')) + ptr3++; + + if (!*ptr3) { + + if ((ptr3 = getenv("AFL_LLVM_CTX_K")) == NULL) + FATAL( + "you must set the K-CTX K with (e.g. for value 2) " + "AFL_LLVM_INSTRUMENT=ctx-2"); + + } + + ctx_k = atoi(ptr3); + if (ctx_k < 1 || ctx_k > CTX_MAX_K) + FATAL( + "K-CTX instrumentation option must be between 1 and CTX_MAX_K " + "(%u)", + CTX_MAX_K); + + if (ctx_k == 1) { + + instrument_opt_mode |= INSTRUMENT_OPT_CALLER; + setenv("AFL_LLVM_CALLER", "1", 1); + unsetenv("AFL_LLVM_CTX_K"); + + } else { + + instrument_opt_mode |= (INSTRUMENT_OPT_CTX_K); + u8 *ptr4 = alloc_printf("%u", ctx_k); + setenv("AFL_LLVM_CTX_K", ptr4, 1); + + } + + } + + if (strncasecmp(ptr2, "ctx", strlen("ctx")) == 0) { + + instrument_opt_mode |= INSTRUMENT_OPT_CTX; + setenv("AFL_LLVM_CTX", "1", 1); + + } + + if (strncasecmp(ptr2, "caller", strlen("caller")) == 0) { + + instrument_opt_mode |= INSTRUMENT_OPT_CALLER; + setenv("AFL_LLVM_CALLER", "1", 1); + + } + + if (strncasecmp(ptr2, "ngram", strlen("ngram")) == 0) { + + u8 *ptr3 = ptr2 + strlen("ngram"); + while (*ptr3 && (*ptr3 < '0' || *ptr3 > '9')) + ptr3++; + + if (!*ptr3) { + + if ((ptr3 = getenv("AFL_LLVM_NGRAM_SIZE")) == NULL) + FATAL( + "you must set the NGRAM size with (e.g. for value 2) " + "AFL_LLVM_INSTRUMENT=ngram-2"); + + } + + ngram_size = atoi(ptr3); + if (ngram_size < 2 || ngram_size > NGRAM_SIZE_MAX) + FATAL( + "NGRAM instrumentation option must be between 2 and " + "NGRAM_SIZE_MAX (%u)", + NGRAM_SIZE_MAX); + instrument_opt_mode |= (INSTRUMENT_OPT_NGRAM); + u8 *ptr4 = alloc_printf("%u", ngram_size); + setenv("AFL_LLVM_NGRAM_SIZE", ptr4, 1); + + } + + ptr2 = strtok(NULL, ":,;"); + + } + + } + + if ((instrument_opt_mode & INSTRUMENT_OPT_CTX) && + (instrument_opt_mode & INSTRUMENT_OPT_CALLER)) { + + FATAL("you cannot set CTX and CALLER together"); + + } + + if ((instrument_opt_mode & INSTRUMENT_OPT_CTX) && + (instrument_opt_mode & INSTRUMENT_OPT_CTX_K)) { + + FATAL("you cannot set CTX and K-CTX together"); + + } + + if ((instrument_opt_mode & INSTRUMENT_OPT_CALLER) && + (instrument_opt_mode & INSTRUMENT_OPT_CTX_K)) { + + FATAL("you cannot set CALLER and K-CTX together"); + + } + + if (instrument_opt_mode && instrument_mode == INSTRUMENT_DEFAULT && + (compiler_mode == LLVM || compiler_mode == UNSET)) { + + instrument_mode = INSTRUMENT_CLASSIC; + compiler_mode = LLVM; + + } + + if (!compiler_mode) { + + // lto is not a default because outside of afl-cc RANLIB and AR have to + // be set to llvm versions so this would work + if (have_llvm) + compiler_mode = LLVM; + else if (have_gcc_plugin) + compiler_mode = GCC_PLUGIN; + else if (have_gcc) + compiler_mode = GCC; + else if (have_lto) + compiler_mode = LTO; + else + FATAL("no compiler mode available"); + + } + + if (compiler_mode == GCC) { + + if (clang_mode) { + + instrument_mode = INSTRUMENT_CLANG; + + } else { + + instrument_mode = INSTRUMENT_GCC; + + } + + } + + if (compiler_mode == CLANG) { instrument_mode = INSTRUMENT_CLANG; } + + if (argc < 2 || strncmp(argv[1], "-h", 2) == 0) { + + printf("afl-cc" VERSION + " by Michal Zalewski, Laszlo Szekeres, Marc Heuse\n"); + + SAYF( + "\n" + "afl-cc/afl-c++ [options]\n" + "\n" + "This is a helper application for afl-fuzz. It serves as a drop-in " + "replacement\n" + "for gcc and 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=afl-cc CXX=afl-c++ ./configure --disable-shared\n" + " cmake -DCMAKE_C_COMPILERC=afl-cc -DCMAKE_CXX_COMPILER=afl-c++ .\n" + " CC=afl-cc CXX=afl-c++ meson\n\n"); + + SAYF( + " |------------- FEATURES " + "-------------|\n" + "MODES: NCC PERSIST DICT LAF " + "CMPLOG SELECT\n" + " [LTO] llvm LTO: %s%s\n" + " PCGUARD DEFAULT yes yes yes yes yes " + " yes\n" + " CLASSIC yes yes yes yes yes " + " yes\n" + " [LLVM] llvm: %s%s\n" + " PCGUARD %s yes yes module yes yes " + "extern\n" + " CLASSIC %s no yes module yes yes " + "yes\n" + " - NORMAL\n" + " - CALLER\n" + " - CTX\n" + " - NGRAM-{2-16}\n" + " INSTRIM no yes module yes yes " + " yes\n" + " - NORMAL\n" + " - CALLER\n" + " - NGRAM-{2-16}\n" + " [GCC_PLUGIN] gcc plugin: %s%s\n" + " CLASSIC DEFAULT no yes no no no " + "yes\n" + " [GCC/CLANG] simple gcc/clang: %s%s\n" + " CLASSIC DEFAULT no no no no no " + "no\n\n", + have_lto ? "AVAILABLE" : "unavailable!", + compiler_mode == LTO ? " [SELECTED]" : "", + have_llvm ? "AVAILABLE" : "unavailable!", + compiler_mode == LLVM ? " [SELECTED]" : "", + LLVM_MAJOR > 6 ? "DEFAULT" : " ", + LLVM_MAJOR > 6 ? " " : "DEFAULT", + have_gcc_plugin ? "AVAILABLE" : "unavailable!", + compiler_mode == GCC_PLUGIN ? " [SELECTED]" : "", + have_gcc ? "AVAILABLE" : "unavailable!", + (compiler_mode == GCC || compiler_mode == CLANG) ? " [SELECTED]" : ""); + + SAYF( + "Modes:\n" + " To select the compiler mode use a symlink version (e.g. " + "afl-clang-fast), set\n" + " the environment variable AFL_CC_COMPILER to a mode (e.g. LLVM) or " + "use the\n" + " command line parameter --afl-MODE (e.g. --afl-llvm). If none is " + "selected,\n" + " afl-cc will select the best available (LLVM -> GCC_PLUGIN -> GCC).\n" + " The best is LTO but it often needs RANLIB and AR settings outside " + "of afl-cc.\n\n"); + +#if LLVM_MAJOR > 10 || (LLVM_MAJOR == 10 && LLVM_MINOR > 0) + #define NATIVE_MSG \ + " NATIVE: use llvm's native PCGUARD instrumentation (less " \ + "performant)\n" +#else + #define NATIVE_MSG "" +#endif + + SAYF( + "Sub-Modes: (set via env AFL_LLVM_INSTRUMENT, afl-cc selects the best " + "available)\n" + " PCGUARD: Dominator tree instrumentation (best!) (README.llvm.md)\n" + + NATIVE_MSG + + " CLASSIC: decision target instrumentation (README.llvm.md)\n" + " CALLER: CLASSIC + single callee context " + "(instrumentation/README.ctx.md)\n" + " CTX: CLASSIC + full callee context " + "(instrumentation/README.ctx.md)\n" + " NGRAM-x: CLASSIC + previous path " + "((instrumentation/README.ngram.md)\n" + " INSTRIM: Dominator tree (for LLVM <= 6.0) " + "(instrumentation/README.instrim.md)\n\n"); + +#undef NATIVE_MSG + + SAYF( + "Features: (see documentation links)\n" + " NCC: non-colliding coverage [automatic] (that is an amazing " + "thing!)\n" + " (instrumentation/README.lto.md)\n" + " PERSIST: persistent mode support [code] (huge speed increase!)\n" + " (instrumentation/README.persistent_mode.md)\n" + " DICT: dictionary in the target [yes=automatic or llvm module " + "pass]\n" + " (instrumentation/README.lto.md + " + "instrumentation/README.llvm.md)\n" + " LAF: comparison splitting [env] " + "(instrumentation/README.laf-intel.md)\n" + " CMPLOG: input2state exploration [env] " + "(instrumentation/README.cmplog.md)\n" + " SELECT: selective instrumentation (allow/deny) on filename or " + "function [env]\n" + " (instrumentation/README.instrument_list.md)\n\n"); + + if (argc < 2 || strncmp(argv[1], "-hh", 3)) { + + SAYF( + "To see all environment variables for the configuration of afl-cc " + "use \"-hh\".\n"); + + } else { + + SAYF( + "Environment variables used:\n" + " AFL_CC: path to the C compiler to use\n" + " AFL_CXX: path to the C++ compiler to use\n" + " AFL_DEBUG: enable developer debugging output\n" + " AFL_DONT_OPTIMIZE: disable optimization instead of -O3\n" + " AFL_NO_BUILTIN: no builtins for string compare functions (for " + "libtokencap.so)\n" + " AFL_NOOP: behave like a normal compiler (to pass configure " + "tests)\n" + " AFL_PATH: path to instrumenting pass and runtime " + "(afl-compiler-rt.*o)\n" + " AFL_IGNORE_UNKNOWN_ENVS: don't warn on unknown env vars\n" + " AFL_INST_RATIO: percentage of branches to instrument\n" + " AFL_QUIET: suppress verbose output\n" + " AFL_HARDEN: adds code hardening to catch memory bugs\n" + " AFL_USE_ASAN: activate address sanitizer\n" + " AFL_USE_CFISAN: activate control flow sanitizer\n" + " AFL_USE_MSAN: activate memory sanitizer\n" + " AFL_USE_UBSAN: activate undefined behaviour sanitizer\n"); + + if (have_gcc_plugin) + SAYF( + "\nGCC Plugin-specific environment variables:\n" + " AFL_GCC_OUT_OF_LINE: disable inlined instrumentation\n" + " AFL_GCC_SKIP_NEVERZERO: do not skip zero on trace counters\n" + " AFL_GCC_INSTRUMENT_FILE: enable selective instrumentation by " + "filename\n"); + +#if LLVM_MAJOR < 9 + #define COUNTER_BEHAVIOUR \ + " AFL_LLVM_NOT_ZERO: use cycling trace counters that skip zero\n" +#else + #define COUNTER_BEHAVIOUR \ + " AFL_LLVM_SKIP_NEVERZERO: do not skip zero on trace counters\n" +#endif + if (have_llvm) + SAYF( + "\nLLVM/LTO/afl-clang-fast/afl-clang-lto specific environment " + "variables:\n" + + COUNTER_BEHAVIOUR + + " AFL_LLVM_DICT2FILE: generate an afl dictionary based on found " + "comparisons\n" + " AFL_LLVM_LAF_ALL: enables all LAF splits/transforms\n" + " AFL_LLVM_LAF_SPLIT_COMPARES: enable cascaded comparisons\n" + " AFL_LLVM_LAF_SPLIT_COMPARES_BITW: size limit (default 8)\n" + " AFL_LLVM_LAF_SPLIT_SWITCHES: cascaded comparisons on switches\n" + " AFL_LLVM_LAF_SPLIT_FLOATS: cascaded comparisons on floats\n" + " AFL_LLVM_LAF_TRANSFORM_COMPARES: cascade comparisons for string " + "functions\n" + " AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST: enable " + "instrument allow/\n" + " deny listing (selective instrumentation)\n"); + + if (have_llvm) + SAYF( + " AFL_LLVM_CMPLOG: log operands of comparisons (RedQueen " + "mutator)\n" + " AFL_LLVM_INSTRUMENT: set instrumentation mode:\n" + " CLASSIC, INSTRIM, PCGUARD, LTO, GCC, CLANG, CALLER, CTX, " + "NGRAM-2 ..-16\n" + " You can also use the old environment variables instead:\n" + " AFL_LLVM_USE_TRACE_PC: use LLVM trace-pc-guard instrumentation\n" + " AFL_LLVM_INSTRIM: use light weight instrumentation InsTrim\n" + " AFL_LLVM_INSTRIM_LOOPHEAD: optimize loop tracing for speed " + "(option to INSTRIM)\n" + " AFL_LLVM_CALLER: use single context sensitive coverage (for " + "CLASSIC)\n" + " AFL_LLVM_CTX: use full context sensitive coverage (for " + "CLASSIC)\n" + " AFL_LLVM_NGRAM_SIZE: use ngram prev_loc count coverage (for " + "CLASSIC & INSTRIM)\n"); + +#ifdef AFL_CLANG_FLTO + if (have_lto) + SAYF( + "\nLTO/afl-clang-lto specific environment variables:\n" + " AFL_LLVM_MAP_ADDR: use a fixed coverage map address (speed), " + "e.g. " + "0x10000\n" + " AFL_LLVM_DOCUMENT_IDS: write all edge IDs and the corresponding " + "functions\n" + " into this file\n" + " AFL_LLVM_LTO_DONTWRITEID: don't write the highest ID used to a " + "global var\n" + " AFL_LLVM_LTO_STARTID: from which ID to start counting from for " + "a " + "bb\n" + " AFL_REAL_LD: use this lld linker instead of the compiled in " + "path\n" + "If anything fails - be sure to read README.lto.md!\n"); +#endif + + } + + SAYF( + "\nFor any information on the available instrumentations and options " + "please \n" + "consult the README.md, especially section 3.1 about instrumenting " + "targets.\n\n"); + +#if (LLVM_MAJOR > 2) + if (have_lto) + SAYF("afl-cc LTO with ld=%s %s\n", AFL_REAL_LD, AFL_CLANG_FLTO); + if (have_llvm) + SAYF("afl-cc LLVM version %d using the binary path \"%s\".\n", LLVM_MAJOR, + LLVM_BINDIR); +#endif + +#ifdef USEMMAP + #if !defined(__HAIKU__) + SAYF("Compiled with shm_open support.\n"); + #else + SAYF("Compiled with shm_open support (adds -lrt when linking).\n"); + #endif +#else + SAYF("Compiled with shmat support.\n"); +#endif + SAYF("\n"); + + SAYF( + "Do not be overwhelmed :) afl-cc uses good defaults if no options are " + "selected.\n" + "Read the documentation for FEATURES though, all are good but few are " + "defaults.\n" + "Recommended is afl-clang-lto with AFL_LLVM_CMPLOG or afl-clang-fast " + "with\n" + "AFL_LLVM_CMPLOG and AFL_LLVM_DICT2FILE.\n\n"); + + exit(1); + + } + + if (compiler_mode == LTO) { + + if (instrument_mode == 0 || instrument_mode == INSTRUMENT_LTO || + instrument_mode == INSTRUMENT_CFG || + instrument_mode == INSTRUMENT_PCGUARD) { + + lto_mode = 1; + // force CFG + // if (!instrument_mode) { + + instrument_mode = INSTRUMENT_PCGUARD; + // ptr = instrument_mode_string[instrument_mode]; + // } + + } else if (instrument_mode == INSTRUMENT_LTO || + + instrument_mode == INSTRUMENT_CLASSIC) { + + lto_mode = 1; + + } else { + + if (!be_quiet) + WARNF("afl-clang-lto called with mode %s, using that mode instead", + instrument_mode_string[instrument_mode]); + + } + + } + + if (instrument_mode == 0 && compiler_mode < GCC_PLUGIN) { + +#if LLVM_MAJOR <= 6 + instrument_mode = INSTRUMENT_AFL; +#else + #if LLVM_MAJOR < 11 && (LLVM_MAJOR < 10 || LLVM_MINOR < 1) + if (have_instr_env) { + + instrument_mode = INSTRUMENT_AFL; + if (!be_quiet) + WARNF( + "Switching to classic instrumentation because " + "AFL_LLVM_ALLOWLIST/DENYLIST does not work with PCGUARD < 10.0.1."); + + } else + + #endif + instrument_mode = INSTRUMENT_PCGUARD; + +#endif + + } + + if (instrument_opt_mode && compiler_mode != LLVM) + FATAL("CTX, CALLER and NGRAM can only be used in LLVM mode"); + + if (!instrument_opt_mode) { + + if (lto_mode && instrument_mode == INSTRUMENT_CFG) + instrument_mode = INSTRUMENT_PCGUARD; + ptr = instrument_mode_string[instrument_mode]; + + } else { + + char *ptr2 = alloc_printf(" + NGRAM-%u", ngram_size); + char *ptr3 = alloc_printf(" + K-CTX-%u", ctx_k); + + ptr = alloc_printf( + "%s%s%s%s%s", instrument_mode_string[instrument_mode], + (instrument_opt_mode & INSTRUMENT_OPT_CTX) ? " + CTX" : "", + (instrument_opt_mode & INSTRUMENT_OPT_CALLER) ? " + CALLER" : "", + (instrument_opt_mode & INSTRUMENT_OPT_NGRAM) ? ptr2 : "", + (instrument_opt_mode & INSTRUMENT_OPT_CTX_K) ? ptr3 : ""); + + ck_free(ptr2); + ck_free(ptr3); + + } + +#ifndef AFL_CLANG_FLTO + if (lto_mode) + FATAL( + "instrumentation mode LTO specified but LLVM support not available " + "(requires LLVM 11 or higher)"); +#endif + + if (instrument_opt_mode && instrument_mode == INSTRUMENT_CFG && + instrument_opt_mode & INSTRUMENT_OPT_CTX) + FATAL("CFG instrumentation mode supports NGRAM and CALLER, but not CTX."); + else if (instrument_opt_mode && instrument_mode != INSTRUMENT_CLASSIC) + // we will drop CFG/INSTRIM in the future so do not advertise + FATAL( + "CALLER, CTX and NGRAM instrumentation options can only be used with " + "the LLVM CLASSIC instrumentation mode."); + + if (getenv("AFL_LLVM_SKIP_NEVERZERO") && getenv("AFL_LLVM_NOT_ZERO")) + FATAL( + "AFL_LLVM_NOT_ZERO and AFL_LLVM_SKIP_NEVERZERO can not be set " + "together"); + +#if LLVM_MAJOR < 11 && (LLVM_MAJOR < 10 || LLVM_MINOR < 1) + if (instrument_mode == INSTRUMENT_PCGUARD && have_instr_env) { + + FATAL( + "Instrumentation type PCGUARD does not support " + "AFL_LLVM_ALLOWLIST/DENYLIST! Use LLVM 10.0.1+ instead."); + + } + +#endif + + u8 *ptr2; + + if ((ptr2 = getenv("AFL_LLVM_DICT2FILE")) != NULL && *ptr2 != '/') + FATAL("AFL_LLVM_DICT2FILE must be set to an absolute file path"); + + if ((isatty(2) && !be_quiet) || debug) { + + SAYF(cCYA + "afl-cc " VERSION cRST + " by Michal Zalewski, Laszlo Szekeres, Marc Heuse - mode: %s-%s\n", + compiler_mode_string[compiler_mode], ptr); + + } + + if (!be_quiet && (compiler_mode == GCC || compiler_mode == CLANG)) { + + WARNF( + "You are using outdated instrumentation, install LLVM and/or " + "gcc-plugin and use afl-clang-fast/afl-clang-lto/afl-gcc-fast " + "instead!"); + + } + + if (debug) { + + DEBUGF("cd '%s';", getthecwd()); + for (i = 0; i < argc; i++) + SAYF(" '%s'", argv[i]); + SAYF("\n"); + fflush(stdout); + fflush(stderr); + + } + + if (getenv("AFL_LLVM_LAF_ALL")) { + + setenv("AFL_LLVM_LAF_SPLIT_SWITCHES", "1", 1); + setenv("AFL_LLVM_LAF_SPLIT_COMPARES", "1", 1); + setenv("AFL_LLVM_LAF_SPLIT_FLOATS", "1", 1); + setenv("AFL_LLVM_LAF_TRANSFORM_COMPARES", "1", 1); + + } + + cmplog_mode = getenv("AFL_CMPLOG") || getenv("AFL_LLVM_CMPLOG"); + if (!be_quiet && cmplog_mode) + printf("CmpLog mode by <andreafioraldi@gmail.com>\n"); + +#ifndef __ANDROID__ + ptr = find_object("afl-compiler-rt.o", argv[0]); + + if (!ptr) { + + FATAL( + "Unable to find 'afl-compiler-rt.o'. Please set the AFL_PATH " + "environment variable."); + + } + + if (debug) { DEBUGF("rt=%s obj_path=%s\n", ptr, obj_path); } + + ck_free(ptr); +#endif + + edit_params(argc, argv, envp); + + if (debug) { + + DEBUGF("cd '%s';", getthecwd()); + for (i = 0; i < (s32)cc_par_cnt; i++) + SAYF(" '%s'", cc_params[i]); + SAYF("\n"); + fflush(stdout); + fflush(stderr); + + } + + if (passthrough) { + + argv[0] = cc_params[0]; + execvp(cc_params[0], (char **)argv); + + } else { + + execvp(cc_params[0], (char **)cc_params); + + } + + FATAL("Oops, failed to execute '%s' - check your PATH", cc_params[0]); + + return 0; + +} + diff --git a/src/afl-common.c b/src/afl-common.c index c023789b..7e56ce3f 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -26,6 +26,7 @@ #include <stdlib.h> #include <stdio.h> #include <strings.h> +#include <math.h> #include "debug.h" #include "alloc-inl.h" @@ -46,7 +47,11 @@ u8 be_quiet = 0; u8 *doc_path = ""; u8 last_intr = 0; -void detect_file_args(char **argv, u8 *prog_in, u8 *use_stdin) { +#ifndef AFL_PATH + #define AFL_PATH "/usr/local/lib/afl/" +#endif + +void detect_file_args(char **argv, u8 *prog_in, bool *use_stdin) { u32 i = 0; u8 cwd[PATH_MAX]; @@ -63,7 +68,7 @@ void detect_file_args(char **argv, u8 *prog_in, u8 *use_stdin) { if (!prog_in) { FATAL("@@ syntax is not supported by this tool."); } - *use_stdin = 0; + *use_stdin = false; if (prog_in[0] != 0) { // not afl-showmap special case @@ -108,6 +113,7 @@ char **argv_cpy_dup(int argc, char **argv) { int i = 0; char **ret = ck_alloc((argc + 1) * sizeof(char *)); + if (unlikely(!ret)) { FATAL("Amount of arguments specified is too high"); } for (i = 0; i < argc; i++) { @@ -130,6 +136,7 @@ void argv_cpy_free(char **argv) { while (argv[i]) { ck_free(argv[i]); + argv[i] = NULL; i++; } @@ -142,10 +149,24 @@ void argv_cpy_free(char **argv) { char **get_qemu_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) { + if (unlikely(getenv("AFL_QEMU_CUSTOM_BIN"))) { + + WARNF( + "AFL_QEMU_CUSTOM_BIN is enabled. " + "You must run your target under afl-qemu-trace on your own!"); + return argv; + + } + + if (!unlikely(own_loc)) { FATAL("BUG: param own_loc is NULL"); } + + u8 *tmp, *cp = NULL, *rsl, *own_copy; + char **new_argv = ck_alloc(sizeof(char *) * (argc + 4)); - u8 * tmp, *cp = NULL, *rsl, *own_copy; + if (unlikely(!new_argv)) { FATAL("Illegal amount of arguments specified"); } - memcpy(new_argv + 3, argv + 1, (int)(sizeof(char *)) * argc); + memcpy(&new_argv[3], &argv[1], (int)(sizeof(char *)) * (argc - 1)); + new_argv[argc + 3] = NULL; new_argv[2] = *target_path_p; new_argv[1] = "--"; @@ -223,10 +244,15 @@ char **get_qemu_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) { char **get_wine_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) { + if (!unlikely(own_loc)) { FATAL("BUG: param own_loc is NULL"); } + + u8 *tmp, *cp = NULL, *rsl, *own_copy; + char **new_argv = ck_alloc(sizeof(char *) * (argc + 3)); - u8 * tmp, *cp = NULL, *rsl, *own_copy; + if (unlikely(!new_argv)) { FATAL("Illegal amount of arguments specified"); } - memcpy(new_argv + 2, argv + 1, (int)(sizeof(char *)) * argc); + memcpy(&new_argv[2], &argv[1], (int)(sizeof(char *)) * (argc - 1)); + new_argv[argc + 2] = NULL; new_argv[1] = *target_path_p; @@ -333,6 +359,8 @@ u8 *find_binary(u8 *fname) { struct stat st; + if (unlikely(!fname)) { FATAL("No binary supplied"); } + if (strchr(fname, '/') || !(env_path = getenv("PATH"))) { target_path = ck_strdup(fname); @@ -340,7 +368,7 @@ u8 *find_binary(u8 *fname) { if (stat(target_path, &st) || !S_ISREG(st.st_mode) || !(st.st_mode & 0111) || st.st_size < 4) { - free(target_path); + ck_free(target_path); FATAL("Program '%s' not found or not executable", fname); } @@ -354,6 +382,14 @@ u8 *find_binary(u8 *fname) { if (delim) { cur_elem = ck_alloc(delim - env_path + 1); + if (unlikely(!cur_elem)) { + + FATAL( + "Unexpected overflow when processing ENV. This should never " + "happend."); + + } + memcpy(cur_elem, env_path, delim - env_path); delim++; @@ -401,15 +437,186 @@ u8 *find_binary(u8 *fname) { } +/* Parses the kill signal environment variable, FATALs on error. + If the env is not set, sets the env to default_signal for the signal handlers + and returns the default_signal. */ +int parse_afl_kill_signal_env(u8 *afl_kill_signal_env, int default_signal) { + + if (afl_kill_signal_env && afl_kill_signal_env[0]) { + + char *endptr; + u8 signal_code; + signal_code = (u8)strtoul(afl_kill_signal_env, &endptr, 10); + /* Did we manage to parse the full string? */ + if (*endptr != '\0' || endptr == (char *)afl_kill_signal_env) { + + FATAL("Invalid AFL_KILL_SIGNAL: %s (expected unsigned int)", + afl_kill_signal_env); + + } + + return signal_code; + + } else { + + char *sigstr = alloc_printf("%d", default_signal); + if (!sigstr) { FATAL("Failed to alloc mem for signal buf"); } + + /* Set the env for signal handler */ + setenv("AFL_KILL_SIGNAL", sigstr, 1); + free(sigstr); + return default_signal; + + } + +} + +static inline unsigned int helper_min3(unsigned int a, unsigned int b, + unsigned int c) { + + return a < b ? (a < c ? a : c) : (b < c ? b : c); + +} + +// from +// https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#C +static int string_distance_levenshtein(char *s1, char *s2) { + + unsigned int s1len, s2len, x, y, lastdiag, olddiag; + s1len = strlen(s1); + s2len = strlen(s2); + unsigned int column[s1len + 1]; + column[s1len] = 1; + + for (y = 1; y <= s1len; y++) + column[y] = y; + for (x = 1; x <= s2len; x++) { + + column[0] = x; + for (y = 1, lastdiag = x - 1; y <= s1len; y++) { + + olddiag = column[y]; + column[y] = helper_min3(column[y] + 1, column[y - 1] + 1, + lastdiag + (s1[y - 1] == s2[x - 1] ? 0 : 1)); + lastdiag = olddiag; + + } + + } + + return column[s1len]; + +} + +#define ENV_SIMILARITY_TRESHOLD 3 + +void print_suggested_envs(char *mispelled_env) { + + size_t env_name_len = + strcspn(mispelled_env, "=") - 4; // remove the AFL_prefix + char *env_name = ck_alloc(env_name_len + 1); + memcpy(env_name, mispelled_env + 4, env_name_len); + + char *seen = ck_alloc(sizeof(afl_environment_variables) / sizeof(char *)); + int found = 0; + + int j; + for (j = 0; afl_environment_variables[j] != NULL; ++j) { + + char *afl_env = afl_environment_variables[j] + 4; + int distance = string_distance_levenshtein(afl_env, env_name); + if (distance < ENV_SIMILARITY_TRESHOLD && seen[j] == 0) { + + SAYF("Did you mean %s?\n", afl_environment_variables[j]); + seen[j] = 1; + found = 1; + + } + + } + + if (found) goto cleanup; + + for (j = 0; afl_environment_variables[j] != NULL; ++j) { + + char * afl_env = afl_environment_variables[j] + 4; + size_t afl_env_len = strlen(afl_env); + char * reduced = ck_alloc(afl_env_len + 1); + + size_t start = 0; + while (start < afl_env_len) { + + size_t end = start + strcspn(afl_env + start, "_") + 1; + memcpy(reduced, afl_env, start); + if (end < afl_env_len) + memcpy(reduced + start, afl_env + end, afl_env_len - end); + reduced[afl_env_len - end + start] = 0; + + int distance = string_distance_levenshtein(reduced, env_name); + if (distance < ENV_SIMILARITY_TRESHOLD && seen[j] == 0) { + + SAYF("Did you mean %s?\n", afl_environment_variables[j]); + seen[j] = 1; + found = 1; + + } + + start = end; + + }; + + ck_free(reduced); + + } + + if (found) goto cleanup; + + char * reduced = ck_alloc(env_name_len + 1); + size_t start = 0; + while (start < env_name_len) { + + size_t end = start + strcspn(env_name + start, "_") + 1; + memcpy(reduced, env_name, start); + if (end < env_name_len) + memcpy(reduced + start, env_name + end, env_name_len - end); + reduced[env_name_len - end + start] = 0; + + for (j = 0; afl_environment_variables[j] != NULL; ++j) { + + int distance = string_distance_levenshtein( + afl_environment_variables[j] + 4, reduced); + if (distance < ENV_SIMILARITY_TRESHOLD && seen[j] == 0) { + + SAYF("Did you mean %s?\n", afl_environment_variables[j]); + seen[j] = 1; + + } + + } + + start = end; + + }; + + ck_free(reduced); + +cleanup: + ck_free(env_name); + ck_free(seen); + +} + void check_environment_vars(char **envp) { if (be_quiet) { return; } int index = 0, issue_detected = 0; - char *env, *val; + char *env, *val, *ignore = getenv("AFL_IGNORE_UNKNOWN_ENVS"); while ((env = envp[index++]) != NULL) { - if (strncmp(env, "ALF_", 4) == 0) { + if (strncmp(env, "ALF_", 4) == 0 || strncmp(env, "_ALF", 4) == 0 || + strncmp(env, "__ALF", 5) == 0 || strncmp(env, "_AFL", 4) == 0 || + strncmp(env, "__AFL", 5) == 0) { WARNF("Potentially mistyped AFL environment variable: %s", env); issue_detected = 1; @@ -424,6 +631,7 @@ void check_environment_vars(char **envp) { env[strlen(afl_environment_variables[i])] == '=') { match = 1; + if ((val = getenv(afl_environment_variables[i])) && !*val) { WARNF( @@ -463,11 +671,13 @@ void check_environment_vars(char **envp) { } - if (match == 0) { + if (match == 0 && !ignore) { WARNF("Mistyped AFL environment variable: %s", env); issue_detected = 1; + print_suggested_envs(env); + } } @@ -605,6 +815,10 @@ u8 *stringify_float(u8 *buf, size_t len, double val) { snprintf(buf, len, "%0.01f", val); + } else if (unlikely(isnan(val) || isinf(val))) { + + strcpy(buf, "inf"); + } else { stringify_int(buf, len, (u64)val); @@ -667,16 +881,16 @@ u8 *stringify_mem_size(u8 *buf, size_t len, u64 val) { u8 *stringify_time_diff(u8 *buf, size_t len, u64 cur_ms, u64 event_ms) { - u64 delta; - s32 t_d, t_h, t_m, t_s; - u8 val_buf[STRINGIFY_VAL_SIZE_MAX]; - if (!event_ms) { snprintf(buf, len, "none seen yet"); } else { + u64 delta; + s32 t_d, t_h, t_m, t_s; + u8 val_buf[STRINGIFY_VAL_SIZE_MAX]; + delta = cur_ms - event_ms; t_d = delta / 1000 / 60 / 60 / 24; @@ -764,6 +978,10 @@ u8 *u_stringify_float(u8 *buf, double val) { sprintf(buf, "%0.01f", val); + } else if (unlikely(isnan(val) || isinf(val))) { + + strcpy(buf, "infinite"); + } else { return u_stringify_int(buf, (u64)val); @@ -825,16 +1043,16 @@ u8 *u_stringify_mem_size(u8 *buf, u64 val) { u8 *u_stringify_time_diff(u8 *buf, u64 cur_ms, u64 event_ms) { - u64 delta; - s32 t_d, t_h, t_m, t_s; - u8 val_buf[STRINGIFY_VAL_SIZE_MAX]; - if (!event_ms) { sprintf(buf, "none seen yet"); } else { + u64 delta; + s32 t_d, t_h, t_m, t_s; + u8 val_buf[STRINGIFY_VAL_SIZE_MAX]; + delta = cur_ms - event_ms; t_d = delta / 1000 / 60 / 60 / 24; @@ -854,20 +1072,20 @@ u8 *u_stringify_time_diff(u8 *buf, u64 cur_ms, u64 event_ms) { /* Reads the map size from ENV */ u32 get_map_size(void) { - uint32_t map_size = MAP_SIZE; + uint32_t map_size = DEFAULT_SHMEM_SIZE; char * ptr; if ((ptr = getenv("AFL_MAP_SIZE")) || (ptr = getenv("AFL_MAPSIZE"))) { map_size = atoi(ptr); - if (map_size < 8 || map_size > (1 << 29)) { + if (!map_size || map_size > (1 << 29)) { - FATAL("illegal AFL_MAP_SIZE %u, must be between %u and %u", map_size, 8, - 1 << 29); + FATAL("illegal AFL_MAP_SIZE %u, must be between %u and %u", map_size, 64U, + 1U << 29); } - if (map_size % 8) { map_size = (((map_size >> 3) + 1) << 3); } + if (map_size % 64) { map_size = (((map_size >> 6) + 1) << 6); } } @@ -875,3 +1093,36 @@ u32 get_map_size(void) { } +/* Create a stream file */ + +FILE *create_ffile(u8 *fn) { + + s32 fd; + FILE *f; + + fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600); + + if (fd < 0) { PFATAL("Unable to create '%s'", fn); } + + f = fdopen(fd, "w"); + + if (!f) { PFATAL("fdopen() failed"); } + + return f; + +} + +/* Create a file */ + +s32 create_file(u8 *fn) { + + s32 fd; + + fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600); + + if (fd < 0) { PFATAL("Unable to create '%s'", fn); } + + return fd; + +} + diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 47493eba..68995388 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -58,8 +58,12 @@ static list_t fsrv_list = {.element_prealloc_count = 0}; static void fsrv_exec_child(afl_forkserver_t *fsrv, char **argv) { + if (fsrv->qemu_mode) { setenv("AFL_DISABLE_LLVM_INSTRUMENTATION", "1", 0); } + execv(fsrv->target_path, argv); + WARNF("Execv failed in forkserver."); + } /* Initializes the struct */ @@ -74,20 +78,24 @@ void afl_fsrv_init(afl_forkserver_t *fsrv) { fsrv->dev_urandom_fd = -1; /* Settings */ - fsrv->use_stdin = 1; - fsrv->no_unlink = 0; + fsrv->use_stdin = true; + fsrv->no_unlink = false; fsrv->exec_tmout = EXEC_TIMEOUT; + fsrv->init_tmout = EXEC_TIMEOUT * FORK_WAIT_MULT; fsrv->mem_limit = MEM_LIMIT; fsrv->out_file = NULL; + fsrv->kill_signal = SIGKILL; /* exec related stuff */ fsrv->child_pid = -1; fsrv->map_size = get_map_size(); - fsrv->use_fauxsrv = 0; - fsrv->last_run_timed_out = 0; + fsrv->use_fauxsrv = false; + fsrv->last_run_timed_out = false; + fsrv->debug = false; + fsrv->uses_crash_exitcode = false; + fsrv->uses_asan = false; fsrv->init_child_func = fsrv_exec_child; - list_append(&fsrv_list, fsrv); } @@ -96,23 +104,29 @@ void afl_fsrv_init(afl_forkserver_t *fsrv) { void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from) { fsrv_to->use_stdin = from->use_stdin; - fsrv_to->out_fd = from->out_fd; fsrv_to->dev_null_fd = from->dev_null_fd; fsrv_to->exec_tmout = from->exec_tmout; + fsrv_to->init_tmout = from->init_tmout; fsrv_to->mem_limit = from->mem_limit; fsrv_to->map_size = from->map_size; fsrv_to->support_shmem_fuzz = from->support_shmem_fuzz; - + fsrv_to->out_file = from->out_file; fsrv_to->dev_urandom_fd = from->dev_urandom_fd; + fsrv_to->out_fd = from->out_fd; // not sure this is a good idea + fsrv_to->no_unlink = from->no_unlink; + fsrv_to->uses_crash_exitcode = from->uses_crash_exitcode; + fsrv_to->crash_exitcode = from->crash_exitcode; + fsrv_to->kill_signal = from->kill_signal; + fsrv_to->debug = from->debug; // These are forkserver specific. fsrv_to->out_dir_fd = -1; fsrv_to->child_pid = -1; fsrv_to->use_fauxsrv = 0; fsrv_to->last_run_timed_out = 0; - fsrv_to->out_file = NULL; - fsrv_to->init_child_func = fsrv_exec_child; + fsrv_to->init_child_func = from->init_child_func; + // Note: do not copy ->add_extra_func list_append(&fsrv_list, fsrv_to); @@ -122,8 +136,8 @@ void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from) { Returns the time passed to read. If the wait times out, returns timeout_ms + 1; Returns 0 if an error occurred (fd closed, signal, ...); */ -static u32 read_s32_timed(s32 fd, s32 *buf, u32 timeout_ms, - volatile u8 *stop_soon_p) { +static u32 __attribute__((hot)) +read_s32_timed(s32 fd, s32 *buf, u32 timeout_ms, volatile u8 *stop_soon_p) { fd_set readfds; FD_ZERO(&readfds); @@ -135,7 +149,7 @@ static u32 read_s32_timed(s32 fd, s32 *buf, u32 timeout_ms, timeout.tv_sec = (timeout_ms / 1000); timeout.tv_usec = (timeout_ms % 1000) * 1000; #if !defined(__linux__) - u64 read_start = get_cur_time_us(); + u32 read_start = get_cur_time_us(); #endif /* set exceptfds as well to return when a child exited/closed the pipe. */ @@ -145,6 +159,13 @@ restart_select: if (likely(sret > 0)) { restart_read: + if (*stop_soon_p) { + + // Early return - the user wants to quit. + return 0; + + } + len_read = read(fd, (u8 *)buf, 4); if (likely(len_read == 4)) { // for speed we put this first @@ -154,7 +175,7 @@ restart_select: timeout_ms, ((u64)timeout_ms - (timeout.tv_sec * 1000 + timeout.tv_usec / 1000))); #else - u32 exec_ms = MIN(timeout_ms, get_cur_time_us() - read_start); + u32 exec_ms = MIN(timeout_ms, (get_cur_time_us() - read_start) / 1000); #endif // ensure to report 1 ms has passed (0 is an error) @@ -194,7 +215,7 @@ restart_select: static void afl_fauxsrv_execv(afl_forkserver_t *fsrv, char **argv) { unsigned char tmp[4] = {0, 0, 0, 0}; - pid_t child_pid = -1; + pid_t child_pid; if (!be_quiet) { ACTF("Using Fauxserver:"); } @@ -228,6 +249,23 @@ static void afl_fauxsrv_execv(afl_forkserver_t *fsrv, char **argv) { if (!child_pid) { // New child + close(fsrv->out_dir_fd); + close(fsrv->dev_null_fd); + close(fsrv->dev_urandom_fd); + + if (fsrv->plot_file != NULL) { + + fclose(fsrv->plot_file); + fsrv->plot_file = NULL; + + } + + // enable terminating on sigpipe in the childs + struct sigaction sa; + memset((char *)&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + sigaction(SIGPIPE, &sa, NULL); + signal(SIGCHLD, old_sigchld_handler); // FORKSRV_FD is for communication with AFL, we don't need it in the // child. @@ -243,7 +281,8 @@ static void afl_fauxsrv_execv(afl_forkserver_t *fsrv, char **argv) { *(u32 *)fsrv->trace_bits = EXEC_FAIL_SIG; - PFATAL("Execv failed in fauxserver."); + WARNF("Execv failed in fauxserver."); + break; } @@ -257,13 +296,13 @@ static void afl_fauxsrv_execv(afl_forkserver_t *fsrv, char **argv) { if (waitpid(child_pid, &status, 0) < 0) { // Zombie Child could not be collected. Scary! - PFATAL("Fauxserver could not determin child's exit code. "); + WARNF("Fauxserver could not determine child's exit code. "); } /* Relay wait status to AFL pipe, then loop back. */ - if (write(FORKSRV_FD + 1, &status, 4) != 4) { exit(0); } + if (write(FORKSRV_FD + 1, &status, 4) != 4) { exit(1); } } @@ -286,8 +325,8 @@ static void report_error_and_exit(int error) { FATAL( "the fuzzing target reports that hardcoded map address might be the " "reason the mmap of the shared memory failed. Solution: recompile " - "the target with either afl-clang-lto and the environment variable " - "AFL_LLVM_MAP_DYNAMIC set or recompile with afl-clang-fast."); + "the target with either afl-clang-lto and do not set " + "AFL_LLVM_MAP_ADDR or recompile with afl-clang-fast."); break; case FS_ERROR_SHM_OPEN: FATAL("the fuzzing target reports that the shm_open() call failed."); @@ -301,7 +340,7 @@ static void report_error_and_exit(int error) { "memory failed."); break; default: - FATAL("unknown error code %u from fuzzing target!", error); + FATAL("unknown error code %d from fuzzing target!", error); } @@ -318,15 +357,16 @@ static void report_error_and_exit(int error) { void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, volatile u8 *stop_soon_p, u8 debug_child_output) { - int st_pipe[2], ctl_pipe[2]; - s32 status; - s32 rlen; + int st_pipe[2], ctl_pipe[2]; + s32 status; + s32 rlen; + char *ignore_autodict = getenv("AFL_NO_AUTODICT"); if (!be_quiet) { ACTF("Spinning up the fork server..."); } if (fsrv->use_fauxsrv) { - /* TODO: Come up with sone nice way to initialize this all */ + /* TODO: Come up with some nice way to initialize this all */ if (fsrv->init_child_func != fsrv_exec_child) { @@ -349,11 +389,22 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, /* CHILD PROCESS */ + // enable terminating on sigpipe in the childs + struct sigaction sa; + memset((char *)&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + sigaction(SIGPIPE, &sa, NULL); + struct rlimit r; + if (!fsrv->cmplog_binary && fsrv->qemu_mode == false) { + + unsetenv(CMPLOG_SHM_ENV_VAR); // we do not want that in non-cmplog fsrv + + } + /* Umpf. On OpenBSD, the default fd limit for root users is set to soft 128. Let's try to fix that... */ - if (!getrlimit(RLIMIT_NOFILE, &r) && r.rlim_cur < FORKSRV_FD + 2) { r.rlim_cur = FORKSRV_FD + 2; @@ -420,47 +471,60 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, close(fsrv->dev_null_fd); close(fsrv->dev_urandom_fd); - if (fsrv->plot_file != NULL) { fclose(fsrv->plot_file); } + if (fsrv->plot_file != NULL) { + + fclose(fsrv->plot_file); + fsrv->plot_file = NULL; + + } /* This should improve performance a bit, since it stops the linker from doing extra work post-fork(). */ - if (!getenv("LD_BIND_LAZY")) { setenv("LD_BIND_NOW", "1", 0); } + if (!getenv("LD_BIND_LAZY")) { setenv("LD_BIND_NOW", "1", 1); } /* Set sane defaults for ASAN if nothing else specified. */ - setenv("ASAN_OPTIONS", - "abort_on_error=1:" - "detect_leaks=0:" - "malloc_context_size=0:" - "symbolize=0:" - "allocator_may_return_null=1:" - "handle_segv=0:" - "handle_sigbus=0:" - "handle_abort=0:" - "handle_sigfpe=0:" - "handle_sigill=0", - 0); + if (!getenv("ASAN_OPTIONS")) + setenv("ASAN_OPTIONS", + "abort_on_error=1:" + "detect_leaks=0:" + "malloc_context_size=0:" + "symbolize=0:" + "allocator_may_return_null=1:" + "detect_odr_violation=0:" + "handle_segv=0:" + "handle_sigbus=0:" + "handle_abort=0:" + "handle_sigfpe=0:" + "handle_sigill=0", + 1); /* Set sane defaults for UBSAN if nothing else specified. */ - setenv("UBSAN_OPTIONS", - "halt_on_error=1:" - "abort_on_error=1:" - "malloc_context_size=0:" - "allocator_may_return_null=1:" - "symbolize=0:" - "handle_segv=0:" - "handle_sigbus=0:" - "handle_abort=0:" - "handle_sigfpe=0:" - "handle_sigill=0", - 0); + if (!getenv("UBSAN_OPTIONS")) + setenv("UBSAN_OPTIONS", + "halt_on_error=1:" + "abort_on_error=1:" + "malloc_context_size=0:" + "allocator_may_return_null=1:" + "symbolize=0:" + "handle_segv=0:" + "handle_sigbus=0:" + "handle_abort=0:" + "handle_sigfpe=0:" + "handle_sigill=0", + 1); + + /* Envs for QASan */ + setenv("QASAN_MAX_CALL_STACK", "0", 0); + setenv("QASAN_SYMBOLIZE", "0", 0); /* MSAN is tricky, because it doesn't support abort_on_error=1 at this point. So, we do this in a very hacky way. */ - setenv("MSAN_OPTIONS", + if (!getenv("MSAN_OPTIONS")) + setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":" "symbolize=0:" "abort_on_error=1:" @@ -472,7 +536,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, "handle_abort=0:" "handle_sigfpe=0:" "handle_sigill=0", - 0); + 1); fsrv->init_child_func(fsrv, argv); @@ -480,8 +544,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, falling through. */ *(u32 *)fsrv->trace_bits = EXEC_FAIL_SIG; - fprintf(stderr, "Error: execv to target failed\n"); - exit(0); + FATAL("Error: execv to target failed\n"); } @@ -507,18 +570,17 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, rlen = 0; if (fsrv->exec_tmout) { - u32 time_ms = - read_s32_timed(fsrv->fsrv_st_fd, &status, - fsrv->exec_tmout * FORK_WAIT_MULT, stop_soon_p); + u32 time_ms = read_s32_timed(fsrv->fsrv_st_fd, &status, fsrv->init_tmout, + stop_soon_p); if (!time_ms) { - kill(fsrv->fsrv_pid, SIGKILL); + kill(fsrv->fsrv_pid, fsrv->kill_signal); - } else if (time_ms > fsrv->exec_tmout * FORK_WAIT_MULT) { + } else if (time_ms > fsrv->init_tmout) { fsrv->last_run_timed_out = 1; - kill(fsrv->fsrv_pid, SIGKILL); + kill(fsrv->fsrv_pid, fsrv->kill_signal); } else { @@ -568,7 +630,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, fsrv->use_shmem_fuzz = 1; if (!be_quiet) { ACTF("Using SHARED MEMORY FUZZING feature."); } - if ((status & FS_OPT_AUTODICT) == 0) { + if ((status & FS_OPT_AUTODICT) == 0 || ignore_autodict) { u32 send_status = (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ); if (write(fsrv->fsrv_ctl_fd, &send_status, 4) != 4) { @@ -595,11 +657,11 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, if (!fsrv->map_size) { fsrv->map_size = MAP_SIZE; } - if (unlikely(tmp_map_size % 8)) { + if (unlikely(tmp_map_size % 64)) { // should not happen WARNF("Target reported non-aligned map size of %u", tmp_map_size); - tmp_map_size = (((tmp_map_size + 8) >> 3) << 3); + tmp_map_size = (((tmp_map_size + 63) >> 6) << 6); } @@ -621,87 +683,104 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, if ((status & FS_OPT_AUTODICT) == FS_OPT_AUTODICT) { - if (fsrv->function_ptr == NULL || fsrv->function_opt == NULL) { + if (!ignore_autodict) { - // this is not afl-fuzz - we deny and return - if (fsrv->use_shmem_fuzz) - status = (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ); - else - status = (FS_OPT_ENABLED); - if (write(fsrv->fsrv_ctl_fd, &status, 4) != 4) { + if (fsrv->add_extra_func == NULL || fsrv->afl_ptr == NULL) { - FATAL("Writing to forkserver failed."); + // this is not afl-fuzz - or it is cmplog - we deny and return + if (fsrv->use_shmem_fuzz) { + + status = (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ); + + } else { + + status = (FS_OPT_ENABLED); + + } + + if (write(fsrv->fsrv_ctl_fd, &status, 4) != 4) { + + FATAL("Writing to forkserver failed."); + + } + + return; } - return; + if (!be_quiet) { ACTF("Using AUTODICT feature."); } - } + if (fsrv->use_shmem_fuzz) { - if (!be_quiet) { ACTF("Using AUTODICT feature."); } + status = (FS_OPT_ENABLED | FS_OPT_AUTODICT | FS_OPT_SHDMEM_FUZZ); - if (fsrv->use_shmem_fuzz) - status = (FS_OPT_ENABLED | FS_OPT_AUTODICT | FS_OPT_SHDMEM_FUZZ); - else - status = (FS_OPT_ENABLED | FS_OPT_AUTODICT); + } else { - if (write(fsrv->fsrv_ctl_fd, &status, 4) != 4) { + status = (FS_OPT_ENABLED | FS_OPT_AUTODICT); - FATAL("Writing to forkserver failed."); + } - } + if (write(fsrv->fsrv_ctl_fd, &status, 4) != 4) { - if (read(fsrv->fsrv_st_fd, &status, 4) != 4) { + FATAL("Writing to forkserver failed."); - FATAL("Reading from forkserver failed."); + } - } + if (read(fsrv->fsrv_st_fd, &status, 4) != 4) { - if (status < 2 || (u32)status > 0xffffff) { + FATAL("Reading from forkserver failed."); - FATAL("Dictionary has an illegal size: %d", status); + } - } + if (status < 2 || (u32)status > 0xffffff) { - u32 len = status, offset = 0, count = 0; - u8 *dict = ck_alloc(len); - if (dict == NULL) { + FATAL("Dictionary has an illegal size: %d", status); - FATAL("Could not allocate %u bytes of autodictionary memory", len); + } - } + u32 offset = 0, count = 0; + u32 len = status; + u8 *dict = ck_alloc(len); + if (dict == NULL) { - while (len != 0) { + FATAL("Could not allocate %u bytes of autodictionary memory", len); - rlen = read(fsrv->fsrv_st_fd, dict + offset, len); - if (rlen > 0) { + } - len -= rlen; - offset += rlen; + while (len != 0) { - } else { + rlen = read(fsrv->fsrv_st_fd, dict + offset, len); + if (rlen > 0) { + + len -= rlen; + offset += rlen; + + } else { - FATAL( - "Reading autodictionary fail at position %u with %u bytes " - "left.", - offset, len); + FATAL( + "Reading autodictionary fail at position %u with %u bytes " + "left.", + offset, len); + + } } - } + offset = 0; + while (offset < (u32)status && + (u8)dict[offset] + offset < (u32)status) { - offset = 0; - while (offset < status && (u8)dict[offset] + offset < status) { + fsrv->add_extra_func(fsrv->afl_ptr, dict + offset + 1, + (u8)dict[offset]); + offset += (1 + dict[offset]); + count++; - fsrv->function_ptr(fsrv->function_opt, dict + offset + 1, - (u8)dict[offset]); - offset += (1 + dict[offset]); - count++; + } - } + if (!be_quiet) { ACTF("Loaded %u autodictionary entries", count); } + ck_free(dict); - if (!be_quiet) { ACTF("Loaded %u autodictionary entries", count); } - ck_free(dict); + } } @@ -740,6 +819,12 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, "before receiving any input\n" " from the fuzzer! There are several probable explanations:\n\n" + " - The target binary requires a large map and crashes before " + "reporting.\n" + " Set a high value (e.g. AFL_MAP_SIZE=8000000) or use " + "AFL_DEBUG=1 to see the\n" + " message from the target binary\n\n" + " - The binary is just buggy and explodes entirely on its own. " "If so, you\n" " need to fix the underlying problem or find a better " @@ -761,6 +846,12 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, "before receiving any input\n" " from the fuzzer! There are several probable explanations:\n\n" + " - The target binary requires a large map and crashes before " + "reporting.\n" + " Set a high value (e.g. AFL_MAP_SIZE=8000000) or use " + "AFL_DEBUG=1 to see the\n" + " message from the target binary\n\n" + " - The current memory limit (%s) is too restrictive, causing " "the\n" " target to hit an OOM condition in the dynamic linker. Try " @@ -818,10 +909,12 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, } else if (!fsrv->mem_limit) { SAYF("\n" cLRD "[-] " cRST - "Hmm, looks like the target binary terminated before we could" - " complete a handshake with the injected code.\n" - "If the target was compiled with afl-clang-lto then recompiling with" - " AFL_LLVM_MAP_DYNAMIC might solve your problem.\n" + "Hmm, looks like the target binary terminated before we could complete" + " a\n" + "handshake with the injected code.\n" + "Most likely the target has a huge coverage map, retry with setting" + " the\n" + "environment variable AFL_MAP_SIZE=8000000\n" "Otherwise there is a horrible bug in the fuzzer.\n" "Poke <afl-users@googlegroups.com> for troubleshooting tips.\n"); @@ -837,6 +930,11 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, "explanations:\n\n" "%s" + + " - Most likely the target has a huge coverage map, retry with " + "setting the\n" + " environment variable AFL_MAP_SIZE=8000000\n\n" + " - The current memory limit (%s) is too restrictive, causing an " "OOM\n" " fault in the dynamic linker. This can be fixed with the -m " @@ -850,10 +948,10 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, " estimate the required amount of virtual memory for the " "binary.\n\n" - " - the target was compiled with afl-clang-lto and a constructor " + " - The target was compiled with afl-clang-lto and a constructor " "was\n" - " instrumented, recompiling with AFL_LLVM_MAP_DYNAMIC might solve " - "your\n" + " instrumented, recompiling without AFL_LLVM_MAP_ADDR might solve " + "your \n" " problem\n\n" " - Less likely, there is a horrible bug in the fuzzer. If other " @@ -875,23 +973,42 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, } -static void afl_fsrv_kill(afl_forkserver_t *fsrv) { +/* Stop the forkserver and child */ + +void afl_fsrv_kill(afl_forkserver_t *fsrv) { - if (fsrv->child_pid > 0) { kill(fsrv->child_pid, SIGKILL); } + if (fsrv->child_pid > 0) { kill(fsrv->child_pid, fsrv->kill_signal); } if (fsrv->fsrv_pid > 0) { - kill(fsrv->fsrv_pid, SIGKILL); + kill(fsrv->fsrv_pid, fsrv->kill_signal); if (waitpid(fsrv->fsrv_pid, NULL, 0) <= 0) { WARNF("error waitpid\n"); } } + close(fsrv->fsrv_ctl_fd); + close(fsrv->fsrv_st_fd); + fsrv->fsrv_pid = -1; + fsrv->child_pid = -1; + +} + +/* Get the map size from the target forkserver */ + +u32 afl_fsrv_get_mapsize(afl_forkserver_t *fsrv, char **argv, + volatile u8 *stop_soon_p, u8 debug_child_output) { + + afl_fsrv_start(fsrv, argv, stop_soon_p, debug_child_output); + return fsrv->map_size; + } /* Delete the current testcase and write the buf to the testcase file */ void afl_fsrv_write_to_testcase(afl_forkserver_t *fsrv, u8 *buf, size_t len) { - if (fsrv->shmem_fuzz) { + if (likely(fsrv->use_shmem_fuzz && fsrv->shmem_fuzz)) { + + if (unlikely(len > MAX_FILE)) len = MAX_FILE; *fsrv->shmem_fuzz_len = len; memcpy(fsrv->shmem_fuzz, buf, len); @@ -902,10 +1019,10 @@ void afl_fsrv_write_to_testcase(afl_forkserver_t *fsrv, u8 *buf, size_t len) { hash64(fsrv->shmem_fuzz, *fsrv->shmem_fuzz_len, 0xa5b35705), *fsrv->shmem_fuzz_len); fprintf(stderr, "SHM :"); - for (int i = 0; i < *fsrv->shmem_fuzz_len; i++) + for (u32 i = 0; i < *fsrv->shmem_fuzz_len; i++) fprintf(stderr, "%02x", fsrv->shmem_fuzz[i]); fprintf(stderr, "\nORIG:"); - for (int i = 0; i < *fsrv->shmem_fuzz_len; i++) + for (u32 i = 0; i < *fsrv->shmem_fuzz_len; i++) fprintf(stderr, "%02x", buf[i]); fprintf(stderr, "\n"); @@ -917,9 +1034,9 @@ void afl_fsrv_write_to_testcase(afl_forkserver_t *fsrv, u8 *buf, size_t len) { s32 fd = fsrv->out_fd; - if (fsrv->out_file) { + if (!fsrv->use_stdin && fsrv->out_file) { - if (fsrv->no_unlink) { + if (unlikely(fsrv->no_unlink)) { fd = open(fsrv->out_file, O_WRONLY | O_CREAT | O_TRUNC, 0600); @@ -932,15 +1049,24 @@ void afl_fsrv_write_to_testcase(afl_forkserver_t *fsrv, u8 *buf, size_t len) { if (fd < 0) { PFATAL("Unable to create '%s'", fsrv->out_file); } + } else if (unlikely(fd <= 0)) { + + // We should have a (non-stdin) fd at this point, else we got a problem. + FATAL( + "Nowhere to write output to (neither out_fd nor out_file set (fd is " + "%d))", + fd); + } else { lseek(fd, 0, SEEK_SET); } + // fprintf(stderr, "WRITE %d %u\n", fd, len); ck_write(fd, buf, len, fsrv->out_file); - if (!fsrv->out_file) { + if (fsrv->use_stdin) { if (ftruncate(fd, len)) { PFATAL("ftruncate() failed"); } lseek(fd, 0, SEEK_SET); @@ -992,7 +1118,19 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout, } - if (fsrv->child_pid <= 0) { FATAL("Fork server is misbehaving (OOM?)"); } + if (fsrv->child_pid <= 0) { + + if (*stop_soon_p) { return 0; } + + if ((fsrv->child_pid & FS_OPT_ERROR) && + FS_OPT_GET_ERROR(fsrv->child_pid) == FS_ERROR_SHM_OPEN) + FATAL( + "Target reported shared memory access failed (perhaps increase " + "shared memory available)."); + + FATAL("Fork server is misbehaving (OOM?)"); + + } exec_ms = read_s32_timed(fsrv->fsrv_st_fd, &fsrv->child_status, timeout, stop_soon_p); @@ -1002,7 +1140,7 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout, /* If there was no response from forkserver after timeout seconds, we kill the child. The forkserver should inform us afterwards */ - kill(fsrv->child_pid, SIGKILL); + kill(fsrv->child_pid, fsrv->kill_signal); fsrv->last_run_timed_out = 1; if (read(fsrv->fsrv_st_fd, &fsrv->child_status, 4) < 4) { exec_ms = 0; } @@ -1015,7 +1153,7 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout, "Unable to communicate with fork server. Some possible reasons:\n\n" " - You've run out of memory. Use -m to increase the the memory " "limit\n" - " to something higher than %lld.\n" + " to something higher than %llu.\n" " - The binary or one of the libraries it uses manages to " "create\n" " threads before the forkserver initializes.\n" @@ -1048,33 +1186,44 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout, /* Report outcome to caller. */ - if (WIFSIGNALED(fsrv->child_status) && !*stop_soon_p) { - - fsrv->last_kill_signal = WTERMSIG(fsrv->child_status); + /* Was the run unsuccessful? */ + if (unlikely(*(u32 *)fsrv->trace_bits == EXEC_FAIL_SIG)) { - if (fsrv->last_run_timed_out && fsrv->last_kill_signal == SIGKILL) { + return FSRV_RUN_ERROR; - return FSRV_RUN_TMOUT; + } - } + /* Did we timeout? */ + if (unlikely(fsrv->last_run_timed_out)) { - return FSRV_RUN_CRASH; + fsrv->last_kill_signal = fsrv->kill_signal; + return FSRV_RUN_TMOUT; } - /* A somewhat nasty hack for MSAN, which doesn't support abort_on_error and - must use a special exit code. */ - - if (fsrv->uses_asan && WEXITSTATUS(fsrv->child_status) == MSAN_ERROR) { - - fsrv->last_kill_signal = 0; + /* Did we crash? + In a normal case, (abort) WIFSIGNALED(child_status) will be set. + MSAN in uses_asan mode uses a special exit code as it doesn't support + abort_on_error. On top, a user may specify a custom AFL_CRASH_EXITCODE. + Handle all three cases here. */ + + if (unlikely( + /* A normal crash/abort */ + (WIFSIGNALED(fsrv->child_status)) || + /* special handling for msan */ + (fsrv->uses_asan && WEXITSTATUS(fsrv->child_status) == MSAN_ERROR) || + /* the custom crash_exitcode was returned by the target */ + (fsrv->uses_crash_exitcode && + WEXITSTATUS(fsrv->child_status) == fsrv->crash_exitcode))) { + + /* For a proper crash, set last_kill_signal to WTERMSIG, else set it to 0 */ + fsrv->last_kill_signal = + WIFSIGNALED(fsrv->child_status) ? WTERMSIG(fsrv->child_status) : 0; return FSRV_RUN_CRASH; } - // Fauxserver should handle this now. - // if (tb4 == EXEC_FAIL_SIG) return FSRV_RUN_ERROR; - + /* success :) */ return FSRV_RUN_OK; } diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index aa8d5a18..4ed59364 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -25,6 +25,9 @@ #include "afl-fuzz.h" #include <limits.h> +#if !defined NAME_MAX + #define NAME_MAX _XOPEN_NAME_MAX +#endif /* Write bitmap to file. The bitmap is useful mostly for the secret -B option, to focus a separate fuzzing session on a particular @@ -49,101 +52,6 @@ void write_bitmap(afl_state_t *afl) { } -/* Check if the current execution path brings anything new to the table. - Update virgin bits to reflect the finds. Returns 1 if the only change is - the hit-count for a particular tuple; 2 if there are new tuples seen. - Updates the map, 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. */ - -u8 has_new_bits(afl_state_t *afl, u8 *virgin_map) { - -#ifdef WORD_SIZE_64 - - u64 *current = (u64 *)afl->fsrv.trace_bits; - u64 *virgin = (u64 *)virgin_map; - - u32 i = (afl->fsrv.map_size >> 3); - -#else - - u32 *current = (u32 *)afl->fsrv.trace_bits; - u32 *virgin = (u32 *)virgin_map; - - u32 i = (afl->fsrv.map_size >> 2); - -#endif /* ^WORD_SIZE_64 */ - // the map size must be a minimum of 8 bytes. - // for variable/dynamic map sizes this is ensured in the forkserver - - u8 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. */ - - // the (*current) is unnecessary but speeds up the overall comparison - if (unlikely(*current) && unlikely(*current & *virgin)) { - - if (likely(ret < 2)) { - - u8 *cur = (u8 *)current; - u8 *vir = (u8 *)virgin; - - /* Looks like we have not found any new bytes yet; see if any non-zero - bytes in current[] are pristine in virgin[]. */ - -#ifdef WORD_SIZE_64 - - if (*virgin == 0xffffffffffffffff || (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 (*virgin == 0xffffffff || (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 /* ^WORD_SIZE_64 */ - - } - - *virgin &= ~*current; - - } - - ++current; - ++virgin; - - } - - if (unlikely(ret) && likely(virgin_map == afl->virgin_bits)) { - - afl->bitmap_changed = 1; - - } - - return ret; - -} - /* Count the number of bits set in the provided bitmap. Used for the status screen several times every second, does not have to be fast. */ @@ -192,10 +100,10 @@ u32 count_bytes(afl_state_t *afl, u8 *mem) { u32 v = *(ptr++); if (!v) { continue; } - if (v & 0x000000ff) { ++ret; } - if (v & 0x0000ff00) { ++ret; } - if (v & 0x00ff0000) { ++ret; } - if (v & 0xff000000) { ++ret; } + if (v & 0x000000ffU) { ++ret; } + if (v & 0x0000ff00U) { ++ret; } + if (v & 0x00ff0000U) { ++ret; } + if (v & 0xff000000U) { ++ret; } } @@ -219,11 +127,11 @@ u32 count_non_255_bytes(afl_state_t *afl, u8 *mem) { /* This is called on the virgin bitmap, so optimize for the most likely case. */ - if (v == 0xffffffff) { continue; } - if ((v & 0x000000ff) != 0x000000ff) { ++ret; } - if ((v & 0x0000ff00) != 0x0000ff00) { ++ret; } - if ((v & 0x00ff0000) != 0x00ff0000) { ++ret; } - if ((v & 0xff000000) != 0xff000000) { ++ret; } + if (v == 0xffffffffU) { continue; } + if ((v & 0x000000ffU) != 0x000000ffU) { ++ret; } + if ((v & 0x0000ff00U) != 0x0000ff00U) { ++ret; } + if ((v & 0x00ff0000U) != 0x00ff0000U) { ++ret; } + if ((v & 0xff000000U) != 0xff000000U) { ++ret; } } @@ -235,98 +143,46 @@ u32 count_non_255_bytes(afl_state_t *afl, u8 *mem) { 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. */ - +#define TIMES4(x) x, x, x, x +#define TIMES8(x) TIMES4(x), TIMES4(x) +#define TIMES16(x) TIMES8(x), TIMES8(x) +#define TIMES32(x) TIMES16(x), TIMES16(x) +#define TIMES64(x) TIMES32(x), TIMES32(x) +#define TIMES255(x) \ + TIMES64(x), TIMES64(x), TIMES64(x), TIMES32(x), TIMES16(x), TIMES8(x), \ + TIMES4(x), x, x, x const u8 simplify_lookup[256] = { - [0] = 1, [1 ... 255] = 128 + [0] = 1, [1] = TIMES255(128) }; -#ifdef WORD_SIZE_64 - -void simplify_trace(afl_state_t *afl, u64 *mem) { - - u32 i = (afl->fsrv.map_size >> 3); - - while (i--) { - - /* Optimize for sparse bitmaps. */ - - if (unlikely(*mem)) { - - u8 *mem8 = (u8 *)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 - -void simplify_trace(afl_state_t *afl, u32 *mem) { - - u32 i = (afl->fsrv.map_size >> 2); - - while (i--) { - - /* Optimize for sparse bitmaps. */ - - if (unlikely(*mem)) { - - u8 *mem8 = (u8 *)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 /* ^WORD_SIZE_64 */ - /* Destructively classify execution counts in a trace. This is used as a preprocessing step for any newly acquired traces. Called on every exec, must be fast. */ -static const u8 count_class_lookup8[256] = { +const u8 count_class_lookup8[256] = { [0] = 0, [1] = 1, [2] = 2, [3] = 4, - [4 ... 7] = 8, - [8 ... 15] = 16, - [16 ... 31] = 32, - [32 ... 127] = 64, - [128 ... 255] = 128 + [4] = TIMES4(8), + [8] = TIMES8(16), + [16] = TIMES16(32), + [32] = TIMES32(64), + [128] = TIMES64(128) }; -static u16 count_class_lookup16[65536]; +#undef TIMES255 +#undef TIMES64 +#undef TIMES32 +#undef TIMES16 +#undef TIMES8 +#undef TIMES4 + +u16 count_class_lookup16[65536]; void init_count_class16(void) { @@ -345,63 +201,87 @@ void init_count_class16(void) { } -#ifdef WORD_SIZE_64 +/* Import coverage processing routines. */ -void classify_counts(afl_forkserver_t *fsrv) { +#ifdef WORD_SIZE_64 + #include "coverage-64.h" +#else + #include "coverage-32.h" +#endif - u64 *mem = (u64 *)fsrv->trace_bits; +/* Check if the current execution path brings anything new to the table. + Update virgin bits to reflect the finds. Returns 1 if the only change is + the hit-count for a particular tuple; 2 if there are new tuples seen. + Updates the map, so subsequent calls will always return 0. - u32 i = (fsrv->map_size >> 3); + 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. */ - while (i--) { +inline u8 has_new_bits(afl_state_t *afl, u8 *virgin_map) { - /* Optimize for sparse bitmaps. */ +#ifdef WORD_SIZE_64 - if (unlikely(*mem)) { + u64 *current = (u64 *)afl->fsrv.trace_bits; + u64 *virgin = (u64 *)virgin_map; - u16 *mem16 = (u16 *)mem; + u32 i = (afl->fsrv.map_size >> 3); - mem16[0] = count_class_lookup16[mem16[0]]; - mem16[1] = count_class_lookup16[mem16[1]]; - mem16[2] = count_class_lookup16[mem16[2]]; - mem16[3] = count_class_lookup16[mem16[3]]; +#else - } + u32 *current = (u32 *)afl->fsrv.trace_bits; + u32 *virgin = (u32 *)virgin_map; - ++mem; + u32 i = (afl->fsrv.map_size >> 2); - } +#endif /* ^WORD_SIZE_64 */ -} + u8 ret = 0; + while (i--) { -#else + if (unlikely(*current)) discover_word(&ret, current, virgin); -void classify_counts(afl_forkserver_t *fsrv) { + current++; + virgin++; - u32 *mem = (u32 *)fsrv->trace_bits; + } - u32 i = (fsrv->map_size >> 2); + if (unlikely(ret) && likely(virgin_map == afl->virgin_bits)) + afl->bitmap_changed = 1; - while (i--) { + return ret; - /* Optimize for sparse bitmaps. */ +} - if (unlikely(*mem)) { +/* A combination of classify_counts and has_new_bits. If 0 is returned, then the + * trace bits are kept as-is. Otherwise, the trace bits are overwritten with + * classified values. + * + * This accelerates the processing: in most cases, no interesting behavior + * happen, and the trace bits will be discarded soon. This function optimizes + * for such cases: one-pass scan on trace bits without modifying anything. Only + * on rare cases it fall backs to the slow path: classify_counts() first, then + * return has_new_bits(). */ - u16 *mem16 = (u16 *)mem; +inline u8 has_new_bits_unclassified(afl_state_t *afl, u8 *virgin_map) { - mem16[0] = count_class_lookup16[mem16[0]]; - mem16[1] = count_class_lookup16[mem16[1]]; + /* Handle the hot path first: no new coverage */ + u8 *end = afl->fsrv.trace_bits + afl->fsrv.map_size; - } +#ifdef WORD_SIZE_64 - ++mem; + if (!skim((u64 *)virgin_map, (u64 *)afl->fsrv.trace_bits, (u64 *)end)) + return 0; - } +#else -} + if (!skim((u32 *)virgin_map, (u32 *)afl->fsrv.trace_bits, (u32 *)end)) + return 0; #endif /* ^WORD_SIZE_64 */ + classify_counts(&afl->fsrv); + return has_new_bits(afl, virgin_map); + +} /* Compact trace bytes into a smaller bitmap. We effectively just drop the count information here. This is called only sporadically, for some @@ -425,8 +305,10 @@ void minimize_bits(afl_state_t *afl, u8 *dst, u8 *src) { /* Construct a file name for a new test case, capturing the operation that led to its discovery. Returns a ptr to afl->describe_op_buf_256. */ -u8 *describe_op(afl_state_t *afl, u8 hnb) { +u8 *describe_op(afl_state_t *afl, u8 new_bits, size_t max_description_len) { + size_t real_max_len = + MIN(max_description_len, sizeof(afl->describe_op_buf_256)); u8 *ret = afl->describe_op_buf_256; if (unlikely(afl->syncing_party)) { @@ -443,31 +325,69 @@ u8 *describe_op(afl_state_t *afl, u8 hnb) { } - sprintf(ret + strlen(ret), ",time:%llu", get_cur_time() - afl->start_time); + sprintf(ret + strlen(ret), ",time:%llu", + get_cur_time() + afl->prev_run_time - afl->start_time); + + if (afl->current_custom_fuzz && + afl->current_custom_fuzz->afl_custom_describe) { - sprintf(ret + strlen(ret), ",op:%s", afl->stage_short); + /* We are currently in a custom mutator that supports afl_custom_describe, + * use it! */ - if (afl->stage_cur_byte >= 0) { + size_t len_current = strlen(ret); + ret[len_current++] = ','; + ret[len_current] = '\0'; - sprintf(ret + strlen(ret), ",pos:%d", afl->stage_cur_byte); + ssize_t size_left = real_max_len - len_current - strlen(",+cov") - 2; + if (unlikely(size_left <= 0)) FATAL("filename got too long"); - if (afl->stage_val_type != STAGE_VAL_NONE) { + const char *custom_description = + afl->current_custom_fuzz->afl_custom_describe( + afl->current_custom_fuzz->data, size_left); + if (!custom_description || !custom_description[0]) { - sprintf(ret + strlen(ret), ",val:%s%+d", - (afl->stage_val_type == STAGE_VAL_BE) ? "be:" : "", - afl->stage_cur_val); + DEBUGF("Error getting a description from afl_custom_describe"); + /* Take the stage name as description fallback */ + sprintf(ret + len_current, "op:%s", afl->stage_short); + + } else { + + /* We got a proper custom description, use it */ + strncat(ret + len_current, custom_description, size_left); } } else { - sprintf(ret + strlen(ret), ",rep:%d", afl->stage_cur_val); + /* Normal testcase descriptions start here */ + sprintf(ret + strlen(ret), ",op:%s", afl->stage_short); + + if (afl->stage_cur_byte >= 0) { + + sprintf(ret + strlen(ret), ",pos:%d", afl->stage_cur_byte); + + if (afl->stage_val_type != STAGE_VAL_NONE) { + + sprintf(ret + strlen(ret), ",val:%s%+d", + (afl->stage_val_type == STAGE_VAL_BE) ? "be:" : "", + afl->stage_cur_val); + + } + + } else { + + sprintf(ret + strlen(ret), ",rep:%d", afl->stage_cur_val); + + } } } - if (hnb == 2) { strcat(ret, ",+cov"); } + if (new_bits == 2) { strcat(ret, ",+cov"); } + + if (unlikely(strlen(ret) >= max_description_len)) + FATAL("describe string is too long"); return ret; @@ -534,14 +454,15 @@ static void write_crash_readme(afl_state_t *afl) { save or queue the input test case for further analysis if so. Returns 1 if entry is saved, 0 otherwise. */ -u8 save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { +u8 __attribute__((hot)) +save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { if (unlikely(len == 0)) { return 0; } u8 *queue_fn = ""; - u8 hnb = '\0'; + u8 new_bits = '\0'; s32 fd; - u8 keeping = 0, res; + u8 keeping = 0, res, classified = 0; u64 cksum = 0; u8 fn[PATH_MAX]; @@ -554,19 +475,9 @@ u8 save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); - struct queue_entry *q = afl->queue; - while (q) { - - if (q->exec_cksum == cksum) { - - ++q->n_fuzz; - break; - - } - - q = q->next; - - } + /* Saturated increment */ + if (afl->n_fuzz[cksum % N_FUZZ_SIZE] < 0xFFFFFFFF) + afl->n_fuzz[cksum % N_FUZZ_SIZE]++; } @@ -575,17 +486,22 @@ u8 save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { /* Keep only if there are new bits in the map, add to queue for future fuzzing, etc. */ - if (!(hnb = has_new_bits(afl, afl->virgin_bits))) { + new_bits = has_new_bits_unclassified(afl, afl->virgin_bits); + + if (likely(!new_bits)) { if (unlikely(afl->crash_mode)) { ++afl->total_crashes; } return 0; } + classified = new_bits; + #ifndef SIMPLE_FILES - queue_fn = alloc_printf("%s/queue/id:%06u,%s", afl->out_dir, - afl->queued_paths, describe_op(afl, hnb)); + queue_fn = alloc_printf( + "%s/queue/id:%06u,%s", afl->out_dir, afl->queued_paths, + describe_op(afl, new_bits, NAME_MAX - strlen("id:000000,"))); #else @@ -593,10 +509,42 @@ u8 save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { alloc_printf("%s/queue/id_%06u", afl->out_dir, afl->queued_paths); #endif /* ^!SIMPLE_FILES */ - + fd = open(queue_fn, O_WRONLY | O_CREAT | O_EXCL, 0600); + if (unlikely(fd < 0)) { PFATAL("Unable to create '%s'", queue_fn); } + ck_write(fd, mem, len, queue_fn); + close(fd); add_to_queue(afl, queue_fn, len, 0); - if (hnb == 2) { +#ifdef INTROSPECTION + if (afl->custom_mutators_count && afl->current_custom_fuzz) { + + LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { + + if (afl->current_custom_fuzz == el && el->afl_custom_introspection) { + + const char *ptr = el->afl_custom_introspection(el->data); + + if (ptr != NULL && *ptr != 0) { + + fprintf(afl->introspection_file, "QUEUE CUSTOM %s = %s\n", ptr, + afl->queue_top->fname); + + } + + } + + }); + + } else if (afl->mutation[0] != 0) { + + fprintf(afl->introspection_file, "QUEUE %s = %s\n", afl->mutation, + afl->queue_top->fname); + + } + +#endif + + if (new_bits == 2) { afl->queue_top->has_new_cov = 1; ++afl->queued_with_cov; @@ -606,9 +554,16 @@ u8 save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { if (cksum) afl->queue_top->exec_cksum = cksum; else - afl->queue_top->exec_cksum = + cksum = afl->queue_top->exec_cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); + if (afl->schedule >= FAST && afl->schedule <= RARE) { + + afl->queue_top->n_fuzz_entry = cksum % N_FUZZ_SIZE; + afl->n_fuzz[afl->queue_top->n_fuzz_entry] = 1; + + } + /* Try to calibrate inline; this also calls update_bitmap_score() when successful. */ @@ -620,10 +575,11 @@ u8 save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { } - fd = open(queue_fn, O_WRONLY | O_CREAT | O_EXCL, 0600); - if (unlikely(fd < 0)) { PFATAL("Unable to create '%s'", queue_fn); } - ck_write(fd, mem, len, queue_fn); - close(fd); + if (likely(afl->q_testcase_max_cache_size)) { + + queue_testcase_store_mem(afl, afl->queue_top, mem); + + } keeping = 1; @@ -644,17 +600,48 @@ u8 save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { if (likely(!afl->non_instrumented_mode)) { -#ifdef WORD_SIZE_64 - simplify_trace(afl, (u64 *)afl->fsrv.trace_bits); -#else - simplify_trace(afl, (u32 *)afl->fsrv.trace_bits); -#endif /* ^WORD_SIZE_64 */ + if (!classified) { + + classify_counts(&afl->fsrv); + classified = 1; + + } + + simplify_trace(afl, afl->fsrv.trace_bits); if (!has_new_bits(afl, afl->virgin_tmout)) { return keeping; } } ++afl->unique_tmouts; +#ifdef INTROSPECTION + if (afl->custom_mutators_count && afl->current_custom_fuzz) { + + LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { + + if (afl->current_custom_fuzz == el && el->afl_custom_introspection) { + + const char *ptr = el->afl_custom_introspection(el->data); + + if (ptr != NULL && *ptr != 0) { + + fprintf(afl->introspection_file, + "UNIQUE_TIMEOUT CUSTOM %s = %s\n", ptr, + afl->queue_top->fname); + + } + + } + + }); + + } else if (afl->mutation[0] != 0) { + + fprintf(afl->introspection_file, "UNIQUE_TIMEOUT %s\n", afl->mutation); + + } + +#endif /* Before saving, we make sure that it's a genuine hang by re-running the target with a more generous timeout (unless the default timeout @@ -665,6 +652,7 @@ u8 save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { u8 new_fault; write_to_testcase(afl, mem, len); new_fault = fuzz_run_target(afl, &afl->fsrv, afl->hang_tmout); + classify_counts(&afl->fsrv); /* A corner case that one user reported bumping into: increasing the timeout actually uncovers a crash. Make sure we don't discard it if @@ -683,7 +671,8 @@ u8 save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { #ifndef SIMPLE_FILES snprintf(fn, PATH_MAX, "%s/hangs/id:%06llu,%s", afl->out_dir, - afl->unique_hangs, describe_op(afl, 0)); + afl->unique_hangs, + describe_op(afl, 0, NAME_MAX - strlen("id:000000,"))); #else @@ -712,11 +701,9 @@ u8 save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { if (likely(!afl->non_instrumented_mode)) { -#ifdef WORD_SIZE_64 - simplify_trace(afl, (u64 *)afl->fsrv.trace_bits); -#else - simplify_trace(afl, (u32 *)afl->fsrv.trace_bits); -#endif /* ^WORD_SIZE_64 */ + if (!classified) { classify_counts(&afl->fsrv); } + + simplify_trace(afl, afl->fsrv.trace_bits); if (!has_new_bits(afl, afl->virgin_crash)) { return keeping; } @@ -728,7 +715,7 @@ u8 save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { snprintf(fn, PATH_MAX, "%s/crashes/id:%06llu,sig:%02u,%s", afl->out_dir, afl->unique_crashes, afl->fsrv.last_kill_signal, - describe_op(afl, 0)); + describe_op(afl, 0, NAME_MAX - strlen("id:000000,sig:00,"))); #else @@ -738,6 +725,33 @@ u8 save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { #endif /* ^!SIMPLE_FILES */ ++afl->unique_crashes; +#ifdef INTROSPECTION + if (afl->custom_mutators_count && afl->current_custom_fuzz) { + + LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { + + if (afl->current_custom_fuzz == el && el->afl_custom_introspection) { + + const char *ptr = el->afl_custom_introspection(el->data); + + if (ptr != NULL && *ptr != 0) { + + fprintf(afl->introspection_file, "UNIQUE_CRASH CUSTOM %s = %s\n", + ptr, afl->queue_top->fname); + + } + + } + + }); + + } else if (afl->mutation[0] != 0) { + + fprintf(afl->introspection_file, "UNIQUE_CRASH %s\n", afl->mutation); + + } + +#endif if (unlikely(afl->infoexec)) { // if the user wants to be informed on new crashes - do that diff --git a/src/afl-fuzz-cmplog.c b/src/afl-fuzz-cmplog.c index faf4dcb7..27c6c413 100644 --- a/src/afl-fuzz-cmplog.c +++ b/src/afl-fuzz-cmplog.c @@ -29,14 +29,12 @@ #include "afl-fuzz.h" #include "cmplog.h" -typedef struct cmplog_data { - -} cmplog_data_t; - void cmplog_exec_child(afl_forkserver_t *fsrv, char **argv) { setenv("___AFL_EINS_ZWEI_POLIZEI___", "1", 1); + if (fsrv->qemu_mode) { setenv("AFL_DISABLE_LLVM_INSTRUMENTATION", "1", 0); } + if (!fsrv->qemu_mode && argv[0] != fsrv->cmplog_binary) { argv[0] = fsrv->cmplog_binary; diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c index 12771cd7..52100fa1 100644 --- a/src/afl-fuzz-extras.c +++ b/src/afl-fuzz-extras.c @@ -25,23 +25,28 @@ #include "afl-fuzz.h" -/* Helper function for load_extras. */ +/* helper function for auto_extras qsort */ +static int compare_auto_extras_len(const void *ae1, const void *ae2) { + + return ((struct auto_extra_data *)ae1)->len - + ((struct auto_extra_data *)ae2)->len; -static int compare_extras_len(const void *p1, const void *p2) { +} - struct extra_data *e1 = (struct extra_data *)p1, - *e2 = (struct extra_data *)p2; +/* descending order */ - return e1->len - e2->len; +static int compare_auto_extras_use_d(const void *ae1, const void *ae2) { + + return ((struct auto_extra_data *)ae2)->hit_cnt - + ((struct auto_extra_data *)ae1)->hit_cnt; } -static int compare_extras_use_d(const void *p1, const void *p2) { +/* Helper function for load_extras. */ - struct extra_data *e1 = (struct extra_data *)p1, - *e2 = (struct extra_data *)p2; +static int compare_extras_len(const void *e1, const void *e2) { - return e2->hit_cnt - e1->hit_cnt; + return ((struct extra_data *)e1)->len - ((struct extra_data *)e2)->len; } @@ -96,7 +101,8 @@ void load_extras_file(afl_state_t *afl, u8 *fname, u32 *min_len, u32 *max_len, if (rptr < lptr || *rptr != '"') { - FATAL("Malformed name=\"value\" pair in line %u.", cur_line); + WARNF("Malformed name=\"value\" pair in line %u.", cur_line); + continue; } @@ -115,7 +121,7 @@ void load_extras_file(afl_state_t *afl, u8 *fname, u32 *min_len, u32 *max_len, if (*lptr == '@') { ++lptr; - if (atoi(lptr) > dict_level) { continue; } + if (atoi(lptr) > (s32)dict_level) { continue; } while (isdigit(*lptr)) { ++lptr; @@ -136,19 +142,27 @@ void load_extras_file(afl_state_t *afl, u8 *fname, u32 *min_len, u32 *max_len, if (*lptr != '"') { - FATAL("Malformed name=\"keyword\" pair in line %u.", cur_line); + WARNF("Malformed name=\"keyword\" pair in line %u.", cur_line); + continue; } ++lptr; - if (!*lptr) { FATAL("Empty keyword in line %u.", cur_line); } + if (!*lptr) { + + WARNF("Empty keyword in line %u.", cur_line); + continue; + + } /* Okay, let's allocate memory and copy data between "...", handling \xNN escaping, \\, and \". */ - afl->extras = ck_realloc_block( - afl->extras, (afl->extras_cnt + 1) * sizeof(struct extra_data)); + afl->extras = + afl_realloc((void **)&afl->extras, + (afl->extras_cnt + 1) * sizeof(struct extra_data)); + if (unlikely(!afl->extras)) { PFATAL("alloc"); } wptr = afl->extras[afl->extras_cnt].data = ck_alloc(rptr - lptr); @@ -162,7 +176,9 @@ void load_extras_file(afl_state_t *afl, u8 *fname, u32 *min_len, u32 *max_len, case 1 ... 31: case 128 ... 255: - FATAL("Non-printable characters in line %u.", cur_line); + WARNF("Non-printable characters in line %u.", cur_line); + continue; + break; case '\\': @@ -178,7 +194,8 @@ void load_extras_file(afl_state_t *afl, u8 *fname, u32 *min_len, u32 *max_len, if (*lptr != 'x' || !isxdigit(lptr[1]) || !isxdigit(lptr[2])) { - FATAL("Invalid escaping (not \\xNN) in line %u.", cur_line); + WARNF("Invalid escaping (not \\xNN) in line %u.", cur_line); + continue; } @@ -202,10 +219,11 @@ void load_extras_file(afl_state_t *afl, u8 *fname, u32 *min_len, u32 *max_len, if (afl->extras[afl->extras_cnt].len > MAX_DICT_FILE) { - FATAL( + WARNF( "Keyword too big in line %u (%s, limit is %s)", cur_line, stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), klen), stringify_mem_size(val_bufs[1], sizeof(val_bufs[1]), MAX_DICT_FILE)); + continue; } @@ -220,6 +238,41 @@ void load_extras_file(afl_state_t *afl, u8 *fname, u32 *min_len, u32 *max_len, } +static void extras_check_and_sort(afl_state_t *afl, u32 min_len, u32 max_len, + u8 *dir) { + + u8 val_bufs[2][STRINGIFY_VAL_SIZE_MAX]; + + if (!afl->extras_cnt) { + + WARNF("No usable data in '%s'", dir); + return; + + } + + qsort(afl->extras, afl->extras_cnt, sizeof(struct extra_data), + compare_extras_len); + + ACTF("Loaded %u extra tokens, size range %s to %s.", afl->extras_cnt, + stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), min_len), + stringify_mem_size(val_bufs[1], sizeof(val_bufs[1]), max_len)); + + if (max_len > 32) { + + WARNF("Some tokens are relatively large (%s) - consider trimming.", + stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), max_len)); + + } + + if (afl->extras_cnt > afl->max_det_extras) { + + WARNF("More than %u tokens - will use them probabilistically.", + afl->max_det_extras); + + } + +} + /* Read extras from the extras directory and sort them by size. */ void load_extras(afl_state_t *afl, u8 *dir) { @@ -249,7 +302,8 @@ void load_extras(afl_state_t *afl, u8 *dir) { if (errno == ENOTDIR) { load_extras_file(afl, dir, &min_len, &max_len, dict_level); - goto check_and_sort; + extras_check_and_sort(afl, min_len, max_len, dir); + return; } @@ -281,18 +335,21 @@ void load_extras(afl_state_t *afl, u8 *dir) { if (st.st_size > MAX_DICT_FILE) { - FATAL( + WARNF( "Extra '%s' is too big (%s, limit is %s)", fn, stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), st.st_size), stringify_mem_size(val_bufs[1], sizeof(val_bufs[1]), MAX_DICT_FILE)); + continue; } if (min_len > st.st_size) { min_len = st.st_size; } if (max_len < st.st_size) { max_len = st.st_size; } - afl->extras = ck_realloc_block( - afl->extras, (afl->extras_cnt + 1) * sizeof(struct extra_data)); + afl->extras = + afl_realloc((void **)&afl->extras, + (afl->extras_cnt + 1) * sizeof(struct extra_data)); + if (unlikely(!afl->extras)) { PFATAL("alloc"); } afl->extras[afl->extras_cnt].data = ck_alloc(st.st_size); afl->extras[afl->extras_cnt].len = st.st_size; @@ -312,56 +369,235 @@ void load_extras(afl_state_t *afl, u8 *dir) { closedir(d); -check_and_sort: + extras_check_and_sort(afl, min_len, max_len, dir); + +} + +/* Helper function for maybe_add_auto(afl, ) */ + +static inline u8 memcmp_nocase(u8 *m1, u8 *m2, u32 len) { + + while (len--) { + + if (tolower(*(m1++)) ^ tolower(*(m2++))) { return 1; } + + } + + return 0; + +} + +/* add an extra/dict/token - no checks performed, no sorting */ + +static void add_extra_nocheck(afl_state_t *afl, u8 *mem, u32 len) { + + afl->extras = afl_realloc((void **)&afl->extras, + (afl->extras_cnt + 1) * sizeof(struct extra_data)); + + if (unlikely(!afl->extras)) { PFATAL("alloc"); } + + afl->extras[afl->extras_cnt].data = ck_alloc(len); + afl->extras[afl->extras_cnt].len = len; + memcpy(afl->extras[afl->extras_cnt].data, mem, len); + afl->extras_cnt++; + + /* We only want to print this once */ + + if (afl->extras_cnt == afl->max_det_extras + 1) { + + WARNF("More than %u tokens - will use them probabilistically.", + afl->max_det_extras); + + } + +} + +/* Sometimes strings in input is transformed to unicode internally, so for + fuzzing we should attempt to de-unicode if it looks like simple unicode */ + +void deunicode_extras(afl_state_t *afl) { + + if (!afl->extras_cnt) return; + + u32 i, j, orig_cnt = afl->extras_cnt; + u8 buf[64]; + + for (i = 0; i < orig_cnt; ++i) { + + if (afl->extras[i].len < 6 || afl->extras[i].len > 64 || + afl->extras[i].len % 2) { + + continue; + + } + + u32 k = 0, z1 = 0, z2 = 0, z3 = 0, z4 = 0, half = afl->extras[i].len >> 1; + u32 quarter = half >> 1; + + for (j = 0; j < afl->extras[i].len; ++j) { + + switch (j % 4) { - if (!afl->extras_cnt) { FATAL("No usable files in '%s'", dir); } + case 2: + if (!afl->extras[i].data[j]) { ++z3; } + // fall through + case 0: + if (!afl->extras[i].data[j]) { ++z1; } + break; + case 3: + if (!afl->extras[i].data[j]) { ++z4; } + // fall through + case 1: + if (!afl->extras[i].data[j]) { ++z2; } + break; + + } + + } + + if ((z1 < half && z2 < half) || z1 + z2 == afl->extras[i].len) { continue; } + + // also maybe 32 bit unicode? + if (afl->extras[i].len % 4 == 0 && afl->extras[i].len >= 12 && + (z3 == quarter || z4 == quarter) && z1 + z2 == quarter * 3) { + + for (j = 0; j < afl->extras[i].len; ++j) { + + if (z4 < quarter) { + + if (j % 4 == 3) { buf[k++] = afl->extras[i].data[j]; } + + } else if (z3 < quarter) { + + if (j % 4 == 2) { buf[k++] = afl->extras[i].data[j]; } + + } else if (z2 < half) { + + if (j % 4 == 1) { buf[k++] = afl->extras[i].data[j]; } + + } else { + + if (j % 4 == 0) { buf[k++] = afl->extras[i].data[j]; } + + } + + } + + add_extra_nocheck(afl, buf, k); + k = 0; + + } + + for (j = 0; j < afl->extras[i].len; ++j) { + + if (z1 < half) { + + if (j % 2 == 0) { buf[k++] = afl->extras[i].data[j]; } + + } else { + + if (j % 2 == 1) { buf[k++] = afl->extras[i].data[j]; } + + } + + } + + add_extra_nocheck(afl, buf, k); + + } qsort(afl->extras, afl->extras_cnt, sizeof(struct extra_data), compare_extras_len); - OKF("Loaded %u extra tokens, size range %s to %s.", afl->extras_cnt, - stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), min_len), - stringify_mem_size(val_bufs[1], sizeof(val_bufs[1]), max_len)); +} - if (max_len > 32) { +/* Removes duplicates from the loaded extras. This can happen if multiple files + are loaded */ - WARNF("Some tokens are relatively large (%s) - consider trimming.", - stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), max_len)); +void dedup_extras(afl_state_t *afl) { - } + if (afl->extras_cnt < 2) return; - if (afl->extras_cnt > MAX_DET_EXTRAS) { + u32 i, j, orig_cnt = afl->extras_cnt; - WARNF("More than %d tokens - will use them probabilistically.", - MAX_DET_EXTRAS); + for (i = 0; i < afl->extras_cnt - 1; ++i) { + + for (j = i + 1; j < afl->extras_cnt; ++j) { + + restart_dedup: + + // if the goto was used we could be at the end of the list + if (j >= afl->extras_cnt || afl->extras[i].len != afl->extras[j].len) + break; + + if (memcmp(afl->extras[i].data, afl->extras[j].data, + afl->extras[i].len) == 0) { + + ck_free(afl->extras[j].data); + if (j + 1 < afl->extras_cnt) // not at the end of the list? + memmove((char *)&afl->extras[j], (char *)&afl->extras[j + 1], + (afl->extras_cnt - j - 1) * sizeof(struct extra_data)); + --afl->extras_cnt; + goto restart_dedup; // restart if several duplicates are in a row + + } + + } } + if (afl->extras_cnt != orig_cnt) + afl->extras = afl_realloc_exact( + (void **)&afl->extras, afl->extras_cnt * sizeof(struct extra_data)); + } -/* Helper function for maybe_add_auto(afl, ) */ +/* Adds a new extra / dict entry. */ +void add_extra(afl_state_t *afl, u8 *mem, u32 len) { -static inline u8 memcmp_nocase(u8 *m1, u8 *m2, u32 len) { + u32 i, found = 0; - while (len--) { + for (i = 0; i < afl->extras_cnt; i++) { - if (tolower(*(m1++)) ^ tolower(*(m2++))) { return 1; } + if (afl->extras[i].len == len) { + + if (memcmp(afl->extras[i].data, mem, len) == 0) return; + found = 1; + + } else { + + if (found) break; + + } } - return 0; + if (len > MAX_DICT_FILE) { + + u8 val_bufs[2][STRINGIFY_VAL_SIZE_MAX]; + WARNF("Extra '%.*s' is too big (%s, limit is %s), skipping file!", (int)len, + mem, stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), len), + stringify_mem_size(val_bufs[1], sizeof(val_bufs[1]), MAX_DICT_FILE)); + return; + + } else if (len > 32) { + + WARNF("Extra '%.*s' is pretty large, consider trimming.", (int)len, mem); + + } + + add_extra_nocheck(afl, mem, len); + + qsort(afl->extras, afl->extras_cnt, sizeof(struct extra_data), + compare_extras_len); } /* Maybe add automatic extra. */ -/* Ugly hack: afl state is transfered as u8* because we import data via - afl-forkserver.c - which is shared with other afl tools that do not - have the afl state struct */ -void maybe_add_auto(void *afl_tmp, u8 *mem, u32 len) { +void maybe_add_auto(afl_state_t *afl, u8 *mem, u32 len) { - afl_state_t *afl = (afl_state_t *)afl_tmp; - u32 i; + u32 i; /* Allow users to specify that they don't want auto dictionaries. */ @@ -375,7 +611,7 @@ void maybe_add_auto(void *afl_tmp, u8 *mem, u32 len) { } - if (i == len) { return; } + if (i == len || unlikely(len > MAX_AUTO_EXTRA)) { return; } /* Reject builtin interesting values. */ @@ -402,7 +638,7 @@ void maybe_add_auto(void *afl_tmp, u8 *mem, u32 len) { while (i--) { - if (*((u32 *)mem) == interesting_32[i] || + if (*((u32 *)mem) == (u32)interesting_32[i] || *((u32 *)mem) == SWAP32(interesting_32[i])) { return; @@ -452,10 +688,7 @@ void maybe_add_auto(void *afl_tmp, u8 *mem, u32 len) { if (afl->a_extras_cnt < MAX_AUTO_EXTRAS) { - afl->a_extras = ck_realloc_block( - afl->a_extras, (afl->a_extras_cnt + 1) * sizeof(struct extra_data)); - - afl->a_extras[afl->a_extras_cnt].data = ck_memdup(mem, len); + memcpy(afl->a_extras[afl->a_extras_cnt].data, mem, len); afl->a_extras[afl->a_extras_cnt].len = len; ++afl->a_extras_cnt; @@ -463,9 +696,7 @@ void maybe_add_auto(void *afl_tmp, u8 *mem, u32 len) { i = MAX_AUTO_EXTRAS / 2 + rand_below(afl, (MAX_AUTO_EXTRAS + 1) / 2); - ck_free(afl->a_extras[i].data); - - afl->a_extras[i].data = ck_memdup(mem, len); + memcpy(afl->a_extras[i].data, mem, len); afl->a_extras[i].len = len; afl->a_extras[i].hit_cnt = 0; @@ -475,13 +706,13 @@ sort_a_extras: /* First, sort all auto extras by use count, descending order. */ - qsort(afl->a_extras, afl->a_extras_cnt, sizeof(struct extra_data), - compare_extras_use_d); + qsort(afl->a_extras, afl->a_extras_cnt, sizeof(struct auto_extra_data), + compare_auto_extras_use_d); /* Then, sort the top USE_AUTO_EXTRAS entries by size. */ - qsort(afl->a_extras, MIN(USE_AUTO_EXTRAS, afl->a_extras_cnt), - sizeof(struct extra_data), compare_extras_len); + qsort(afl->a_extras, MIN((u32)USE_AUTO_EXTRAS, afl->a_extras_cnt), + sizeof(struct auto_extra_data), compare_auto_extras_len); } @@ -494,7 +725,7 @@ void save_auto(afl_state_t *afl) { if (!afl->auto_changed) { return; } afl->auto_changed = 0; - for (i = 0; i < MIN(USE_AUTO_EXTRAS, afl->a_extras_cnt); ++i) { + for (i = 0; i < MIN((u32)USE_AUTO_EXTRAS, afl->a_extras_cnt); ++i) { u8 *fn = alloc_printf("%s/queue/.state/auto_extras/auto_%06u", afl->out_dir, i); @@ -544,7 +775,7 @@ void load_auto(afl_state_t *afl) { if (len >= MIN_AUTO_EXTRA && len <= MAX_AUTO_EXTRA) { - maybe_add_auto((u8 *)afl, tmp, len); + maybe_add_auto(afl, tmp, len); } @@ -559,7 +790,7 @@ void load_auto(afl_state_t *afl) { } else { - OKF("No auto-generated dictionary tokens to reuse."); + ACTF("No auto-generated dictionary tokens to reuse."); } @@ -577,15 +808,7 @@ void destroy_extras(afl_state_t *afl) { } - ck_free(afl->extras); - - for (i = 0; i < afl->a_extras_cnt; ++i) { - - ck_free(afl->a_extras[i].data); - - } - - ck_free(afl->a_extras); + afl_free(afl->extras); } diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 9ec28cc5..06385330 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -25,13 +25,13 @@ #include "afl-fuzz.h" #include <limits.h> +#include "cmplog.h" #ifdef HAVE_AFFINITY -/* Build a list of processes bound to specific cores. Returns -1 if nothing - can be found. Assumes an upper bound of 4k CPUs. */ +/* bind process to a specific cpu. Returns 0 on failure. */ -void bind_to_free_cpu(afl_state_t *afl) { +static u8 bind_cpu(afl_state_t *afl, s32 cpuid) { #if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) cpu_set_t c; @@ -41,17 +41,107 @@ void bind_to_free_cpu(afl_state_t *afl) { psetid_t c; #endif - if (afl->cpu_core_count < 2) { return; } + afl->cpu_aff = cpuid; + + #if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) + + CPU_ZERO(&c); + CPU_SET(cpuid, &c); + + #elif defined(__NetBSD__) + + c = cpuset_create(); + if (c == NULL) { PFATAL("cpuset_create failed"); } + cpuset_set(cpuid, c); + + #elif defined(__sun) + + pset_create(&c); + if (pset_assign(c, cpuid, NULL)) { PFATAL("pset_assign failed"); } + + #endif + + #if defined(__linux__) + + return (sched_setaffinity(0, sizeof(c), &c) == 0); + + #elif defined(__FreeBSD__) || defined(__DragonFly__) + + return (pthread_setaffinity_np(pthread_self(), sizeof(c), &c) == 0); + + #elif defined(__NetBSD__) + + if (pthread_setaffinity_np(pthread_self(), cpuset_size(c), c)) { + + cpuset_destroy(c); + return 0; + + } + + cpuset_destroy(c); + return 1; + + #elif defined(__sun) + + if (pset_bind(c, P_PID, getpid(), NULL)) { + + pset_destroy(c); + return 0; + + } + + pset_destroy(c); + return 1; + + #else + + // this will need something for other platforms + // TODO: Solaris/Illumos has processor_bind ... might worth a try + WARNF("Cannot bind to CPU yet on this platform."); + return 1; + + #endif + +} + +/* Build a list of processes bound to specific cores. Returns -1 if nothing + can be found. Assumes an upper bound of 4k CPUs. */ + +void bind_to_free_cpu(afl_state_t *afl) { + + u8 cpu_used[4096] = {0}; + u8 lockfile[PATH_MAX] = ""; + s32 i; if (afl->afl_env.afl_no_affinity) { + if (afl->cpu_to_bind != -1) { + + FATAL("-b and AFL_NO_AFFINITY are mututally exclusive."); + + } + WARNF("Not binding to a CPU core (AFL_NO_AFFINITY set)."); return; } - u8 cpu_used[4096] = {0}, lockfile[PATH_MAX] = ""; - u32 i; + if (afl->cpu_to_bind != -1) { + + if (!bind_cpu(afl, afl->cpu_to_bind)) { + + FATAL( + "Could not bind to requested CPU %d! Make sure you passed a valid " + "-b.", + afl->cpu_to_bind); + + } + + return; + + } + + if (afl->cpu_core_count < 2) { return; } if (afl->sync_id) { @@ -167,23 +257,23 @@ void bind_to_free_cpu(afl_state_t *afl) { } - for (i = 0; i < proccount; i++) { + for (i = 0; i < (s32)proccount; i++) { #if defined(__FreeBSD__) if (!strcmp(procs[i].ki_comm, "idle")) continue; // fix when ki_oncpu = -1 - int oncpu; + s32 oncpu; oncpu = procs[i].ki_oncpu; if (oncpu == -1) oncpu = procs[i].ki_lastcpu; - if (oncpu != -1 && oncpu < sizeof(cpu_used) && procs[i].ki_pctcpu > 60) + if (oncpu != -1 && oncpu < (s32)sizeof(cpu_used) && procs[i].ki_pctcpu > 60) cpu_used[oncpu] = 1; #elif defined(__DragonFly__) - if (procs[i].kp_lwp.kl_cpuid < sizeof(cpu_used) && + if (procs[i].kp_lwp.kl_cpuid < (s32)sizeof(cpu_used) && procs[i].kp_lwp.kl_pctcpu > 10) cpu_used[procs[i].kp_lwp.kl_cpuid] = 1; @@ -222,7 +312,7 @@ void bind_to_free_cpu(afl_state_t *afl) { } - for (i = 0; i < proccount; i++) { + for (i = 0; i < (s32)proccount; i++) { if (procs[i].p_cpuid < sizeof(cpu_used) && procs[i].p_pctcpu > 0) cpu_used[procs[i].p_cpuid] = 1; @@ -266,7 +356,7 @@ void bind_to_free_cpu(afl_state_t *afl) { if (ncpus > sizeof(cpu_used)) ncpus = sizeof(cpu_used); - for (i = 0; i < ncpus; i++) { + for (i = 0; i < (s32)ncpus; i++) { k = kstat_lookup(m, "cpu_stat", i, NULL); if (kstat_read(m, k, &cs)) { @@ -293,130 +383,50 @@ void bind_to_free_cpu(afl_state_t *afl) { size_t cpu_start = 0; - try: - #if !defined(__ANDROID__) - for (i = cpu_start; i < afl->cpu_core_count; i++) { - - if (!cpu_used[i]) { break; } - - } - - if (i == afl->cpu_core_count) { + for (i = cpu_start; i < afl->cpu_core_count; i++) { #else - for (i = afl->cpu_core_count - cpu_start - 1; i > -1; i--) - if (!cpu_used[i]) break; - if (i == -1) { - - #endif - - SAYF("\n" cLRD "[-] " cRST - "Uh-oh, looks like all %d CPU cores on your system are allocated to\n" - " other instances of afl-fuzz (or similar CPU-locked tasks). " - "Starting\n" - " another fuzzer on this machine is probably a bad plan, but if " - "you are\n" - " absolutely sure, you can set AFL_NO_AFFINITY and try again.\n", - afl->cpu_core_count); - FATAL("No more free CPU cores"); - - } - - OKF("Found a free CPU core, try binding to #%u.", i); - - afl->cpu_aff = i; - - #if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) - - CPU_ZERO(&c); - CPU_SET(i, &c); - - #elif defined(__NetBSD__) - - c = cpuset_create(); - if (c == NULL) PFATAL("cpuset_create failed"); - cpuset_set(i, c); - - #elif defined(__sun) + /* for some reason Android goes backwards */ -pset_create(&c); -if (pset_assign(c, i, NULL)) PFATAL("pset_assign failed"); + for (i = afl->cpu_core_count - 1; i > -1; i--) { #endif - #if defined(__linux__) + if (cpu_used[i]) { continue; } - if (sched_setaffinity(0, sizeof(c), &c)) { + OKF("Found a free CPU core, try binding to #%u.", i); - if (cpu_start == afl->cpu_core_count) { + if (bind_cpu(afl, i)) { - PFATAL("sched_setaffinity failed for CPU %d, exit", i); + /* Success :) */ + break; } - WARNF("sched_setaffinity failed to CPU %d, trying next CPU", i); + WARNF("setaffinity failed to CPU %d, trying next CPU", i); cpu_start++; - goto try - ; } - #elif defined(__FreeBSD__) || defined(__DragonFly__) + if (lockfile[0]) unlink(lockfile); - if (pthread_setaffinity_np(pthread_self(), sizeof(c), &c)) { + if (i == afl->cpu_core_count || i == -1) { - if (cpu_start == afl->cpu_core_count) - PFATAL("pthread_setaffinity failed for cpu %d, exit", i); - WARNF("pthread_setaffinity failed to CPU %d, trying next CPU", i); - cpu_start++; - goto try - ; + SAYF("\n" cLRD "[-] " cRST + "Uh-oh, looks like all %d CPU cores on your system are allocated to\n" + " other instances of afl-fuzz (or similar CPU-locked tasks). " + "Starting\n" + " another fuzzer on this machine is probably a bad plan, but if " + "you are\n" + " absolutely sure, you can set AFL_NO_AFFINITY and try again.\n", + afl->cpu_core_count); + FATAL("No more free CPU cores"); } - #elif defined(__NetBSD__) - -if (pthread_setaffinity_np(pthread_self(), cpuset_size(c), c)) { - - if (cpu_start == afl->cpu_core_count) - PFATAL("pthread_setaffinity failed for cpu %d, exit", i); - WARNF("pthread_setaffinity failed to CPU %d, trying next CPU", i); - cpu_start++; - goto try - ; - -} - -cpuset_destroy(c); - - #elif defined(__sun) - -if (pset_bind(c, P_PID, getpid(), NULL)) { - - if (cpu_start == afl->cpu_core_count) - PFATAL("pset_bind failed for cpu %d, exit", i); - WARNF("pset_bind failed to CPU %d, trying next CPU", i); - cpu_start++; - goto try - ; - -} - -pset_destroy(c); - - #else - - // this will need something for other platforms - // TODO: Solaris/Illumos has processor_bind ... might worth a try - - #endif - - if (lockfile[0]) unlink(lockfile); - // we leave the environment variable to ensure a cleanup for other processes - } #endif /* HAVE_AFFINITY */ @@ -451,6 +461,7 @@ void read_foreign_testcases(afl_state_t *afl, int first) { u32 i, iter; u8 val_buf[2][STRINGIFY_VAL_SIZE_MAX]; + u8 foreign_name[16]; for (iter = 0; iter < afl->foreign_sync_cnt; iter++) { @@ -458,11 +469,22 @@ void read_foreign_testcases(afl_state_t *afl, int first) { afl->foreign_syncs[iter].dir[0] != 0) { if (first) ACTF("Scanning '%s'...", afl->foreign_syncs[iter].dir); - time_t ctime_max = 0; + time_t mtime_max = 0; + u8 * name = strrchr(afl->foreign_syncs[iter].dir, '/'); + if (!name) { name = afl->foreign_syncs[iter].dir; } + if (!strcmp(name, "queue") || !strcmp(name, "out") || + !strcmp(name, "default")) { + + snprintf(foreign_name, sizeof(foreign_name), "foreign_%u", iter); + + } else { - /* We use scandir() + alphasort() rather than readdir() because otherwise, - the ordering of test cases would vary somewhat randomly and would be - difficult to control. */ + snprintf(foreign_name, sizeof(foreign_name), "%s_%u", name, iter); + + } + + /* We do not use sorting yet and do a more expensive mtime check instead. + a mtimesort() implementation would be better though. */ nl_cnt = scandir(afl->foreign_syncs[iter].dir, &nl, NULL, NULL); @@ -481,9 +503,13 @@ void read_foreign_testcases(afl_state_t *afl, int first) { if (nl_cnt == 0) { - if (first) + if (first) { + WARNF("directory %s is currently empty", afl->foreign_syncs[iter].dir); + + } + continue; } @@ -496,7 +522,7 @@ void read_foreign_testcases(afl_state_t *afl, int first) { afl->stage_cur = 0; afl->stage_max = 0; - for (i = 0; i < nl_cnt; ++i) { + for (i = 0; i < (u32)nl_cnt; ++i) { struct stat st; @@ -512,8 +538,8 @@ void read_foreign_testcases(afl_state_t *afl, int first) { } - /* we detect new files by their ctime */ - if (likely(st.st_ctime <= afl->foreign_syncs[iter].ctime)) { + /* we detect new files by their mtime */ + if (likely(st.st_mtime <= afl->foreign_syncs[iter].mtime)) { ck_free(fn2); continue; @@ -531,11 +557,15 @@ void read_foreign_testcases(afl_state_t *afl, int first) { if (st.st_size > MAX_FILE) { - if (first) + if (first) { + WARNF( "Test case '%s' is too big (%s, limit is %s), skipping", fn2, stringify_mem_size(val_buf[0], sizeof(val_buf[0]), st.st_size), stringify_mem_size(val_buf[1], sizeof(val_buf[1]), MAX_FILE)); + + } + ck_free(fn2); continue; @@ -564,18 +594,18 @@ void read_foreign_testcases(afl_state_t *afl, int first) { write_to_testcase(afl, mem, st.st_size); fault = fuzz_run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout); - afl->syncing_party = "foreign"; + afl->syncing_party = foreign_name; afl->queued_imported += save_if_interesting(afl, mem, st.st_size, fault); afl->syncing_party = 0; munmap(mem, st.st_size); close(fd); - if (st.st_ctime > ctime_max) ctime_max = st.st_ctime; + if (st.st_mtime > mtime_max) mtime_max = st.st_mtime; } - afl->foreign_syncs[iter].ctime = ctime_max; + afl->foreign_syncs[iter].mtime = mtime_max; free(nl); /* not tracked */ } @@ -594,37 +624,43 @@ void read_foreign_testcases(afl_state_t *afl, int first) { /* Read all testcases from the input directory, then queue them for testing. Called at startup. */ -void read_testcases(afl_state_t *afl) { +void read_testcases(afl_state_t *afl, u8 *directory) { struct dirent **nl; - s32 nl_cnt; + s32 nl_cnt, subdirs = 1; u32 i; - u8 * fn1; - - u8 val_buf[2][STRINGIFY_VAL_SIZE_MAX]; + u8 * fn1, *dir = directory; + u8 val_buf[2][STRINGIFY_VAL_SIZE_MAX]; /* Auto-detect non-in-place resumption attempts. */ - fn1 = alloc_printf("%s/queue", afl->in_dir); - if (!access(fn1, F_OK)) { + if (dir == NULL) { - afl->in_dir = fn1; + fn1 = alloc_printf("%s/queue", afl->in_dir); + if (!access(fn1, F_OK)) { - } else { + afl->in_dir = fn1; + subdirs = 0; + + } else { + + ck_free(fn1); - ck_free(fn1); + } + + dir = afl->in_dir; } - ACTF("Scanning '%s'...", afl->in_dir); + ACTF("Scanning '%s'...", dir); /* We use scandir() + alphasort() rather than readdir() because otherwise, the ordering of test cases would vary somewhat randomly and would be difficult to control. */ - nl_cnt = scandir(afl->in_dir, &nl, NULL, alphasort); + nl_cnt = scandir(dir, &nl, NULL, alphasort); - if (nl_cnt < 0) { + if (nl_cnt < 0 && directory == NULL) { if (errno == ENOENT || errno == ENOTDIR) { @@ -639,37 +675,47 @@ void read_testcases(afl_state_t *afl) { } - PFATAL("Unable to open '%s'", afl->in_dir); + PFATAL("Unable to open '%s'", dir); } - if (afl->shuffle_queue && nl_cnt > 1) { + if (unlikely(afl->old_seed_selection && afl->shuffle_queue && nl_cnt > 1)) { ACTF("Shuffling queue..."); shuffle_ptrs(afl, (void **)nl, nl_cnt); } - for (i = 0; i < nl_cnt; ++i) { + for (i = 0; i < (u32)nl_cnt; ++i) { struct stat st; u8 dfn[PATH_MAX]; snprintf(dfn, PATH_MAX, "%s/.state/deterministic_done/%s", afl->in_dir, nl[i]->d_name); - u8 *fn2 = alloc_printf("%s/%s", afl->in_dir, nl[i]->d_name); + u8 *fn2 = alloc_printf("%s/%s", dir, nl[i]->d_name); u8 passed_det = 0; - free(nl[i]); /* not tracked */ - if (lstat(fn2, &st) || access(fn2, R_OK)) { PFATAL("Unable to access '%s'", fn2); } - /* This also takes care of . and .. */ + /* obviously we want to skip "descending" into . and .. directories, + however it is a good idea to skip also directories that start with + a dot */ + if (subdirs && S_ISDIR(st.st_mode) && nl[i]->d_name[0] != '.') { + + free(nl[i]); /* not tracked */ + read_testcases(afl, fn2); + ck_free(fn2); + continue; + + } + + free(nl[i]); if (!S_ISREG(st.st_mode) || !st.st_size || strstr(fn2, "/README.txt")) { @@ -680,11 +726,9 @@ void read_testcases(afl_state_t *afl) { if (st.st_size > MAX_FILE) { - WARNF("Test case '%s' is too big (%s, limit is %s), skipping", fn2, + WARNF("Test case '%s' is too big (%s, limit is %s), partial reading", fn2, stringify_mem_size(val_buf[0], sizeof(val_buf[0]), st.st_size), stringify_mem_size(val_buf[1], sizeof(val_buf[1]), MAX_FILE)); - ck_free(fn2); - continue; } @@ -695,13 +739,49 @@ void read_testcases(afl_state_t *afl) { if (!access(dfn, F_OK)) { passed_det = 1; } - add_to_queue(afl, fn2, st.st_size, passed_det); + add_to_queue(afl, fn2, st.st_size >= MAX_FILE ? MAX_FILE : st.st_size, + passed_det); + + if (unlikely(afl->shm.cmplog_mode)) { + + if (afl->cmplog_lvl == 1) { + + if (!afl->cmplog_max_filesize || + afl->cmplog_max_filesize < st.st_size) { + + afl->cmplog_max_filesize = st.st_size; + + } + + } else if (afl->cmplog_lvl == 2) { + + if (!afl->cmplog_max_filesize || + afl->cmplog_max_filesize > st.st_size) { + + afl->cmplog_max_filesize = st.st_size; + + } + + } + + } + + /* + if (unlikely(afl->schedule >= FAST && afl->schedule <= RARE)) { + + u64 cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, + HASH_CONST); afl->queue_top->n_fuzz_entry = cksum % N_FUZZ_SIZE; + afl->n_fuzz[afl->queue_top->n_fuzz_entry] = 1; + + } + + */ } free(nl); /* not tracked */ - if (!afl->queued_paths) { + if (!afl->queued_paths && directory == NULL) { SAYF("\n" cLRD "[-] " cRST "Looks like there are no valid test cases in the input directory! The " @@ -716,6 +796,20 @@ void read_testcases(afl_state_t *afl) { } + if (unlikely(afl->shm.cmplog_mode)) { + + if (afl->cmplog_max_filesize < 1024) { + + afl->cmplog_max_filesize = 1024; + + } else { + + afl->cmplog_max_filesize = (((afl->cmplog_max_filesize >> 10) + 1) << 10); + + } + + } + afl->last_path_time = 0; afl->queued_at_start = afl->queued_paths; @@ -726,16 +820,28 @@ void read_testcases(afl_state_t *afl) { void perform_dry_run(afl_state_t *afl) { - struct queue_entry *q = afl->queue; - u32 cal_failures = 0; + struct queue_entry *q; + u32 cal_failures = 0, idx; u8 * skip_crashes = afl->afl_env.afl_skip_crashes; + u8 * use_mem; + + for (idx = 0; idx < afl->queued_paths; idx++) { - while (q) { + q = afl->queue_buf[idx]; + if (unlikely(!q || q->disabled)) { continue; } - u8 *use_mem; u8 res; s32 fd; + if (unlikely(!q->len)) { + + WARNF("Skipping 0-sized entry in queue (%s)", q->fname); + continue; + + } + + if (afl->afl_env.afl_cmplog_only_new) { q->colorized = CMPLOG_LVL_MAX; } + u8 *fn = strrchr(q->fname, '/') + 1; ACTF("Attempting dry run with '%s'...", fn); @@ -743,9 +849,9 @@ void perform_dry_run(afl_state_t *afl) { fd = open(q->fname, O_RDONLY); if (fd < 0) { PFATAL("Unable to open '%s'", q->fname); } - use_mem = ck_alloc_nozero(q->len); - - if (read(fd, use_mem, q->len) != q->len) { + u32 read_len = MIN(q->len, (u32)MAX_FILE); + use_mem = afl_realloc(AFL_BUF_PARAM(in), read_len); + if (read(fd, use_mem, read_len) != (ssize_t)read_len) { FATAL("Short read from '%s'", q->fname); @@ -754,7 +860,6 @@ void perform_dry_run(afl_state_t *afl) { close(fd); res = calibrate_case(afl, q, use_mem, 0, 1); - ck_free(use_mem); if (afl->stop_soon) { return; } @@ -777,32 +882,23 @@ void perform_dry_run(afl_state_t *afl) { if (afl->timeout_given) { - /* The -t nn+ syntax in the command line sets afl->timeout_given to - '2' and instructs afl-fuzz to tolerate but skip queue entries that - time out. */ + /* if we have a timeout but a timeout value was given then always + skip. The '+' meaning has been changed! */ + WARNF("Test case results in a timeout (skipping)"); + ++cal_failures; + q->cal_failed = CAL_CHANCES; + q->disabled = 1; + q->perf_score = 0; - if (afl->timeout_given > 1) { + if (!q->was_fuzzed) { - WARNF("Test case results in a timeout (skipping)"); - q->cal_failed = CAL_CHANCES; - ++cal_failures; - break; + q->was_fuzzed = 1; + --afl->pending_not_fuzzed; + --afl->active_paths; } - SAYF("\n" cLRD "[-] " cRST - "The program took more than %u ms to process one of the initial " - "test cases.\n" - " Usually, the right thing to do is to relax the -t option - " - "or to delete it\n" - " altogether and allow the fuzzer to auto-calibrate. That " - "said, if you know\n" - " what you are doing and want to simply skip the unruly test " - "cases, append\n" - " '+' at the end of the value passed to -t ('-t %u+').\n", - afl->fsrv.exec_tmout, afl->fsrv.exec_tmout); - - FATAL("Test case '%s' results in a timeout", fn); + break; } else { @@ -828,7 +924,19 @@ void perform_dry_run(afl_state_t *afl) { if (skip_crashes) { - WARNF("Test case results in a crash (skipping)"); + if (afl->fsrv.uses_crash_exitcode) { + + WARNF( + "Test case results in a crash or AFL_CRASH_EXITCODE %d " + "(skipping)", + (int)(s8)afl->fsrv.crash_exitcode); + + } else { + + WARNF("Test case results in a crash (skipping)"); + + } + q->cal_failed = CAL_CHANCES; ++cal_failures; break; @@ -914,7 +1022,62 @@ void perform_dry_run(afl_state_t *afl) { #undef MSG_ULIMIT_USAGE #undef MSG_FORK_ON_APPLE - FATAL("Test case '%s' results in a crash", fn); + if (afl->fsrv.uses_crash_exitcode) { + + WARNF( + "Test case '%s' results in a crash or AFL_CRASH_EXITCODE %d, " + "skipping", + fn, (int)(s8)afl->fsrv.crash_exitcode); + + } else { + + WARNF("Test case '%s' results in a crash, skipping", fn); + + } + + /* Remove from fuzzing queue but keep for splicing */ + + struct queue_entry *p = afl->queue; + + if (!p->was_fuzzed) { + + p->was_fuzzed = 1; + --afl->pending_not_fuzzed; + --afl->active_paths; + + } + + p->disabled = 1; + p->perf_score = 0; + + u32 i = 0; + while (unlikely(i < afl->queued_paths && afl->queue_buf[i] && + afl->queue_buf[i]->disabled)) { + + ++i; + + } + + if (i < afl->queued_paths && afl->queue_buf[i]) { + + afl->queue = afl->queue_buf[i]; + + } else { + + afl->queue = afl->queue_buf[0]; + + } + + afl->max_depth = 0; + for (i = 0; i < afl->queued_paths && likely(afl->queue_buf[i]); i++) { + + if (!afl->queue_buf[i]->disabled && + afl->queue_buf[i]->depth > afl->max_depth) + afl->max_depth = afl->queue_buf[i]->depth; + + } + + break; case FSRV_RUN_ERROR: @@ -943,8 +1106,6 @@ void perform_dry_run(afl_state_t *afl) { } - q = q->next; - } if (cal_failures) { @@ -968,6 +1129,79 @@ void perform_dry_run(afl_state_t *afl) { } + /* Now we remove all entries from the queue that have a duplicate trace map */ + + u32 duplicates = 0, i; + + for (idx = 0; idx < afl->queued_paths; idx++) { + + q = afl->queue_buf[idx]; + if (!q || q->disabled || q->cal_failed || !q->exec_cksum) { continue; } + + u32 done = 0; + for (i = idx + 1; + i < afl->queued_paths && !done && likely(afl->queue_buf[i]); i++) { + + struct queue_entry *p = afl->queue_buf[i]; + if (p->disabled || p->cal_failed || !p->exec_cksum) { continue; } + + if (p->exec_cksum == q->exec_cksum) { + + duplicates = 1; + + // we keep the shorter file + if (p->len >= q->len) { + + if (!p->was_fuzzed) { + + p->was_fuzzed = 1; + --afl->pending_not_fuzzed; + --afl->active_paths; + + } + + p->disabled = 1; + p->perf_score = 0; + + } else { + + if (!q->was_fuzzed) { + + q->was_fuzzed = 1; + --afl->pending_not_fuzzed; + --afl->active_paths; + + } + + q->disabled = 1; + q->perf_score = 0; + + done = 1; + + } + + } + + } + + } + + if (duplicates) { + + afl->max_depth = 0; + + for (idx = 0; idx < afl->queued_paths; idx++) { + + if (afl->queue_buf[idx] && !afl->queue_buf[idx]->disabled && + afl->queue_buf[idx]->depth > afl->max_depth) + afl->max_depth = afl->queue_buf[idx]->depth; + + } + + afl->queue_top = afl->queue; + + } + OKF("All test cases processed."); } @@ -1009,12 +1243,16 @@ static void link_or_copy(u8 *old_path, u8 *new_path) { void pivot_inputs(afl_state_t *afl) { - struct queue_entry *q = afl->queue; - u32 id = 0; + struct queue_entry *q; + u32 id = 0, i; ACTF("Creating hard links for all input files..."); - while (q) { + for (i = 0; i < afl->queued_paths && likely(afl->queue_buf[i]); i++) { + + q = afl->queue_buf[i]; + + if (unlikely(q->disabled)) { continue; } u8 *nfn, *rsl = strrchr(q->fname, '/'); u32 orig_id; @@ -1042,19 +1280,14 @@ void pivot_inputs(afl_state_t *afl) { afl->resuming_fuzz = 1; nfn = alloc_printf("%s/queue/%s", afl->out_dir, rsl); - /* Since we're at it, let's also try to find parent and figure out the + /* Since we're at it, let's also get the parent and figure out the appropriate depth for this entry. */ src_str = strchr(rsl + 3, ':'); if (src_str && sscanf(src_str + 1, "%06u", &src_id) == 1) { - struct queue_entry *s = afl->queue; - while (src_id-- && s) { - - s = s->next; - - } + struct queue_entry *s = afl->queue_buf[src_id]; if (s) { q->depth = s->depth + 1; } @@ -1102,7 +1335,6 @@ void pivot_inputs(afl_state_t *afl) { if (q->passed_det) { mark_as_det_done(afl, q); } - q = q->next; ++id; } @@ -1495,20 +1727,21 @@ static void handle_existing_out_dir(afl_state_t *afl) { if (afl->in_place_resume && rmdir(fn)) { - time_t cur_t = time(0); - struct tm *t = localtime(&cur_t); + time_t cur_t = time(0); + struct tm t; + localtime_r(&cur_t, &t); #ifndef SIMPLE_FILES - u8 *nfn = alloc_printf("%s.%04d-%02d-%02d-%02d:%02d:%02d", fn, - t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, - t->tm_hour, t->tm_min, t->tm_sec); + u8 *nfn = + alloc_printf("%s.%04d-%02d-%02d-%02d:%02d:%02d", fn, t.tm_year + 1900, + t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); #else - u8 *nfn = alloc_printf("%s_%04d%02d%02d%02d%02d%02d", fn, t->tm_year + 1900, - t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, - t->tm_sec); + u8 *nfn = + alloc_printf("%s_%04d%02d%02d%02d%02d%02d", fn, t.tm_year + 1900, + t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); #endif /* ^!SIMPLE_FILES */ @@ -1526,20 +1759,21 @@ static void handle_existing_out_dir(afl_state_t *afl) { if (afl->in_place_resume && rmdir(fn)) { - time_t cur_t = time(0); - struct tm *t = localtime(&cur_t); + time_t cur_t = time(0); + struct tm t; + localtime_r(&cur_t, &t); #ifndef SIMPLE_FILES - u8 *nfn = alloc_printf("%s.%04d-%02d-%02d-%02d:%02d:%02d", fn, - t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, - t->tm_hour, t->tm_min, t->tm_sec); + u8 *nfn = + alloc_printf("%s.%04d-%02d-%02d-%02d:%02d:%02d", fn, t.tm_year + 1900, + t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); #else - u8 *nfn = alloc_printf("%s_%04d%02d%02d%02d%02d%02d", fn, t->tm_year + 1900, - t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, - t->tm_sec); + u8 *nfn = + alloc_printf("%s_%04d%02d%02d%02d%02d%02d", fn, t.tm_year + 1900, + t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); #endif /* ^!SIMPLE_FILES */ @@ -1653,7 +1887,6 @@ int check_main_node_exists(afl_state_t *afl) { void setup_dirs_fds(afl_state_t *afl) { u8 *tmp; - s32 fd; ACTF("Setting up output directories..."); @@ -1779,7 +2012,7 @@ void setup_dirs_fds(afl_state_t *afl) { /* Gnuplot output file. */ tmp = alloc_printf("%s/plot_data", afl->out_dir); - fd = open(tmp, O_WRONLY | O_CREAT | O_EXCL, 0600); + int fd = open(tmp, O_WRONLY | O_CREAT | O_EXCL, 0600); if (fd < 0) { PFATAL("Unable to create '%s'", tmp); } ck_free(tmp); @@ -1789,7 +2022,7 @@ void setup_dirs_fds(afl_state_t *afl) { fprintf(afl->fsrv.plot_file, "# unix_time, cycles_done, cur_path, paths_total, " "pending_total, pending_favs, map_size, unique_crashes, " - "unique_hangs, max_depth, execs_per_sec\n"); + "unique_hangs, max_depth, execs_per_sec, total_execs, edges_found\n"); fflush(afl->fsrv.plot_file); /* ignore errors */ @@ -1828,24 +2061,26 @@ void setup_cmdline_file(afl_state_t *afl, char **argv) { void setup_stdio_file(afl_state_t *afl) { - u8 *fn; if (afl->file_extension) { - fn = alloc_printf("%s/.cur_input.%s", afl->tmp_dir, afl->file_extension); + afl->fsrv.out_file = + alloc_printf("%s/.cur_input.%s", afl->tmp_dir, afl->file_extension); } else { - fn = alloc_printf("%s/.cur_input", afl->tmp_dir); + afl->fsrv.out_file = alloc_printf("%s/.cur_input", afl->tmp_dir); } - unlink(fn); /* Ignore errors */ + unlink(afl->fsrv.out_file); /* Ignore errors */ - afl->fsrv.out_fd = open(fn, O_RDWR | O_CREAT | O_EXCL, 0600); + afl->fsrv.out_fd = open(afl->fsrv.out_file, O_RDWR | O_CREAT | O_EXCL, 0600); - if (afl->fsrv.out_fd < 0) { PFATAL("Unable to create '%s'", fn); } + if (afl->fsrv.out_fd < 0) { - ck_free(fn); + PFATAL("Unable to create '%s'", afl->fsrv.out_file); + + } } @@ -2059,6 +2294,8 @@ void check_cpu_governor(afl_state_t *afl) { "drop.\n", min / 1024, max / 1024); FATAL("Suboptimal CPU scaling governor"); +#else + (void)afl; #endif } @@ -2134,7 +2371,7 @@ void get_core_count(afl_state_t *afl) { WARNF("System under apparent load, performance may be spotty."); - } else if (cur_runnable + 1 <= afl->cpu_core_count) { + } else if ((s64)cur_runnable + 1 <= (s64)afl->cpu_core_count) { OKF("Try parallel jobs - see %s/parallel_fuzzing.md.", doc_path); @@ -2157,12 +2394,6 @@ void fix_up_sync(afl_state_t *afl) { u8 *x = afl->sync_id; - if (afl->non_instrumented_mode) { - - FATAL("-S / -M and -n are mutually exclusive"); - - } - while (*x) { if (!isalnum(*x) && *x != '_' && *x != '-') { @@ -2188,16 +2419,19 @@ void fix_up_sync(afl_state_t *afl) { static void handle_resize(int sig) { + (void)sig; afl_states_clear_screen(); } /* Check ASAN options. */ -void check_asan_opts(void) { +void check_asan_opts(afl_state_t *afl) { u8 *x = get_afl_env("ASAN_OPTIONS"); + (void)(afl); + if (x) { if (!strstr(x, "abort_on_error=1")) { @@ -2206,12 +2440,15 @@ void check_asan_opts(void) { } - if (!strstr(x, "symbolize=0")) { +#ifndef ASAN_BUILD + if (!afl->debug && !strstr(x, "symbolize=0")) { FATAL("Custom ASAN_OPTIONS set without symbolize=0 - please fix!"); } +#endif + } x = get_afl_env("MSAN_OPTIONS"); @@ -2225,7 +2462,7 @@ void check_asan_opts(void) { } - if (!strstr(x, "symbolize=0")) { + if (!afl->debug && !strstr(x, "symbolize=0")) { FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!"); @@ -2239,6 +2476,7 @@ void check_asan_opts(void) { static void handle_stop_sig(int sig) { + (void)sig; afl_states_stop(); } @@ -2247,6 +2485,7 @@ static void handle_stop_sig(int sig) { static void handle_skipreq(int sig) { + (void)sig; afl_states_request_skip(); } @@ -2259,6 +2498,7 @@ void setup_testcase_shmem(afl_state_t *afl) { // we need to set the non-instrumented mode to not overwrite the SHM_ENV_VAR u8 *map = afl_shm_init(afl->shm_fuzz, MAX_FILE + sizeof(u32), 1); + afl->shm_fuzz->shmemfuzz_mode = 1; if (!map) { FATAL("BUG: Zero return from afl_shm_init."); } @@ -2281,6 +2521,8 @@ void setup_testcase_shmem(afl_state_t *afl) { void check_binary(afl_state_t *afl, u8 *fname) { + if (unlikely(!fname)) { FATAL("BUG: Binary name is NULL"); } + u8 * env_path = 0; struct stat st; @@ -2309,6 +2551,7 @@ void check_binary(afl_state_t *afl, u8 *fname) { if (delim) { cur_elem = ck_alloc(delim - env_path + 1); + if (unlikely(!cur_elem)) { FATAL("Unexpected large PATH"); } memcpy(cur_elem, env_path, delim - env_path); ++delim; @@ -2352,7 +2595,9 @@ void check_binary(afl_state_t *afl, u8 *fname) { } - if (afl->afl_env.afl_skip_bin_check || afl->use_wine || afl->unicorn_mode) { + if (afl->afl_env.afl_skip_bin_check || afl->use_wine || afl->unicorn_mode || + (afl->fsrv.qemu_mode && getenv("AFL_QEMU_CUSTOM_BIN")) || + afl->non_instrumented_mode) { return; diff --git a/src/afl-fuzz-mutators.c b/src/afl-fuzz-mutators.c index b288cf9f..80df6d08 100644 --- a/src/afl-fuzz-mutators.c +++ b/src/afl-fuzz-mutators.c @@ -93,9 +93,9 @@ void setup_custom_mutators(afl_state_t *afl) { } - struct custom_mutator *mutator = load_custom_mutator_py(afl, module_name); + struct custom_mutator *m = load_custom_mutator_py(afl, module_name); afl->custom_mutators_count++; - list_append(&afl->custom_mutator_list, mutator); + list_append(&afl->custom_mutator_list, m); } @@ -122,9 +122,8 @@ void destroy_custom_mutators(afl_state_t *afl) { if (el->post_process_buf) { - ck_free(el->post_process_buf); + afl_free(el->post_process_buf); el->post_process_buf = NULL; - el->post_process_size = 0; } @@ -142,6 +141,10 @@ struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) { struct custom_mutator *mutator = ck_alloc(sizeof(struct custom_mutator)); mutator->name = fn; + if (memchr(fn, '/', strlen(fn))) + mutator->name_short = strrchr(fn, '/') + 1; + else + mutator->name_short = strdup(fn); ACTF("Loading custom mutator library from '%s'...", fn); dh = dlopen(fn, RTLD_NOW); @@ -151,7 +154,11 @@ struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) { /* Mutator */ /* "afl_custom_init", optional for backward compatibility */ mutator->afl_custom_init = dlsym(dh, "afl_custom_init"); - if (!mutator->afl_custom_init) FATAL("Symbol 'afl_custom_init' not found."); + if (!mutator->afl_custom_init) { + + FATAL("Symbol 'afl_custom_init' not found."); + + } /* "afl_custom_fuzz" or "afl_custom_mutator", required */ mutator->afl_custom_fuzz = dlsym(dh, "afl_custom_fuzz"); @@ -161,37 +168,74 @@ struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) { WARNF("Symbol 'afl_custom_fuzz' not found. Try 'afl_custom_mutator'."); mutator->afl_custom_fuzz = dlsym(dh, "afl_custom_mutator"); - if (!mutator->afl_custom_fuzz) + if (!mutator->afl_custom_fuzz) { + WARNF("Symbol 'afl_custom_mutator' not found."); + } + + } + + /* "afl_custom_introspection", optional */ +#ifdef INTROSPECTION + mutator->afl_custom_introspection = dlsym(dh, "afl_custom_introspection"); + if (!mutator->afl_custom_introspection) { + + ACTF("optional symbol 'afl_custom_introspection' not found."); + + } + +#endif + + /* "afl_custom_fuzz_count", optional */ + mutator->afl_custom_fuzz_count = dlsym(dh, "afl_custom_fuzz_count"); + if (!mutator->afl_custom_fuzz_count) { + + ACTF("optional symbol 'afl_custom_fuzz_count' not found."); + } /* "afl_custom_deinit", optional for backward compatibility */ mutator->afl_custom_deinit = dlsym(dh, "afl_custom_deinit"); - if (!mutator->afl_custom_deinit) + if (!mutator->afl_custom_deinit) { + FATAL("Symbol 'afl_custom_deinit' not found."); + } + /* "afl_custom_post_process", optional */ mutator->afl_custom_post_process = dlsym(dh, "afl_custom_post_process"); - if (!mutator->afl_custom_post_process) + if (!mutator->afl_custom_post_process) { + ACTF("optional symbol 'afl_custom_post_process' not found."); + } + u8 notrim = 0; /* "afl_custom_init_trim", optional */ mutator->afl_custom_init_trim = dlsym(dh, "afl_custom_init_trim"); - if (!mutator->afl_custom_init_trim) + if (!mutator->afl_custom_init_trim) { + ACTF("optional symbol 'afl_custom_init_trim' not found."); + } + /* "afl_custom_trim", optional */ mutator->afl_custom_trim = dlsym(dh, "afl_custom_trim"); - if (!mutator->afl_custom_trim) + if (!mutator->afl_custom_trim) { + ACTF("optional symbol 'afl_custom_trim' not found."); + } + /* "afl_custom_post_trim", optional */ mutator->afl_custom_post_trim = dlsym(dh, "afl_custom_post_trim"); - if (!mutator->afl_custom_post_trim) + if (!mutator->afl_custom_post_trim) { + ACTF("optional symbol 'afl_custom_post_trim' not found."); + } + if (notrim) { mutator->afl_custom_init_trim = NULL; @@ -205,31 +249,54 @@ struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) { /* "afl_custom_havoc_mutation", optional */ mutator->afl_custom_havoc_mutation = dlsym(dh, "afl_custom_havoc_mutation"); - if (!mutator->afl_custom_havoc_mutation) + if (!mutator->afl_custom_havoc_mutation) { + ACTF("optional symbol 'afl_custom_havoc_mutation' not found."); + } + /* "afl_custom_havoc_mutation", optional */ mutator->afl_custom_havoc_mutation_probability = dlsym(dh, "afl_custom_havoc_mutation_probability"); - if (!mutator->afl_custom_havoc_mutation_probability) + if (!mutator->afl_custom_havoc_mutation_probability) { + ACTF("optional symbol 'afl_custom_havoc_mutation_probability' not found."); + } + /* "afl_custom_queue_get", optional */ mutator->afl_custom_queue_get = dlsym(dh, "afl_custom_queue_get"); - if (!mutator->afl_custom_queue_get) + if (!mutator->afl_custom_queue_get) { + ACTF("optional symbol 'afl_custom_queue_get' not found."); + } + /* "afl_custom_queue_new_entry", optional */ mutator->afl_custom_queue_new_entry = dlsym(dh, "afl_custom_queue_new_entry"); - if (!mutator->afl_custom_queue_new_entry) + if (!mutator->afl_custom_queue_new_entry) { + ACTF("optional symbol 'afl_custom_queue_new_entry' not found"); + } + + /* "afl_custom_describe", optional */ + mutator->afl_custom_describe = dlsym(dh, "afl_custom_describe"); + if (!mutator->afl_custom_describe) { + + ACTF("Symbol 'afl_custom_describe' not found."); + + } + OKF("Custom mutator '%s' installed successfully.", fn); /* Initialize the custom mutator */ - if (mutator->afl_custom_init) + if (mutator->afl_custom_init) { + mutator->data = mutator->afl_custom_init(afl, rand_below(afl, 0xFFFFFFFF)); + } + mutator->stacked_custom = (mutator && mutator->afl_custom_havoc_mutation); mutator->stacked_custom_prob = 6; // like one of the default mutations in havoc @@ -252,16 +319,20 @@ u8 trim_case_custom(afl_state_t *afl, struct queue_entry *q, u8 *in_buf, /* Initialize trimming in the custom mutator */ afl->stage_cur = 0; - afl->stage_max = mutator->afl_custom_init_trim(mutator->data, in_buf, q->len); - if (unlikely(afl->stage_max) < 0) { + s32 retval = mutator->afl_custom_init_trim(mutator->data, in_buf, q->len); + if (unlikely(retval) < 0) { + + FATAL("custom_init_trim error ret: %d", retval); + + } else { - FATAL("custom_init_trim error ret: %d", afl->stage_max); + afl->stage_max = retval; } if (afl->not_on_tty && afl->debug) { - SAYF("[Custom Trimming] START: Max %d iterations, %u bytes", afl->stage_max, + SAYF("[Custom Trimming] START: Max %u iterations, %u bytes", afl->stage_max, q->len); } @@ -279,7 +350,7 @@ u8 trim_case_custom(afl_state_t *afl, struct queue_entry *q, u8 *in_buf, if (unlikely(!retbuf)) { - FATAL("custom_trim failed (ret %zd)", retlen); + FATAL("custom_trim failed (ret %zu)", retlen); } else if (unlikely(retlen > orig_len)) { @@ -308,20 +379,23 @@ u8 trim_case_custom(afl_state_t *afl, struct queue_entry *q, u8 *in_buf, unsuccessful trimming and skip it, instead of aborting the trimming. */ ++afl->trim_execs; - goto unsuccessful_trimming; } - write_to_testcase(afl, retbuf, retlen); + if (likely(retlen)) { - fault = fuzz_run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout); - ++afl->trim_execs; + write_to_testcase(afl, retbuf, retlen); - if (afl->stop_soon || fault == FSRV_RUN_ERROR) { goto abort_trimming; } + fault = fuzz_run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout); + ++afl->trim_execs; + + if (afl->stop_soon || fault == FSRV_RUN_ERROR) { goto abort_trimming; } - cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); + cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); + + } - if (cksum == q->exec_cksum) { + if (likely(retlen && cksum == q->exec_cksum)) { q->len = retlen; memcpy(in_buf, retbuf, retlen); @@ -342,26 +416,28 @@ u8 trim_case_custom(afl_state_t *afl, struct queue_entry *q, u8 *in_buf, if (afl->not_on_tty && afl->debug) { - SAYF("[Custom Trimming] SUCCESS: %d/%d iterations (now at %u bytes)", + SAYF("[Custom Trimming] SUCCESS: %u/%u iterations (now at %u bytes)", afl->stage_cur, afl->stage_max, q->len); } } else { - unsuccessful_trimming: - /* Tell the custom mutator that the trimming was unsuccessful */ - afl->stage_cur = mutator->afl_custom_post_trim(mutator->data, 0); - if (unlikely(afl->stage_cur < 0)) { + s32 retval2 = mutator->afl_custom_post_trim(mutator->data, 0); + if (unlikely(retval2 < 0)) { + + FATAL("Error ret in custom_post_trim: %d", retval2); + + } else { - FATAL("Error ret in custom_post_trim: %d", afl->stage_cur); + afl->stage_cur = retval2; } if (afl->not_on_tty && afl->debug) { - SAYF("[Custom Trimming] FAILURE: %d/%d iterations", afl->stage_cur, + SAYF("[Custom Trimming] FAILURE: %u/%u iterations", afl->stage_cur, afl->stage_max); } diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 1f0bf30e..c73e394a 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -26,14 +26,13 @@ #include "afl-fuzz.h" #include <string.h> #include <limits.h> +#include "cmplog.h" /* MOpt */ -static int select_algorithm(afl_state_t *afl) { +static int select_algorithm(afl_state_t *afl, u32 max_algorithm) { - int i_puppet, j_puppet = 0, operator_number = operator_num; - - if (!afl->extras_cnt && !afl->a_extras_cnt) operator_number -= 2; + int i_puppet, j_puppet = 0, operator_number = max_algorithm; double range_sele = (double)afl->probability_now[afl->swarm_now][operator_number - 1]; @@ -77,7 +76,7 @@ static int select_algorithm(afl_state_t *afl) { static u32 choose_block_len(afl_state_t *afl, u32 limit) { u32 min_value, max_value; - u32 rlim = MIN(afl->queue_cycle, 3); + u32 rlim = MIN(afl->queue_cycle, (u32)3); if (unlikely(!afl->run_over10m)) { rlim = 1; } @@ -95,7 +94,7 @@ static u32 choose_block_len(afl_state_t *afl, u32 limit) { default: - if (rand_below(afl, 10)) { + if (likely(rand_below(afl, 10))) { min_value = HAVOC_BLK_MEDIUM; max_value = HAVOC_BLK_LARGE; @@ -167,7 +166,7 @@ static u8 could_be_arith(u32 old_val, u32 new_val, u8 blen) { /* See if one-byte adjustments to any byte could produce this result. */ - for (i = 0; i < blen; ++i) { + for (i = 0; (u8)i < blen; ++i) { u8 a = old_val >> (8 * i), b = new_val >> (8 * i); @@ -195,7 +194,7 @@ static u8 could_be_arith(u32 old_val, u32 new_val, u8 blen) { diffs = 0; - for (i = 0; i < blen / 2; ++i) { + for (i = 0; (u8)i < blen / 2; ++i) { u16 a = old_val >> (16 * i), b = new_val >> (16 * i); @@ -292,7 +291,7 @@ static u8 could_be_interest(u32 old_val, u32 new_val, u8 blen, u8 check_le) { /* See if two-byte insertions over old_val could give us new_val. */ - for (i = 0; i < blen - 1; ++i) { + for (i = 0; (u8)i < blen - 1; ++i) { for (j = 0; j < sizeof(interesting_16) / 2; ++j) { @@ -364,15 +363,15 @@ static void locate_diffs(u8 *ptr1, u8 *ptr2, u32 len, s32 *first, s32 *last) { #endif /* !IGNORE_FINDS */ -#define BUF_PARAMS(name) (void **)&afl->name##_buf, &afl->name##_size - /* Take the current entry from the queue, fuzz it for a while. This function is a tad too long... returns 0 if fuzzed successfully, 1 if skipped or bailed out. */ u8 fuzz_one_original(afl_state_t *afl) { - s32 len, fd, temp_len, i, j; + u32 len, temp_len; + u32 j; + u32 i; u8 *in_buf, *out_buf, *orig_in, *ex_tmp, *eff_map = 0; u64 havoc_queued = 0, orig_hit_cnt, new_hit_cnt = 0, prev_cksum; u32 splice_cycle = 0, perf_score = 100, orig_perf, eff_cnt = 1; @@ -382,9 +381,6 @@ u8 fuzz_one_original(afl_state_t *afl) { u8 a_collect[MAX_AUTO_EXTRA]; u32 a_len = 0; -/* Not pretty, but saves a lot of writing */ -#define BUF_PARAMS(name) (void **)&afl->name##_buf, &afl->name##_size - #ifdef IGNORE_FINDS /* In IGNORE_FINDS mode, skip any entries that weren't in the @@ -419,7 +415,7 @@ u8 fuzz_one_original(afl_state_t *afl) { if (((afl->queue_cur->was_fuzzed > 0 || afl->queue_cur->fuzz_level > 0) || !afl->queue_cur->favored) && - rand_below(afl, 100) < SKIP_TO_NEW_PROB) { + likely(rand_below(afl, 100) < SKIP_TO_NEW_PROB)) { return 1; @@ -436,11 +432,11 @@ u8 fuzz_one_original(afl_state_t *afl) { if (afl->queue_cycle > 1 && (afl->queue_cur->fuzz_level == 0 || afl->queue_cur->was_fuzzed)) { - if (rand_below(afl, 100) < SKIP_NFAV_NEW_PROB) { return 1; } + if (likely(rand_below(afl, 100) < SKIP_NFAV_NEW_PROB)) { return 1; } } else { - if (rand_below(afl, 100) < SKIP_NFAV_OLD_PROB) { return 1; } + if (likely(rand_below(afl, 100) < SKIP_NFAV_OLD_PROB)) { return 1; } } @@ -450,39 +446,22 @@ u8 fuzz_one_original(afl_state_t *afl) { if (unlikely(afl->not_on_tty)) { - ACTF("Fuzzing test case #%u (%u total, %llu uniq crashes found)...", - afl->current_entry, afl->queued_paths, afl->unique_crashes); + ACTF( + "Fuzzing test case #%u (%u total, %llu uniq crashes found, " + "perf_score=%0.0f, exec_us=%llu, hits=%u, map=%u)...", + afl->current_entry, afl->queued_paths, afl->unique_crashes, + afl->queue_cur->perf_score, afl->queue_cur->exec_us, + likely(afl->n_fuzz) ? afl->n_fuzz[afl->queue_cur->n_fuzz_entry] : 0, + afl->queue_cur->bitmap_size); fflush(stdout); } - /* Map the test case into memory. */ - - fd = open(afl->queue_cur->fname, O_RDONLY); - - if (unlikely(fd < 0)) { - - PFATAL("Unable to open '%s'", afl->queue_cur->fname); - - } - + orig_in = in_buf = queue_testcase_get(afl, afl->queue_cur); len = afl->queue_cur->len; - orig_in = in_buf = mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); - - if (unlikely(orig_in == MAP_FAILED)) { - - PFATAL("Unable to mmap '%s' with len %d", afl->queue_cur->fname, len); - - } - - close(fd); - - /* We could mmap() out_buf as MAP_PRIVATE, but we end up clobbering every - single byte anyway, so it wouldn't give us any performance or memory usage - benefits. */ - - out_buf = ck_maybe_grow(BUF_PARAMS(out), len); + out_buf = afl_realloc(AFL_BUF_PARAM(out), len); + if (unlikely(!out_buf)) { PFATAL("alloc"); } afl->subseq_tmouts = 0; @@ -524,10 +503,13 @@ u8 fuzz_one_original(afl_state_t *afl) { * TRIMMING * ************/ - if (!afl->non_instrumented_mode && !afl->queue_cur->trim_done && - !afl->disable_trim) { + if (unlikely(!afl->non_instrumented_mode && !afl->queue_cur->trim_done && + !afl->disable_trim)) { + + u32 old_len = afl->queue_cur->len; u8 res = trim_case(afl, afl->queue_cur, in_buf); + orig_in = in_buf = queue_testcase_get(afl, afl->queue_cur); if (unlikely(res == FSRV_RUN_ERROR)) { @@ -548,6 +530,9 @@ u8 fuzz_one_original(afl_state_t *afl) { len = afl->queue_cur->len; + /* maybe current entry is not ready for splicing anymore */ + if (unlikely(len <= 4 && old_len > 4)) --afl->ready_for_splicing_count; + } memcpy(out_buf, in_buf, len); @@ -556,16 +541,36 @@ u8 fuzz_one_original(afl_state_t *afl) { * PERFORMANCE SCORE * *********************/ - orig_perf = perf_score = calculate_score(afl, afl->queue_cur); + if (likely(!afl->old_seed_selection)) + orig_perf = perf_score = afl->queue_cur->perf_score; + else + afl->queue_cur->perf_score = orig_perf = perf_score = + calculate_score(afl, afl->queue_cur); - if (unlikely(perf_score == 0)) { goto abandon_entry; } + if (unlikely(perf_score <= 0)) { goto abandon_entry; } - if (afl->shm.cmplog_mode && !afl->queue_cur->fully_colorized) { + if (unlikely(afl->shm.cmplog_mode && + afl->queue_cur->colorized < afl->cmplog_lvl && + (u32)len <= afl->cmplog_max_filesize)) { - if (input_to_state_stage(afl, in_buf, out_buf, len, - afl->queue_cur->exec_cksum)) { + if (unlikely(len < 4)) { - goto abandon_entry; + afl->queue_cur->colorized = CMPLOG_LVL_MAX; + + } else { + + if (afl->cmplog_lvl == 3 || + (afl->cmplog_lvl == 2 && afl->queue_cur->tc_ref) || + !(afl->fsrv.total_execs % afl->queued_paths) || + get_cur_time() - afl->last_path_time > 300000) { + + if (input_to_state_stage(afl, in_buf, out_buf, len)) { + + goto abandon_entry; + + } + + } } @@ -589,8 +594,9 @@ u8 fuzz_one_original(afl_state_t *afl) { /* Skip deterministic fuzzing if exec path checksum puts this out of scope for this main instance. */ - if (afl->main_node_max && (afl->queue_cur->exec_cksum % afl->main_node_max) != - afl->main_node_id - 1) { + if (unlikely(afl->main_node_max && + (afl->queue_cur->exec_cksum % afl->main_node_max) != + afl->main_node_id - 1)) { goto custom_mutator_stage; @@ -629,6 +635,11 @@ u8 fuzz_one_original(afl_state_t *afl) { FLIP_BIT(out_buf, afl->stage_cur); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s FLIP_BIT1-%u", + afl->queue_cur->fname, afl->stage_cur); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } FLIP_BIT(out_buf, afl->stage_cur); @@ -679,7 +690,7 @@ u8 fuzz_one_original(afl_state_t *afl) { if (a_len >= MIN_AUTO_EXTRA && a_len <= MAX_AUTO_EXTRA) { - maybe_add_auto((u8 *)afl, a_collect, a_len); + maybe_add_auto(afl, a_collect, a_len); } @@ -690,7 +701,7 @@ u8 fuzz_one_original(afl_state_t *afl) { if (a_len >= MIN_AUTO_EXTRA && a_len <= MAX_AUTO_EXTRA) { - maybe_add_auto((u8 *)afl, a_collect, a_len); + maybe_add_auto(afl, a_collect, a_len); } @@ -738,6 +749,11 @@ u8 fuzz_one_original(afl_state_t *afl) { FLIP_BIT(out_buf, afl->stage_cur); FLIP_BIT(out_buf, afl->stage_cur + 1); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s FLIP_BIT2-%u", + afl->queue_cur->fname, afl->stage_cur); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } FLIP_BIT(out_buf, afl->stage_cur); @@ -767,6 +783,11 @@ u8 fuzz_one_original(afl_state_t *afl) { FLIP_BIT(out_buf, afl->stage_cur + 2); FLIP_BIT(out_buf, afl->stage_cur + 3); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s FLIP_BIT4-%u", + afl->queue_cur->fname, afl->stage_cur); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } FLIP_BIT(out_buf, afl->stage_cur); @@ -797,7 +818,8 @@ u8 fuzz_one_original(afl_state_t *afl) { /* Initialize effector map for the next step (see comments below). Always flag first and last byte as doing something. */ - eff_map = ck_maybe_grow(BUF_PARAMS(eff), EFF_ALEN(len)); + eff_map = afl_realloc(AFL_BUF_PARAM(eff), EFF_ALEN(len)); + if (unlikely(!eff_map)) { PFATAL("alloc"); } eff_map[0] = 1; if (EFF_APOS(len - 1) != 0) { @@ -821,6 +843,11 @@ u8 fuzz_one_original(afl_state_t *afl) { out_buf[afl->stage_cur] ^= 0xFF; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s FLIP_BIT8-%u", + afl->queue_cur->fname, afl->stage_cur); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } /* We also use this stage to pull off a simple trick: we identify @@ -862,7 +889,7 @@ u8 fuzz_one_original(afl_state_t *afl) { whole thing as worth fuzzing, since we wouldn't be saving much time anyway. */ - if (eff_cnt != EFF_ALEN(len) && + if (eff_cnt != (u32)EFF_ALEN(len) && eff_cnt * 100 / EFF_ALEN(len) > EFF_MAX_PERC) { memset(eff_map, 1, EFF_ALEN(len)); @@ -908,6 +935,11 @@ u8 fuzz_one_original(afl_state_t *afl) { *(u16 *)(out_buf + i) ^= 0xFFFF; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s FLIP_BIT16-%u", + afl->queue_cur->fname, afl->stage_cur); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -946,6 +978,11 @@ u8 fuzz_one_original(afl_state_t *afl) { *(u32 *)(out_buf + i) ^= 0xFFFFFFFF; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s FLIP_BIT32-%u", + afl->queue_cur->fname, afl->stage_cur); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -977,7 +1014,7 @@ skip_bitflip: orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len; ++i) { + for (i = 0; i < (u32)len; ++i) { u8 orig = out_buf[i]; @@ -1004,6 +1041,11 @@ skip_bitflip: afl->stage_cur_val = j; out_buf[i] = orig + j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH8+-%u-%u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1020,6 +1062,11 @@ skip_bitflip: afl->stage_cur_val = -j; out_buf[i] = orig - j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH8--%u-%u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1051,7 +1098,7 @@ skip_bitflip: orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len - 1; ++i) { + for (i = 0; i < (u32)len - 1; ++i) { u16 orig = *(u16 *)(out_buf + i); @@ -1084,6 +1131,11 @@ skip_bitflip: afl->stage_cur_val = j; *(u16 *)(out_buf + i) = orig + j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH16+-%u-%u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1098,6 +1150,11 @@ skip_bitflip: afl->stage_cur_val = -j; *(u16 *)(out_buf + i) = orig - j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH16--%u-%u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1116,6 +1173,11 @@ skip_bitflip: afl->stage_cur_val = j; *(u16 *)(out_buf + i) = SWAP16(SWAP16(orig) + j); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH16+BE-%u-%u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1130,6 +1192,11 @@ skip_bitflip: afl->stage_cur_val = -j; *(u16 *)(out_buf + i) = SWAP16(SWAP16(orig) - j); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH16_BE-%u-%u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1161,7 +1228,7 @@ skip_bitflip: orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len - 3; ++i) { + for (i = 0; i < (u32)len - 3; ++i) { u32 orig = *(u32 *)(out_buf + i); @@ -1193,6 +1260,11 @@ skip_bitflip: afl->stage_cur_val = j; *(u32 *)(out_buf + i) = orig + j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH32+-%u-%u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1202,11 +1274,16 @@ skip_bitflip: } - if ((orig & 0xffff) < j && !could_be_bitflip(r2)) { + if ((orig & 0xffff) < (u32)j && !could_be_bitflip(r2)) { afl->stage_cur_val = -j; *(u32 *)(out_buf + i) = orig - j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH32_-%u-%u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1225,6 +1302,11 @@ skip_bitflip: afl->stage_cur_val = j; *(u32 *)(out_buf + i) = SWAP32(SWAP32(orig) + j); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH32+BE-%u-%u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1234,11 +1316,16 @@ skip_bitflip: } - if ((SWAP32(orig) & 0xffff) < j && !could_be_bitflip(r4)) { + if ((SWAP32(orig) & 0xffff) < (u32)j && !could_be_bitflip(r4)) { afl->stage_cur_val = -j; *(u32 *)(out_buf + i) = SWAP32(SWAP32(orig) - j); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH32_BE-%u-%u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1276,7 +1363,7 @@ skip_arith: /* Setting 8-bit integers. */ - for (i = 0; i < len; ++i) { + for (i = 0; i < (u32)len; ++i) { u8 orig = out_buf[i]; @@ -1291,7 +1378,7 @@ skip_arith: afl->stage_cur_byte = i; - for (j = 0; j < sizeof(interesting_8); ++j) { + for (j = 0; j < (u32)sizeof(interesting_8); ++j) { /* Skip if the value could be a product of bitflips or arithmetics. */ @@ -1306,6 +1393,11 @@ skip_arith: afl->stage_cur_val = interesting_8[j]; out_buf[i] = interesting_8[j]; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s INTERESTING8_%u_%u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } out_buf[i] = orig; @@ -1361,6 +1453,11 @@ skip_arith: *(u16 *)(out_buf + i) = interesting_16[j]; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s INTERESTING16_%u_%u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1377,6 +1474,11 @@ skip_arith: afl->stage_val_type = STAGE_VAL_BE; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s INTERESTING16BE_%u_%u", afl->queue_cur->fname, i, j); +#endif + *(u16 *)(out_buf + i) = SWAP16(interesting_16[j]); if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1440,6 +1542,11 @@ skip_arith: *(u32 *)(out_buf + i) = interesting_32[j]; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s INTERESTING32_%u_%u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1456,6 +1563,11 @@ skip_arith: afl->stage_val_type = STAGE_VAL_BE; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s INTERESTING32BE_%u_%u", afl->queue_cur->fname, i, j); +#endif + *(u32 *)(out_buf + i) = SWAP32(interesting_32[j]); if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1496,7 +1608,7 @@ skip_interest: orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len; ++i) { + for (i = 0; i < (u32)len; ++i) { u32 last_len = 0; @@ -1509,13 +1621,13 @@ skip_interest: for (j = 0; j < afl->extras_cnt; ++j) { - /* Skip extras probabilistically if afl->extras_cnt > MAX_DET_EXTRAS. Also - skip them if there's no room to insert the payload, if the token + /* Skip extras probabilistically if afl->extras_cnt > AFL_MAX_DET_EXTRAS. + Also skip them if there's no room to insert the payload, if the token is redundant, or if its entire span has no bytes set in the effector map. */ - if ((afl->extras_cnt > MAX_DET_EXTRAS && - rand_below(afl, afl->extras_cnt) >= MAX_DET_EXTRAS) || + if ((afl->extras_cnt > afl->max_det_extras && + rand_below(afl, afl->extras_cnt) >= afl->max_det_extras) || afl->extras[j].len > len - i || !memcmp(afl->extras[j].data, out_buf + i, afl->extras[j].len) || !memchr(eff_map + EFF_APOS(i), 1, @@ -1529,6 +1641,11 @@ skip_interest: last_len = afl->extras[j].len; memcpy(out_buf + i, afl->extras[j].data, last_len); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s EXTRAS_overwrite-%u-%u", afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1554,9 +1671,10 @@ skip_interest: orig_hit_cnt = new_hit_cnt; - ex_tmp = ck_maybe_grow(BUF_PARAMS(ex), len + MAX_DICT_FILE); + ex_tmp = afl_realloc(AFL_BUF_PARAM(ex), len + MAX_DICT_FILE); + if (unlikely(!ex_tmp)) { PFATAL("alloc"); } - for (i = 0; i <= len; ++i) { + for (i = 0; i <= (u32)len; ++i) { afl->stage_cur_byte = i; @@ -1575,6 +1693,11 @@ skip_interest: /* Copy tail */ memcpy(ex_tmp + i + afl->extras[j].len, out_buf + i, len - i); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s EXTRAS_insert-%u-%u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, ex_tmp, len + afl->extras[j].len)) { goto abandon_entry; @@ -1602,19 +1725,20 @@ skip_user_extras: afl->stage_name = "auto extras (over)"; afl->stage_short = "ext_AO"; afl->stage_cur = 0; - afl->stage_max = MIN(afl->a_extras_cnt, USE_AUTO_EXTRAS) * len; + afl->stage_max = MIN(afl->a_extras_cnt, (u32)USE_AUTO_EXTRAS) * len; afl->stage_val_type = STAGE_VAL_NONE; orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len; ++i) { + for (i = 0; i < (u32)len; ++i) { u32 last_len = 0; afl->stage_cur_byte = i; - for (j = 0; j < MIN(afl->a_extras_cnt, USE_AUTO_EXTRAS); ++j) { + u32 min_extra_len = MIN(afl->a_extras_cnt, (u32)USE_AUTO_EXTRAS); + for (j = 0; j < min_extra_len; ++j) { /* See the comment in the earlier code; extras are sorted by size. */ @@ -1631,6 +1755,11 @@ skip_user_extras: last_len = afl->a_extras[j].len; memcpy(out_buf + i, afl->a_extras[j].data, last_len); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s AUTO_EXTRAS_overwrite-%u-%u", afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1670,109 +1799,117 @@ custom_mutator_stage: if (afl->stage_max < HAVOC_MIN) { afl->stage_max = HAVOC_MIN; } - const u32 max_seed_size = MAX_FILE; + const u32 max_seed_size = MAX_FILE, saved_max = afl->stage_max; orig_hit_cnt = afl->queued_paths + afl->unique_crashes; +#ifdef INTROSPECTION + afl->mutation[0] = 0; +#endif + LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { if (el->afl_custom_fuzz) { - has_custom_fuzz = true; + afl->current_custom_fuzz = el; - for (afl->stage_cur = 0; afl->stage_cur < afl->stage_max; - ++afl->stage_cur) { + if (el->afl_custom_fuzz_count) { - struct queue_entry *target; - u32 tid; - u8 * new_buf; + afl->stage_max = el->afl_custom_fuzz_count(el->data, out_buf, len); - retry_external_pick: - /* Pick a random other queue entry for passing to external API */ + } else { - do { + afl->stage_max = saved_max; - tid = rand_below(afl, afl->queued_paths); + } - } while (tid == afl->current_entry && afl->queued_paths > 1); + has_custom_fuzz = true; - target = afl->queue; + afl->stage_short = el->name_short; - while (tid >= 100) { + if (afl->stage_max) { - target = target->next_100; - tid -= 100; + for (afl->stage_cur = 0; afl->stage_cur < afl->stage_max; + ++afl->stage_cur) { - } + struct queue_entry *target = NULL; + u32 tid; + u8 * new_buf = NULL; + u32 target_len = 0; - while (tid--) { + /* check if splicing makes sense yet (enough entries) */ + if (likely(afl->ready_for_splicing_count > 1)) { - target = target->next; + /* Pick a random other queue entry for passing to external API + that has the necessary length */ - } + do { - /* Make sure that the target has a reasonable length. */ + tid = rand_below(afl, afl->queued_paths); - while (target && (target->len < 2 || target == afl->queue_cur) && - afl->queued_paths > 3) { + } while (unlikely(tid == afl->current_entry || - target = target->next; - ++afl->splicing_with; + afl->queue_buf[tid]->len < 4)); - } + target = afl->queue_buf[tid]; + afl->splicing_with = tid; - if (!target) { goto retry_external_pick; } + /* Read the additional testcase into a new buffer. */ + new_buf = queue_testcase_get(afl, target); + target_len = target->len; - /* Read the additional testcase into a new buffer. */ - fd = open(target->fname, O_RDONLY); - if (unlikely(fd < 0)) { PFATAL("Unable to open '%s'", target->fname); } + } - new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), target->len); - ck_read(fd, new_buf, target->len, target->fname); - close(fd); + u8 *mutated_buf = NULL; - u8 *mutated_buf = NULL; + size_t mutated_size = + el->afl_custom_fuzz(el->data, out_buf, len, &mutated_buf, new_buf, + target_len, max_seed_size); - size_t mutated_size = - el->afl_custom_fuzz(el->data, out_buf, len, &mutated_buf, new_buf, - target->len, max_seed_size); + if (unlikely(!mutated_buf)) { - if (unlikely(!mutated_buf)) { + FATAL("Error in custom_fuzz. Size returned: %zu", mutated_size); - FATAL("Error in custom_fuzz. Size returned: %zd", mutated_size); + } - } + if (mutated_size > 0) { - if (mutated_size > 0) { + if (common_fuzz_stuff(afl, mutated_buf, (u32)mutated_size)) { - if (common_fuzz_stuff(afl, mutated_buf, (u32)mutated_size)) { + goto abandon_entry; - goto abandon_entry; + } - } + if (!el->afl_custom_fuzz_count) { - /* If we're finding new stuff, let's run for a bit longer, limits - permitting. */ + /* If we're finding new stuff, let's run for a bit longer, limits + permitting. */ - if (afl->queued_paths != havoc_queued) { + if (afl->queued_paths != havoc_queued) { - if (perf_score <= afl->havoc_max_mult * 100) { + if (perf_score <= afl->havoc_max_mult * 100) { - afl->stage_max *= 2; - perf_score *= 2; + afl->stage_max *= 2; + perf_score *= 2; - } + } - havoc_queued = afl->queued_paths; + havoc_queued = afl->queued_paths; + + } + + } } - } + /* `(afl->)out_buf` may have been changed by the call to custom_fuzz + */ + /* TODO: Only do this when `mutated_buf` == `out_buf`? Branch vs + * Memcpy. + */ + memcpy(out_buf, in_buf, len); - /* `(afl->)out_buf` may have been changed by the call to custom_fuzz */ - /* TODO: Only do this when `mutated_buf` == `out_buf`? Branch vs Memcpy. - */ - memcpy(out_buf, in_buf, len); + } } @@ -1780,6 +1917,8 @@ custom_mutator_stage: }); + afl->current_custom_fuzz = NULL; + if (!has_custom_fuzz) goto havoc_stage; new_hit_cnt = afl->queued_paths + afl->unique_crashes; @@ -1860,25 +1999,37 @@ havoc_stage: u32 r_max, r; - if (unlikely(afl->expand_havoc)) { + r_max = 15 + ((afl->extras_cnt + afl->a_extras_cnt) ? 2 : 0); + + if (unlikely(afl->expand_havoc && afl->ready_for_splicing_count > 1)) { /* add expensive havoc cases here, they are activated after a full cycle without finds happened */ - r_max = 16 + ((afl->extras_cnt + afl->a_extras_cnt) ? 2 : 0); + r_max++; - } else { + } + + if (unlikely(get_cur_time() - afl->last_path_time > 5000 && + afl->ready_for_splicing_count > 1)) { + + /* add expensive havoc cases here if there is no findings in the last 5s */ - r_max = 15 + ((afl->extras_cnt + afl->a_extras_cnt) ? 2 : 0); + r_max++; } for (afl->stage_cur = 0; afl->stage_cur < afl->stage_max; ++afl->stage_cur) { - u32 use_stacking = 1 << (1 + rand_below(afl, HAVOC_STACK_POW2)); + u32 use_stacking = 1 << (1 + rand_below(afl, afl->havoc_stack_pow2)); afl->stage_cur_val = use_stacking; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s HAVOC-%u", + afl->queue_cur->fname, use_stacking); +#endif + for (i = 0; i < use_stacking; ++i) { if (afl->custom_mutators_count) { @@ -1893,7 +2044,7 @@ havoc_stage: el->data, out_buf, temp_len, &custom_havoc_buf, MAX_FILE); if (unlikely(!custom_havoc_buf)) { - FATAL("Error in custom_havoc (return %zd)", new_len); + FATAL("Error in custom_havoc (return %zu)", new_len); } @@ -1902,7 +2053,8 @@ havoc_stage: temp_len = new_len; if (out_buf != custom_havoc_buf) { - ck_maybe_grow(BUF_PARAMS(out), temp_len); + afl_realloc(AFL_BUF_PARAM(out), temp_len); + if (unlikely(!afl->out_buf)) { PFATAL("alloc"); } memcpy(out_buf, custom_havoc_buf, temp_len); } @@ -1921,6 +2073,10 @@ havoc_stage: /* Flip a single bit somewhere. Spooky! */ +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP_BIT1"); + strcat(afl->mutation, afl->m_tmp); +#endif FLIP_BIT(out_buf, rand_below(afl, temp_len << 3)); break; @@ -1928,6 +2084,10 @@ havoc_stage: /* Set byte to interesting value. */ +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING8"); + strcat(afl->mutation, afl->m_tmp); +#endif out_buf[rand_below(afl, temp_len)] = interesting_8[rand_below(afl, sizeof(interesting_8))]; break; @@ -1940,11 +2100,19 @@ havoc_stage: if (rand_below(afl, 2)) { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING16"); + strcat(afl->mutation, afl->m_tmp); +#endif *(u16 *)(out_buf + rand_below(afl, temp_len - 1)) = interesting_16[rand_below(afl, sizeof(interesting_16) >> 1)]; } else { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING16BE"); + strcat(afl->mutation, afl->m_tmp); +#endif *(u16 *)(out_buf + rand_below(afl, temp_len - 1)) = SWAP16( interesting_16[rand_below(afl, sizeof(interesting_16) >> 1)]); @@ -1960,11 +2128,19 @@ havoc_stage: if (rand_below(afl, 2)) { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING32"); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + rand_below(afl, temp_len - 3)) = interesting_32[rand_below(afl, sizeof(interesting_32) >> 2)]; } else { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING32BE"); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + rand_below(afl, temp_len - 3)) = SWAP32( interesting_32[rand_below(afl, sizeof(interesting_32) >> 2)]); @@ -1976,6 +2152,10 @@ havoc_stage: /* Randomly subtract from byte. */ +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH8_"); + strcat(afl->mutation, afl->m_tmp); +#endif out_buf[rand_below(afl, temp_len)] -= 1 + rand_below(afl, ARITH_MAX); break; @@ -1983,6 +2163,10 @@ havoc_stage: /* Randomly add to byte. */ +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH8+"); + strcat(afl->mutation, afl->m_tmp); +#endif out_buf[rand_below(afl, temp_len)] += 1 + rand_below(afl, ARITH_MAX); break; @@ -1996,6 +2180,10 @@ havoc_stage: u32 pos = rand_below(afl, temp_len - 1); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16_-%u", pos); + strcat(afl->mutation, afl->m_tmp); +#endif *(u16 *)(out_buf + pos) -= 1 + rand_below(afl, ARITH_MAX); } else { @@ -2003,6 +2191,11 @@ havoc_stage: u32 pos = rand_below(afl, temp_len - 1); u16 num = 1 + rand_below(afl, ARITH_MAX); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16_BE-%u_%u", pos, + num); + strcat(afl->mutation, afl->m_tmp); +#endif *(u16 *)(out_buf + pos) = SWAP16(SWAP16(*(u16 *)(out_buf + pos)) - num); @@ -2020,6 +2213,10 @@ havoc_stage: u32 pos = rand_below(afl, temp_len - 1); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16+-%u", pos); + strcat(afl->mutation, afl->m_tmp); +#endif *(u16 *)(out_buf + pos) += 1 + rand_below(afl, ARITH_MAX); } else { @@ -2027,6 +2224,11 @@ havoc_stage: u32 pos = rand_below(afl, temp_len - 1); u16 num = 1 + rand_below(afl, ARITH_MAX); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16+BE-%u_%u", pos, + num); + strcat(afl->mutation, afl->m_tmp); +#endif *(u16 *)(out_buf + pos) = SWAP16(SWAP16(*(u16 *)(out_buf + pos)) + num); @@ -2044,6 +2246,10 @@ havoc_stage: u32 pos = rand_below(afl, temp_len - 3); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32_-%u", pos); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + pos) -= 1 + rand_below(afl, ARITH_MAX); } else { @@ -2051,6 +2257,11 @@ havoc_stage: u32 pos = rand_below(afl, temp_len - 3); u32 num = 1 + rand_below(afl, ARITH_MAX); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32_BE-%u-%u", pos, + num); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + pos) = SWAP32(SWAP32(*(u32 *)(out_buf + pos)) - num); @@ -2068,6 +2279,10 @@ havoc_stage: u32 pos = rand_below(afl, temp_len - 3); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32+-%u", pos); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + pos) += 1 + rand_below(afl, ARITH_MAX); } else { @@ -2075,6 +2290,11 @@ havoc_stage: u32 pos = rand_below(afl, temp_len - 3); u32 num = 1 + rand_below(afl, ARITH_MAX); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32+BE-%u-%u", pos, + num); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + pos) = SWAP32(SWAP32(*(u32 *)(out_buf + pos)) + num); @@ -2088,6 +2308,10 @@ havoc_stage: why not. We use XOR with 1-255 to eliminate the possibility of a no-op. */ +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " RAND8"); + strcat(afl->mutation, afl->m_tmp); +#endif out_buf[rand_below(afl, temp_len)] ^= 1 + rand_below(afl, 255); break; @@ -2107,6 +2331,11 @@ havoc_stage: del_from = rand_below(afl, temp_len - del_len + 1); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " DEL-%u-%u", del_from, + del_len); + strcat(afl->mutation, afl->m_tmp); +#endif memmove(out_buf + del_from, out_buf + del_from + del_len, temp_len - del_from - del_len); @@ -2126,7 +2355,7 @@ havoc_stage: u32 clone_from, clone_to, clone_len; u8 *new_buf; - if (actually_clone) { + if (likely(actually_clone)) { clone_len = choose_block_len(afl, temp_len); clone_from = rand_below(afl, temp_len - clone_len + 1); @@ -2140,8 +2369,15 @@ havoc_stage: clone_to = rand_below(afl, temp_len); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " CLONE-%s-%u-%u-%u", + actually_clone ? "clone" : "insert", clone_from, clone_to, + clone_len); + strcat(afl->mutation, afl->m_tmp); +#endif new_buf = - ck_maybe_grow(BUF_PARAMS(out_scratch), temp_len + clone_len); + afl_realloc(AFL_BUF_PARAM(out_scratch), temp_len + clone_len); + if (unlikely(!new_buf)) { PFATAL("alloc"); } /* Head */ @@ -2149,7 +2385,7 @@ havoc_stage: /* Inserted part */ - if (actually_clone) { + if (likely(actually_clone)) { memcpy(new_buf + clone_to, out_buf + clone_from, clone_len); @@ -2166,9 +2402,8 @@ havoc_stage: memcpy(new_buf + clone_to + clone_len, out_buf + clone_to, temp_len - clone_to); - swap_bufs(BUF_PARAMS(out), BUF_PARAMS(out_scratch)); out_buf = new_buf; - new_buf = NULL; + afl_swap_bufs(AFL_BUF_PARAM(out), AFL_BUF_PARAM(out_scratch)); temp_len += clone_len; } @@ -2189,16 +2424,27 @@ havoc_stage: copy_from = rand_below(afl, temp_len - copy_len + 1); copy_to = rand_below(afl, temp_len - copy_len + 1); - if (rand_below(afl, 4)) { + if (likely(rand_below(afl, 4))) { - if (copy_from != copy_to) { + if (likely(copy_from != copy_to)) { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " OVERWRITE_COPY-%u-%u-%u", copy_from, copy_to, + copy_len); + strcat(afl->mutation, afl->m_tmp); +#endif memmove(out_buf + copy_to, out_buf + copy_from, copy_len); } } else { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " OVERWRITE_FIXED-%u-%u-%u", copy_from, copy_to, copy_len); + strcat(afl->mutation, afl->m_tmp); +#endif memset(out_buf + copy_to, rand_below(afl, 2) ? rand_below(afl, 256) : out_buf[rand_below(afl, temp_len)], @@ -2229,11 +2475,15 @@ havoc_stage: u32 use_extra = rand_below(afl, afl->a_extras_cnt); u32 extra_len = afl->a_extras[use_extra].len; - u32 insert_at; if (extra_len > temp_len) { break; } - insert_at = rand_below(afl, temp_len - extra_len + 1); + u32 insert_at = rand_below(afl, temp_len - extra_len + 1); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " AUTO_EXTRA_OVERWRITE-%u-%u", insert_at, extra_len); + strcat(afl->mutation, afl->m_tmp); +#endif memcpy(out_buf + insert_at, afl->a_extras[use_extra].data, extra_len); @@ -2243,11 +2493,15 @@ havoc_stage: u32 use_extra = rand_below(afl, afl->extras_cnt); u32 extra_len = afl->extras[use_extra].len; - u32 insert_at; if (extra_len > temp_len) { break; } - insert_at = rand_below(afl, temp_len - extra_len + 1); + u32 insert_at = rand_below(afl, temp_len - extra_len + 1); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " EXTRA_OVERWRITE-%u-%u", insert_at, extra_len); + strcat(afl->mutation, afl->m_tmp); +#endif memcpy(out_buf + insert_at, afl->extras[use_extra].data, extra_len); @@ -2270,18 +2524,29 @@ havoc_stage: use_extra = rand_below(afl, afl->a_extras_cnt); extra_len = afl->a_extras[use_extra].len; ptr = afl->a_extras[use_extra].data; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " AUTO_EXTRA_INSERT-%u-%u", insert_at, extra_len); + strcat(afl->mutation, afl->m_tmp); +#endif } else { use_extra = rand_below(afl, afl->extras_cnt); extra_len = afl->extras[use_extra].len; ptr = afl->extras[use_extra].data; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " EXTRA_INSERT-%u-%u", + insert_at, extra_len); + strcat(afl->mutation, afl->m_tmp); +#endif } if (temp_len + extra_len >= MAX_FILE) { break; } - out_buf = ck_maybe_grow(BUF_PARAMS(out), temp_len + extra_len); + out_buf = afl_realloc(AFL_BUF_PARAM(out), temp_len + extra_len); + if (unlikely(!out_buf)) { PFATAL("alloc"); } /* Tail */ memmove(out_buf + insert_at + extra_len, out_buf + insert_at, @@ -2308,54 +2573,24 @@ havoc_stage: /* Overwrite bytes with a randomly selected chunk from another testcase or insert that chunk. */ - if (afl->queued_paths < 4) break; - /* Pick a random queue entry and seek to it. */ u32 tid; - do - tid = rand_below(afl, afl->queued_paths); - while (tid == afl->current_entry); - - struct queue_entry *target = afl->queue_buf[tid]; - - /* Make sure that the target has a reasonable length. */ - - while (target && (target->len < 2 || target == afl->queue_cur)) - target = target->next; - - if (!target) break; - - /* Read the testcase into a new buffer. */ - - fd = open(target->fname, O_RDONLY); - - if (unlikely(fd < 0)) { + do { - PFATAL("Unable to open '%s'", target->fname); - - } - - u32 new_len = target->len; - u8 *new_buf = ck_maybe_grow(BUF_PARAMS(in_scratch), new_len); - - ck_read(fd, new_buf, new_len, target->fname); - - close(fd); + tid = rand_below(afl, afl->queued_paths); - u8 overwrite = 0; - if (temp_len >= 2 && rand_below(afl, 2)) - overwrite = 1; - else if (temp_len + HAVOC_BLK_XL >= MAX_FILE) { + } while (tid == afl->current_entry || afl->queue_buf[tid]->len < 4); - if (temp_len >= 2) - overwrite = 1; - else - break; + /* Get the testcase for splicing. */ + struct queue_entry *target = afl->queue_buf[tid]; + u32 new_len = target->len; + u8 * new_buf = queue_testcase_get(afl, target); - } + if ((temp_len >= 2 && rand_below(afl, 2)) || + temp_len + HAVOC_BLK_XL >= MAX_FILE) { - if (overwrite) { + /* overwrite mode */ u32 copy_from, copy_to, copy_len; @@ -2365,20 +2600,34 @@ havoc_stage: copy_from = rand_below(afl, new_len - copy_len + 1); copy_to = rand_below(afl, temp_len - copy_len + 1); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " SPLICE_OVERWRITE-%u-%u-%u-%s", copy_from, copy_to, + copy_len, target->fname); + strcat(afl->mutation, afl->m_tmp); +#endif memmove(out_buf + copy_to, new_buf + copy_from, copy_len); } else { + /* insert mode */ + u32 clone_from, clone_to, clone_len; clone_len = choose_block_len(afl, new_len); clone_from = rand_below(afl, new_len - clone_len + 1); + clone_to = rand_below(afl, temp_len + 1); - clone_to = rand_below(afl, temp_len); - - u8 *temp_buf = - ck_maybe_grow(BUF_PARAMS(out_scratch), temp_len + clone_len); + u8 *temp_buf = afl_realloc(AFL_BUF_PARAM(out_scratch), + temp_len + clone_len + 1); + if (unlikely(!temp_buf)) { PFATAL("alloc"); } +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " SPLICE_INSERT-%u-%u-%u-%s", clone_from, clone_to, + clone_len, target->fname); + strcat(afl->mutation, afl->m_tmp); +#endif /* Head */ memcpy(temp_buf, out_buf, clone_to); @@ -2391,8 +2640,8 @@ havoc_stage: memcpy(temp_buf + clone_to + clone_len, out_buf + clone_to, temp_len - clone_to); - swap_bufs(BUF_PARAMS(out), BUF_PARAMS(out_scratch)); out_buf = temp_buf; + afl_swap_bufs(AFL_BUF_PARAM(out), AFL_BUF_PARAM(out_scratch)); temp_len += clone_len; } @@ -2412,7 +2661,8 @@ havoc_stage: /* out_buf might have been mangled a bit, so let's restore it to its original size and shape. */ - out_buf = ck_maybe_grow(BUF_PARAMS(out), len); + out_buf = afl_realloc(AFL_BUF_PARAM(out), len); + if (unlikely(!out_buf)) { PFATAL("alloc"); } temp_len = len; memcpy(out_buf, in_buf, len); @@ -2462,7 +2712,7 @@ havoc_stage: retry_splicing: if (afl->use_splicing && splice_cycle++ < SPLICE_CYCLES && - afl->queued_paths > 1 && afl->queue_cur->len > 1) { + afl->ready_for_splicing_count > 1 && afl->queue_cur->len >= 4) { struct queue_entry *target; u32 tid, split_at; @@ -2485,39 +2735,18 @@ retry_splicing: tid = rand_below(afl, afl->queued_paths); - } while (tid == afl->current_entry); + } while (tid == afl->current_entry || afl->queue_buf[tid]->len < 4); + /* Get the testcase */ afl->splicing_with = tid; target = afl->queue_buf[tid]; - - /* Make sure that the target has a reasonable length. */ - - while (target && (target->len < 2 || target == afl->queue_cur)) { - - target = target->next; - ++afl->splicing_with; - - } - - if (!target) { goto retry_splicing; } - - /* Read the testcase into a new buffer. */ - - fd = open(target->fname, O_RDONLY); - - if (unlikely(fd < 0)) { PFATAL("Unable to open '%s'", target->fname); } - - new_buf = ck_maybe_grow(BUF_PARAMS(in_scratch), target->len); - - ck_read(fd, new_buf, target->len, target->fname); - - close(fd); + new_buf = queue_testcase_get(afl, target); /* Find a suitable splicing location, somewhere between the first and the last differing byte. Bail out if the difference is just a single byte or so. */ - locate_diffs(in_buf, new_buf, MIN(len, target->len), &f_diff, &l_diff); + locate_diffs(in_buf, new_buf, MIN(len, (s64)target->len), &f_diff, &l_diff); if (f_diff < 0 || l_diff < 2 || f_diff == l_diff) { goto retry_splicing; } @@ -2528,17 +2757,17 @@ retry_splicing: /* Do the thing. */ len = target->len; - memcpy(new_buf, in_buf, split_at); - swap_bufs(BUF_PARAMS(in), BUF_PARAMS(in_scratch)); - in_buf = new_buf; - - out_buf = ck_maybe_grow(BUF_PARAMS(out), len); + afl->in_scratch_buf = afl_realloc(AFL_BUF_PARAM(in_scratch), len); + memcpy(afl->in_scratch_buf, in_buf, split_at); + memcpy(afl->in_scratch_buf + split_at, new_buf, len - split_at); + in_buf = afl->in_scratch_buf; + afl_swap_bufs(AFL_BUF_PARAM(in), AFL_BUF_PARAM(in_scratch)); + + out_buf = afl_realloc(AFL_BUF_PARAM(out), len); + if (unlikely(!out_buf)) { PFATAL("alloc"); } memcpy(out_buf, in_buf, len); goto custom_mutator_stage; - /* ???: While integrating Python module, the author decided to jump to - python stage, but the reason behind this is not clear.*/ - // goto havoc_stage; } @@ -2555,18 +2784,21 @@ abandon_entry: cycle and have not seen this entry before. */ if (!afl->stop_soon && !afl->queue_cur->cal_failed && - (afl->queue_cur->was_fuzzed == 0 || afl->queue_cur->fuzz_level == 0)) { + (afl->queue_cur->was_fuzzed == 0 || afl->queue_cur->fuzz_level == 0) && + !afl->queue_cur->disabled) { - --afl->pending_not_fuzzed; - afl->queue_cur->was_fuzzed = 1; - if (afl->queue_cur->favored) { --afl->pending_favored; } + if (!afl->queue_cur->was_fuzzed) { - } + --afl->pending_not_fuzzed; + afl->queue_cur->was_fuzzed = 1; + if (afl->queue_cur->favored) { --afl->pending_favored; } - ++afl->queue_cur->fuzz_level; + } - munmap(orig_in, afl->queue_cur->len); + } + ++afl->queue_cur->fuzz_level; + orig_in = NULL; return ret_val; #undef FLIP_BIT @@ -2587,7 +2819,9 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { } - s32 len, fd, temp_len, i, j; + u32 len, temp_len; + u32 i; + u32 j; u8 *in_buf, *out_buf, *orig_in, *ex_tmp, *eff_map = 0; u64 havoc_queued = 0, orig_hit_cnt, new_hit_cnt = 0, cur_ms_lv, prev_cksum; u32 splice_cycle = 0, perf_score = 100, orig_perf, eff_cnt = 1; @@ -2606,13 +2840,14 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { #else - if (afl->pending_favored) { + if (likely(afl->pending_favored)) { /* If we have any favored, non-fuzzed new arrivals in the queue, possibly skip to them at the expense of already-fuzzed or non-favored cases. */ - if ((afl->queue_cur->was_fuzzed || !afl->queue_cur->favored) && + if (((afl->queue_cur->was_fuzzed > 0 || afl->queue_cur->fuzz_level > 0) || + !afl->queue_cur->favored) && rand_below(afl, 100) < SKIP_TO_NEW_PROB) { return 1; @@ -2627,13 +2862,14 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { The odds of skipping stuff are higher for already-fuzzed inputs and lower for never-fuzzed entries. */ - if (afl->queue_cycle > 1 && !afl->queue_cur->was_fuzzed) { + if (afl->queue_cycle > 1 && + (afl->queue_cur->fuzz_level == 0 || afl->queue_cur->was_fuzzed)) { - if (rand_below(afl, 100) < SKIP_NFAV_NEW_PROB) { return 1; } + if (likely(rand_below(afl, 100) < SKIP_NFAV_NEW_PROB)) { return 1; } } else { - if (rand_below(afl, 100) < SKIP_NFAV_OLD_PROB) { return 1; } + if (likely(rand_below(afl, 100) < SKIP_NFAV_OLD_PROB)) { return 1; } } @@ -2650,28 +2886,11 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { } /* Map the test case into memory. */ - - fd = open(afl->queue_cur->fname, O_RDONLY); - - if (fd < 0) { PFATAL("Unable to open '%s'", afl->queue_cur->fname); } - + orig_in = in_buf = queue_testcase_get(afl, afl->queue_cur); len = afl->queue_cur->len; - orig_in = in_buf = mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); - - if (orig_in == MAP_FAILED) { - - PFATAL("Unable to mmap '%s'", afl->queue_cur->fname); - - } - - close(fd); - - /* We could mmap() out_buf as MAP_PRIVATE, but we end up clobbering every - single byte anyway, so it wouldn't give us any performance or memory usage - benefits. */ - - out_buf = ck_maybe_grow(BUF_PARAMS(out), len); + out_buf = afl_realloc(AFL_BUF_PARAM(out), len); + if (unlikely(!out_buf)) { PFATAL("alloc"); } afl->subseq_tmouts = 0; @@ -2681,7 +2900,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { * CALIBRATION (only if failed earlier on) * *******************************************/ - if (afl->queue_cur->cal_failed) { + if (unlikely(afl->queue_cur->cal_failed)) { u8 res = FSRV_RUN_TMOUT; @@ -2713,9 +2932,13 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { * TRIMMING * ************/ - if (!afl->non_instrumented_mode && !afl->queue_cur->trim_done) { + if (unlikely(!afl->non_instrumented_mode && !afl->queue_cur->trim_done && + !afl->disable_trim)) { + + u32 old_len = afl->queue_cur->len; u8 res = trim_case(afl, afl->queue_cur, in_buf); + orig_in = in_buf = queue_testcase_get(afl, afl->queue_cur); if (res == FSRV_RUN_ERROR) { @@ -2736,6 +2959,9 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { len = afl->queue_cur->len; + /* maybe current entry is not ready for splicing anymore */ + if (unlikely(len <= 4 && old_len > 4)) --afl->ready_for_splicing_count; + } memcpy(out_buf, in_buf, len); @@ -2744,14 +2970,35 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { * PERFORMANCE SCORE * *********************/ - orig_perf = perf_score = calculate_score(afl, afl->queue_cur); + if (likely(!afl->old_seed_selection)) + orig_perf = perf_score = afl->queue_cur->perf_score; + else + orig_perf = perf_score = calculate_score(afl, afl->queue_cur); - if (afl->shm.cmplog_mode && !afl->queue_cur->fully_colorized) { + if (unlikely(perf_score <= 0)) { goto abandon_entry; } - if (input_to_state_stage(afl, in_buf, out_buf, len, - afl->queue_cur->exec_cksum)) { + if (unlikely(afl->shm.cmplog_mode && + afl->queue_cur->colorized < afl->cmplog_lvl && + (u32)len <= afl->cmplog_max_filesize)) { - goto abandon_entry; + if (unlikely(len < 4)) { + + afl->queue_cur->colorized = CMPLOG_LVL_MAX; + + } else { + + if (afl->cmplog_lvl == 3 || + (afl->cmplog_lvl == 2 && afl->queue_cur->tc_ref) || + !(afl->fsrv.total_execs % afl->queued_paths) || + get_cur_time() - afl->last_path_time > 300000) { + + if (input_to_state_stage(afl, in_buf, out_buf, len)) { + + goto abandon_entry; + + } + + } } @@ -2761,9 +3008,9 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { cur_ms_lv = get_cur_time(); if (!(afl->key_puppet == 0 && - ((cur_ms_lv - afl->last_path_time < afl->limit_time_puppet) || + ((cur_ms_lv - afl->last_path_time < (u32)afl->limit_time_puppet) || (afl->last_crash_time != 0 && - cur_ms_lv - afl->last_crash_time < afl->limit_time_puppet) || + cur_ms_lv - afl->last_crash_time < (u32)afl->limit_time_puppet) || afl->last_path_time == 0))) { afl->key_puppet = 1; @@ -2775,8 +3022,8 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { this entry ourselves (was_fuzzed), or if it has gone through deterministic testing in earlier, resumed runs (passed_det). */ - if (afl->skip_deterministic || afl->queue_cur->was_fuzzed || - afl->queue_cur->passed_det) { + if (likely(afl->skip_deterministic || afl->queue_cur->was_fuzzed || + afl->queue_cur->passed_det)) { goto havoc_stage; @@ -2785,8 +3032,9 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { /* Skip deterministic fuzzing if exec path checksum puts this out of scope for this main instance. */ - if (afl->main_node_max && (afl->queue_cur->exec_cksum % afl->main_node_max) != - afl->main_node_id - 1) { + if (unlikely(afl->main_node_max && + (afl->queue_cur->exec_cksum % afl->main_node_max) != + afl->main_node_id - 1)) { goto havoc_stage; @@ -2825,6 +3073,10 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { FLIP_BIT(out_buf, afl->stage_cur); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_FLIP_BIT1-%u", + afl->queue_cur->fname, afl->stage_cur); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } FLIP_BIT(out_buf, afl->stage_cur); @@ -2875,7 +3127,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { if (a_len >= MIN_AUTO_EXTRA && a_len <= MAX_AUTO_EXTRA) { - maybe_add_auto((u8 *)afl, a_collect, a_len); + maybe_add_auto(afl, a_collect, a_len); } @@ -2886,7 +3138,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { if (a_len >= MIN_AUTO_EXTRA && a_len <= MAX_AUTO_EXTRA) { - maybe_add_auto((u8 *)afl, a_collect, a_len); + maybe_add_auto(afl, a_collect, a_len); } @@ -2934,6 +3186,10 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { FLIP_BIT(out_buf, afl->stage_cur); FLIP_BIT(out_buf, afl->stage_cur + 1); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_FLIP_BIT2-%u", + afl->queue_cur->fname, afl->stage_cur); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } FLIP_BIT(out_buf, afl->stage_cur); @@ -2963,6 +3219,10 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { FLIP_BIT(out_buf, afl->stage_cur + 2); FLIP_BIT(out_buf, afl->stage_cur + 3); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_FLIP_BIT4-%u", + afl->queue_cur->fname, afl->stage_cur); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } FLIP_BIT(out_buf, afl->stage_cur); @@ -2993,7 +3253,8 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { /* Initialize effector map for the next step (see comments below). Always flag first and last byte as doing something. */ - eff_map = ck_maybe_grow(BUF_PARAMS(eff), EFF_ALEN(len)); + eff_map = afl_realloc(AFL_BUF_PARAM(eff), EFF_ALEN(len)); + if (unlikely(!eff_map)) { PFATAL("alloc"); } eff_map[0] = 1; if (EFF_APOS(len - 1) != 0) { @@ -3017,6 +3278,10 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { out_buf[afl->stage_cur] ^= 0xFF; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_FLIP_BIT8-%u", + afl->queue_cur->fname, afl->stage_cur); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } /* We also use this stage to pull off a simple trick: we identify @@ -3058,7 +3323,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { whole thing as worth fuzzing, since we wouldn't be saving much time anyway. */ - if (eff_cnt != EFF_ALEN(len) && + if (eff_cnt != (u32)EFF_ALEN(len) && eff_cnt * 100 / EFF_ALEN(len) > EFF_MAX_PERC) { memset(eff_map, 1, EFF_ALEN(len)); @@ -3104,6 +3369,10 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { *(u16 *)(out_buf + i) ^= 0xFFFF; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_FLIP_BIT16-%u", + afl->queue_cur->fname, afl->stage_cur); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3142,6 +3411,10 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { *(u32 *)(out_buf + i) ^= 0xFFFFFFFF; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_FLIP_BIT32-%u", + afl->queue_cur->fname, afl->stage_cur); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3173,7 +3446,7 @@ skip_bitflip: orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len; ++i) { + for (i = 0; i < (u32)len; ++i) { u8 orig = out_buf[i]; @@ -3200,6 +3473,10 @@ skip_bitflip: afl->stage_cur_val = j; out_buf[i] = orig + j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_ARITH8+-%u-%u", + afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3216,6 +3493,10 @@ skip_bitflip: afl->stage_cur_val = -j; out_buf[i] = orig - j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_ARITH8_-%u-%u", + afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3280,6 +3561,10 @@ skip_bitflip: afl->stage_cur_val = j; *(u16 *)(out_buf + i) = orig + j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_ARITH16+-%u-%u", + afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3294,6 +3579,10 @@ skip_bitflip: afl->stage_cur_val = -j; *(u16 *)(out_buf + i) = orig - j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_ARITH16_-%u-%u", + afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3312,6 +3601,10 @@ skip_bitflip: afl->stage_cur_val = j; *(u16 *)(out_buf + i) = SWAP16(SWAP16(orig) + j); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_ARITH16+BE-%u-%u", afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3326,6 +3619,10 @@ skip_bitflip: afl->stage_cur_val = -j; *(u16 *)(out_buf + i) = SWAP16(SWAP16(orig) - j); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_ARITH16_BE+%u+%u", afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3389,6 +3686,10 @@ skip_bitflip: afl->stage_cur_val = j; *(u32 *)(out_buf + i) = orig + j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_ARITH32+-%u-%u", + afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3403,6 +3704,10 @@ skip_bitflip: afl->stage_cur_val = -j; *(u32 *)(out_buf + i) = orig - j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_ARITH32_-%u-%u", + afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3421,6 +3726,10 @@ skip_bitflip: afl->stage_cur_val = j; *(u32 *)(out_buf + i) = SWAP32(SWAP32(orig) + j); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_ARITH32+BE-%u-%u", afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3435,6 +3744,10 @@ skip_bitflip: afl->stage_cur_val = -j; *(u32 *)(out_buf + i) = SWAP32(SWAP32(orig) - j); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_ARITH32_BE-%u-%u", afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3472,7 +3785,7 @@ skip_arith: /* Setting 8-bit integers. */ - for (i = 0; i < len; ++i) { + for (i = 0; i < (u32)len; ++i) { u8 orig = out_buf[i]; @@ -3502,6 +3815,10 @@ skip_arith: afl->stage_cur_val = interesting_8[j]; out_buf[i] = interesting_8[j]; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_INTERESTING8-%u-%u", afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } out_buf[i] = orig; @@ -3557,6 +3874,10 @@ skip_arith: *(u16 *)(out_buf + i) = interesting_16[j]; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_INTERESTING16-%u-%u", afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3573,6 +3894,10 @@ skip_arith: afl->stage_val_type = STAGE_VAL_BE; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_INTERESTING16BE-%u-%u", afl->queue_cur->fname, i, j); +#endif *(u16 *)(out_buf + i) = SWAP16(interesting_16[j]); if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3636,6 +3961,10 @@ skip_arith: *(u32 *)(out_buf + i) = interesting_32[j]; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_INTERESTING32-%u-%u", afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3652,6 +3981,10 @@ skip_arith: afl->stage_val_type = STAGE_VAL_BE; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_INTERESTING32BE-%u-%u", afl->queue_cur->fname, i, j); +#endif *(u32 *)(out_buf + i) = SWAP32(interesting_32[j]); if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3692,7 +4025,7 @@ skip_interest: orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len; ++i) { + for (i = 0; i < (u32)len; ++i) { u32 last_len = 0; @@ -3705,13 +4038,13 @@ skip_interest: for (j = 0; j < afl->extras_cnt; ++j) { - /* Skip extras probabilistically if afl->extras_cnt > MAX_DET_EXTRAS. Also - skip them if there's no room to insert the payload, if the token + /* Skip extras probabilistically if afl->extras_cnt > AFL_MAX_DET_EXTRAS. + Also skip them if there's no room to insert the payload, if the token is redundant, or if its entire span has no bytes set in the effector map. */ - if ((afl->extras_cnt > MAX_DET_EXTRAS && - rand_below(afl, afl->extras_cnt) >= MAX_DET_EXTRAS) || + if ((afl->extras_cnt > afl->max_det_extras && + rand_below(afl, afl->extras_cnt) >= afl->max_det_extras) || afl->extras[j].len > len - i || !memcmp(afl->extras[j].data, out_buf + i, afl->extras[j].len) || !memchr(eff_map + EFF_APOS(i), 1, @@ -3725,6 +4058,11 @@ skip_interest: last_len = afl->extras[j].len; memcpy(out_buf + i, afl->extras[j].data, last_len); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_EXTRAS_overwrite-%u-%u", afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3750,9 +4088,10 @@ skip_interest: orig_hit_cnt = new_hit_cnt; - ex_tmp = ck_maybe_grow(BUF_PARAMS(ex), len + MAX_DICT_FILE); + ex_tmp = afl_realloc(AFL_BUF_PARAM(ex), len + MAX_DICT_FILE); + if (unlikely(!ex_tmp)) { PFATAL("alloc"); } - for (i = 0; i <= len; ++i) { + for (i = 0; i <= (u32)len; ++i) { afl->stage_cur_byte = i; @@ -3771,6 +4110,11 @@ skip_interest: /* Copy tail */ memcpy(ex_tmp + i + afl->extras[j].len, out_buf + i, len - i); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_EXTRAS_insert-%u-%u", afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, ex_tmp, len + afl->extras[j].len)) { goto abandon_entry; @@ -3798,23 +4142,24 @@ skip_user_extras: afl->stage_name = "auto extras (over)"; afl->stage_short = "ext_AO"; afl->stage_cur = 0; - afl->stage_max = MIN(afl->a_extras_cnt, USE_AUTO_EXTRAS) * len; + afl->stage_max = MIN(afl->a_extras_cnt, (u32)USE_AUTO_EXTRAS) * len; afl->stage_val_type = STAGE_VAL_NONE; orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len; ++i) { + for (i = 0; i < (u32)len; ++i) { u32 last_len = 0; afl->stage_cur_byte = i; - for (j = 0; j < MIN(afl->a_extras_cnt, USE_AUTO_EXTRAS); ++j) { + u32 min_extra_len = MIN(afl->a_extras_cnt, (u32)USE_AUTO_EXTRAS); + for (j = 0; j < min_extra_len; ++j) { /* See the comment in the earlier code; extras are sorted by size. */ - if (afl->a_extras[j].len > len - i || + if ((afl->a_extras[j].len) > (len - i) || !memcmp(afl->a_extras[j].data, out_buf + i, afl->a_extras[j].len) || !memchr(eff_map + EFF_APOS(i), 1, EFF_SPAN_ALEN(i, afl->a_extras[j].len))) { @@ -3827,6 +4172,12 @@ skip_user_extras: last_len = afl->a_extras[j].len; memcpy(out_buf + i, afl->a_extras[j].data, last_len); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_AUTO_EXTRAS_overwrite-%u-%u", afl->queue_cur->fname, i, + j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3940,10 +4291,23 @@ pacemaker_fuzzing: havoc_queued = afl->queued_paths; + u32 r_max; + + r_max = 15 + ((afl->extras_cnt + afl->a_extras_cnt) ? 2 : 0); + + if (unlikely(afl->expand_havoc && afl->ready_for_splicing_count > 1)) { + + /* add expensive havoc cases here, they are activated after a full + cycle without finds happened */ + + ++r_max; + + } + for (afl->stage_cur = 0; afl->stage_cur < afl->stage_max; ++afl->stage_cur) { - u32 use_stacking = 1 << (1 + rand_below(afl, HAVOC_STACK_POW2)); + u32 use_stacking = 1 << (1 + rand_below(afl, afl->havoc_stack_pow2)); afl->stage_cur_val = use_stacking; @@ -3953,14 +4317,23 @@ pacemaker_fuzzing: } +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_HAVOC-%u", + afl->queue_cur->fname, use_stacking); +#endif + for (i = 0; i < use_stacking; ++i) { - switch (select_algorithm(afl)) { + switch (select_algorithm(afl, r_max)) { case 0: /* Flip a single bit somewhere. Spooky! */ FLIP_BIT(out_buf, rand_below(afl, temp_len << 3)); - MOpt_globals.cycles_v2[STAGE_FLIP1] += 1; + MOpt_globals.cycles_v2[STAGE_FLIP1]++; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP_BIT1"); + strcat(afl->mutation, afl->m_tmp); +#endif break; case 1: @@ -3968,7 +4341,11 @@ pacemaker_fuzzing: temp_len_puppet = rand_below(afl, (temp_len << 3) - 1); FLIP_BIT(out_buf, temp_len_puppet); FLIP_BIT(out_buf, temp_len_puppet + 1); - MOpt_globals.cycles_v2[STAGE_FLIP2] += 1; + MOpt_globals.cycles_v2[STAGE_FLIP2]++; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP_BIT2"); + strcat(afl->mutation, afl->m_tmp); +#endif break; case 2: @@ -3978,25 +4355,41 @@ pacemaker_fuzzing: FLIP_BIT(out_buf, temp_len_puppet + 1); FLIP_BIT(out_buf, temp_len_puppet + 2); FLIP_BIT(out_buf, temp_len_puppet + 3); - MOpt_globals.cycles_v2[STAGE_FLIP4] += 1; + MOpt_globals.cycles_v2[STAGE_FLIP4]++; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP_BIT4"); + strcat(afl->mutation, afl->m_tmp); +#endif break; case 3: if (temp_len < 4) { break; } out_buf[rand_below(afl, temp_len)] ^= 0xFF; - MOpt_globals.cycles_v2[STAGE_FLIP8] += 1; + MOpt_globals.cycles_v2[STAGE_FLIP8]++; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP_BIT8"); + strcat(afl->mutation, afl->m_tmp); +#endif break; case 4: if (temp_len < 8) { break; } *(u16 *)(out_buf + rand_below(afl, temp_len - 1)) ^= 0xFFFF; - MOpt_globals.cycles_v2[STAGE_FLIP16] += 1; + MOpt_globals.cycles_v2[STAGE_FLIP16]++; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP_BIT16"); + strcat(afl->mutation, afl->m_tmp); +#endif break; case 5: if (temp_len < 8) { break; } *(u32 *)(out_buf + rand_below(afl, temp_len - 3)) ^= 0xFFFFFFFF; - MOpt_globals.cycles_v2[STAGE_FLIP32] += 1; + MOpt_globals.cycles_v2[STAGE_FLIP32]++; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP_BIT32"); + strcat(afl->mutation, afl->m_tmp); +#endif break; case 6: @@ -4004,7 +4397,11 @@ pacemaker_fuzzing: 1 + rand_below(afl, ARITH_MAX); out_buf[rand_below(afl, temp_len)] += 1 + rand_below(afl, ARITH_MAX); - MOpt_globals.cycles_v2[STAGE_ARITH8] += 1; + MOpt_globals.cycles_v2[STAGE_ARITH8]++; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH8"); + strcat(afl->mutation, afl->m_tmp); +#endif break; case 7: @@ -4014,11 +4411,20 @@ pacemaker_fuzzing: u32 pos = rand_below(afl, temp_len - 1); *(u16 *)(out_buf + pos) -= 1 + rand_below(afl, ARITH_MAX); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16-%u", pos); + strcat(afl->mutation, afl->m_tmp); +#endif } else { u32 pos = rand_below(afl, temp_len - 1); u16 num = 1 + rand_below(afl, ARITH_MAX); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16BE-%u-%u", + pos, num); + strcat(afl->mutation, afl->m_tmp); +#endif *(u16 *)(out_buf + pos) = SWAP16(SWAP16(*(u16 *)(out_buf + pos)) - num); @@ -4028,18 +4434,27 @@ pacemaker_fuzzing: if (rand_below(afl, 2)) { u32 pos = rand_below(afl, temp_len - 1); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16+-%u", pos); + strcat(afl->mutation, afl->m_tmp); +#endif *(u16 *)(out_buf + pos) += 1 + rand_below(afl, ARITH_MAX); } else { u32 pos = rand_below(afl, temp_len - 1); u16 num = 1 + rand_below(afl, ARITH_MAX); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16BE+-%u-%u", + pos, num); + strcat(afl->mutation, afl->m_tmp); +#endif *(u16 *)(out_buf + pos) = SWAP16(SWAP16(*(u16 *)(out_buf + pos)) + num); } - MOpt_globals.cycles_v2[STAGE_ARITH16] += 1; + MOpt_globals.cycles_v2[STAGE_ARITH16]++; break; case 8: @@ -4048,12 +4463,21 @@ pacemaker_fuzzing: if (rand_below(afl, 2)) { u32 pos = rand_below(afl, temp_len - 3); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32_-%u", pos); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + pos) -= 1 + rand_below(afl, ARITH_MAX); } else { u32 pos = rand_below(afl, temp_len - 3); u32 num = 1 + rand_below(afl, ARITH_MAX); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32BE_-%u-%u", + pos, num); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + pos) = SWAP32(SWAP32(*(u32 *)(out_buf + pos)) - num); @@ -4064,18 +4488,27 @@ pacemaker_fuzzing: if (rand_below(afl, 2)) { u32 pos = rand_below(afl, temp_len - 3); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32+-%u", pos); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + pos) += 1 + rand_below(afl, ARITH_MAX); } else { u32 pos = rand_below(afl, temp_len - 3); u32 num = 1 + rand_below(afl, ARITH_MAX); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32BE+-%u-%u", + pos, num); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + pos) = SWAP32(SWAP32(*(u32 *)(out_buf + pos)) + num); } - MOpt_globals.cycles_v2[STAGE_ARITH32] += 1; + MOpt_globals.cycles_v2[STAGE_ARITH32]++; break; case 9: @@ -4083,7 +4516,11 @@ pacemaker_fuzzing: if (temp_len < 4) { break; } out_buf[rand_below(afl, temp_len)] = interesting_8[rand_below(afl, sizeof(interesting_8))]; - MOpt_globals.cycles_v2[STAGE_INTEREST8] += 1; + MOpt_globals.cycles_v2[STAGE_INTEREST8]++; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING8"); + strcat(afl->mutation, afl->m_tmp); +#endif break; case 10: @@ -4091,19 +4528,27 @@ pacemaker_fuzzing: if (temp_len < 8) { break; } if (rand_below(afl, 2)) { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING16"); + strcat(afl->mutation, afl->m_tmp); +#endif *(u16 *)(out_buf + rand_below(afl, temp_len - 1)) = interesting_16[rand_below(afl, sizeof(interesting_16) >> 1)]; } else { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING16BE"); + strcat(afl->mutation, afl->m_tmp); +#endif *(u16 *)(out_buf + rand_below(afl, temp_len - 1)) = SWAP16(interesting_16[rand_below( afl, sizeof(interesting_16) >> 1)]); } - MOpt_globals.cycles_v2[STAGE_INTEREST16] += 1; + MOpt_globals.cycles_v2[STAGE_INTEREST16]++; break; case 11: @@ -4113,19 +4558,27 @@ pacemaker_fuzzing: if (rand_below(afl, 2)) { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING32"); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + rand_below(afl, temp_len - 3)) = interesting_32[rand_below(afl, sizeof(interesting_32) >> 2)]; } else { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING32BE"); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + rand_below(afl, temp_len - 3)) = SWAP32(interesting_32[rand_below( afl, sizeof(interesting_32) >> 2)]); } - MOpt_globals.cycles_v2[STAGE_INTEREST32] += 1; + MOpt_globals.cycles_v2[STAGE_INTEREST32]++; break; case 12: @@ -4135,7 +4588,11 @@ pacemaker_fuzzing: possibility of a no-op. */ out_buf[rand_below(afl, temp_len)] ^= 1 + rand_below(afl, 255); - MOpt_globals.cycles_v2[STAGE_RANDOMBYTE] += 1; + MOpt_globals.cycles_v2[STAGE_RANDOMBYTE]++; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " RAND8"); + strcat(afl->mutation, afl->m_tmp); +#endif break; case 13: { @@ -4154,11 +4611,16 @@ pacemaker_fuzzing: del_from = rand_below(afl, temp_len - del_len + 1); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " DEL-%u%u", del_from, + del_len); + strcat(afl->mutation, afl->m_tmp); +#endif memmove(out_buf + del_from, out_buf + del_from + del_len, temp_len - del_from - del_len); temp_len -= del_len; - MOpt_globals.cycles_v2[STAGE_DELETEBYTE] += 1; + MOpt_globals.cycles_v2[STAGE_DELETEBYTE]++; break; } @@ -4174,7 +4636,7 @@ pacemaker_fuzzing: u32 clone_from, clone_to, clone_len; u8 *new_buf; - if (actually_clone) { + if (likely(actually_clone)) { clone_len = choose_block_len(afl, temp_len); clone_from = rand_below(afl, temp_len - clone_len + 1); @@ -4188,8 +4650,15 @@ pacemaker_fuzzing: clone_to = rand_below(afl, temp_len); - new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), - temp_len + clone_len); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " CLONE_%s-%u-%u-%u", + actually_clone ? "clone" : "insert", clone_from, + clone_to, clone_len); + strcat(afl->mutation, afl->m_tmp); +#endif + new_buf = afl_realloc(AFL_BUF_PARAM(out_scratch), + temp_len + clone_len); + if (unlikely(!new_buf)) { PFATAL("alloc"); } /* Head */ @@ -4215,10 +4684,10 @@ pacemaker_fuzzing: memcpy(new_buf + clone_to + clone_len, out_buf + clone_to, temp_len - clone_to); - swap_bufs(BUF_PARAMS(out), BUF_PARAMS(out_scratch)); out_buf = new_buf; + afl_swap_bufs(AFL_BUF_PARAM(out), AFL_BUF_PARAM(out_scratch)); temp_len += clone_len; - MOpt_globals.cycles_v2[STAGE_Clone75] += 1; + MOpt_globals.cycles_v2[STAGE_Clone75]++; } @@ -4238,16 +4707,28 @@ pacemaker_fuzzing: copy_from = rand_below(afl, temp_len - copy_len + 1); copy_to = rand_below(afl, temp_len - copy_len + 1); - if (rand_below(afl, 4)) { + if (likely(rand_below(afl, 4))) { - if (copy_from != copy_to) { + if (likely(copy_from != copy_to)) { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " OVERWRITE_COPY-%u-%u-%u", copy_from, copy_to, + copy_len); + strcat(afl->mutation, afl->m_tmp); +#endif memmove(out_buf + copy_to, out_buf + copy_from, copy_len); } } else { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " OVERWRITE_FIXED-%u-%u-%u", copy_from, copy_to, + copy_len); + strcat(afl->mutation, afl->m_tmp); +#endif memset(out_buf + copy_to, rand_below(afl, 2) ? rand_below(afl, 256) : out_buf[rand_below(afl, temp_len)], @@ -4255,7 +4736,7 @@ pacemaker_fuzzing: } - MOpt_globals.cycles_v2[STAGE_OverWrite75] += 1; + MOpt_globals.cycles_v2[STAGE_OverWrite75]++; break; } /* case 15 */ @@ -4276,9 +4757,14 @@ pacemaker_fuzzing: u32 use_extra = rand_below(afl, afl->a_extras_cnt); u32 extra_len = afl->a_extras[use_extra].len; - if (extra_len > temp_len) break; + if (extra_len > (u32)temp_len) break; u32 insert_at = rand_below(afl, temp_len - extra_len + 1); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " AUTO_EXTRA_OVERWRITE-%u-%u", insert_at, extra_len); + strcat(afl->mutation, afl->m_tmp); +#endif memcpy(out_buf + insert_at, afl->a_extras[use_extra].data, extra_len); @@ -4289,16 +4775,20 @@ pacemaker_fuzzing: u32 use_extra = rand_below(afl, afl->extras_cnt); u32 extra_len = afl->extras[use_extra].len; - if (extra_len > temp_len) break; + if (extra_len > (u32)temp_len) break; u32 insert_at = rand_below(afl, temp_len - extra_len + 1); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " EXTRA_OVERWRITE-%u-%u", insert_at, extra_len); + strcat(afl->mutation, afl->m_tmp); +#endif memcpy(out_buf + insert_at, afl->extras[use_extra].data, extra_len); } - afl->stage_cycles_puppet_v2[afl->swarm_now] - [STAGE_OverWriteExtra] += 1; + MOpt_globals.cycles_v2[STAGE_OverWriteExtra]++; break; @@ -4321,18 +4811,29 @@ pacemaker_fuzzing: use_extra = rand_below(afl, afl->a_extras_cnt); extra_len = afl->a_extras[use_extra].len; ptr = afl->a_extras[use_extra].data; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " AUTO_EXTRA_INSERT-%u-%u", insert_at, extra_len); + strcat(afl->mutation, afl->m_tmp); +#endif } else { use_extra = rand_below(afl, afl->extras_cnt); extra_len = afl->extras[use_extra].len; ptr = afl->extras[use_extra].data; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " EXTRA_INSERT-%u-%u", + insert_at, extra_len); + strcat(afl->mutation, afl->m_tmp); +#endif } if (temp_len + extra_len >= MAX_FILE) break; - out_buf = ck_maybe_grow(BUF_PARAMS(out), temp_len + extra_len); + out_buf = afl_realloc(AFL_BUF_PARAM(out), temp_len + extra_len); + if (unlikely(!out_buf)) { PFATAL("alloc"); } /* Tail */ memmove(out_buf + insert_at + extra_len, out_buf + insert_at, @@ -4342,17 +4843,98 @@ pacemaker_fuzzing: memcpy(out_buf + insert_at, ptr, extra_len); temp_len += extra_len; - afl->stage_cycles_puppet_v2[afl->swarm_now][STAGE_InsertExtra] += - 1; + MOpt_globals.cycles_v2[STAGE_InsertExtra]++; break; } + default: { + + if (unlikely(afl->ready_for_splicing_count < 2)) break; + + u32 tid; + do { + + tid = rand_below(afl, afl->queued_paths); + + } while (tid == afl->current_entry || + + afl->queue_buf[tid]->len < 4); + + /* Get the testcase for splicing. */ + struct queue_entry *target = afl->queue_buf[tid]; + u32 new_len = target->len; + u8 * new_buf = queue_testcase_get(afl, target); + + if ((temp_len >= 2 && rand_below(afl, 2)) || + temp_len + HAVOC_BLK_XL >= MAX_FILE) { + + /* overwrite mode */ + + u32 copy_from, copy_to, copy_len; + + copy_len = choose_block_len(afl, new_len - 1); + if (copy_len > temp_len) copy_len = temp_len; + + copy_from = rand_below(afl, new_len - copy_len + 1); + copy_to = rand_below(afl, temp_len - copy_len + 1); + +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " SPLICE_OVERWRITE-%u-%u-%u-%s", copy_from, copy_to, + copy_len, target->fname); + strcat(afl->mutation, afl->m_tmp); +#endif + memmove(out_buf + copy_to, new_buf + copy_from, copy_len); + + } else { + + /* insert mode */ + + u32 clone_from, clone_to, clone_len; + + clone_len = choose_block_len(afl, new_len); + clone_from = rand_below(afl, new_len - clone_len + 1); + clone_to = rand_below(afl, temp_len + 1); + + u8 *temp_buf = afl_realloc(AFL_BUF_PARAM(out_scratch), + temp_len + clone_len + 1); + if (unlikely(!temp_buf)) { PFATAL("alloc"); } + +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " SPLICE_INSERT-%u-%u-%u-%s", clone_from, clone_to, + clone_len, target->fname); + strcat(afl->mutation, afl->m_tmp); +#endif + /* Head */ + + memcpy(temp_buf, out_buf, clone_to); + + /* Inserted part */ + + memcpy(temp_buf + clone_to, new_buf + clone_from, clone_len); + + /* Tail */ + memcpy(temp_buf + clone_to + clone_len, out_buf + clone_to, + temp_len - clone_to); + + out_buf = temp_buf; + afl_swap_bufs(AFL_BUF_PARAM(out), AFL_BUF_PARAM(out_scratch)); + temp_len += clone_len; + + } + + MOpt_globals.cycles_v2[STAGE_Splice]++; + break; + + } // end of default: + } /* switch select_algorithm() */ } /* for i=0; i < use_stacking */ - *MOpt_globals.pTime += 1; + ++*MOpt_globals.pTime; u64 temp_total_found = afl->queued_paths + afl->unique_crashes; @@ -4365,7 +4947,8 @@ pacemaker_fuzzing: /* out_buf might have been mangled a bit, so let's restore it to its original size and shape. */ - out_buf = ck_maybe_grow(BUF_PARAMS(out), len); + out_buf = afl_realloc(AFL_BUF_PARAM(out), len); + if (unlikely(!out_buf)) { PFATAL("alloc"); } temp_len = len; memcpy(out_buf, in_buf, len); @@ -4449,8 +5032,9 @@ pacemaker_fuzzing: retry_splicing_puppet: - if (afl->use_splicing && splice_cycle++ < afl->SPLICE_CYCLES_puppet && - afl->queued_paths > 1 && afl->queue_cur->len > 1) { + if (afl->use_splicing && + splice_cycle++ < (u32)afl->SPLICE_CYCLES_puppet && + afl->ready_for_splicing_count > 1 && afl->queue_cur->len >= 4) { struct queue_entry *target; u32 tid, split_at; @@ -4474,46 +5058,13 @@ pacemaker_fuzzing: tid = rand_below(afl, afl->queued_paths); - } while (tid == afl->current_entry); + } while (tid == afl->current_entry || afl->queue_buf[tid]->len < 4); afl->splicing_with = tid; - target = afl->queue; - - while (tid >= 100) { - - target = target->next_100; - tid -= 100; - - } - - while (tid--) { - - target = target->next; - - } - - /* Make sure that the target has a reasonable length. */ - - while (target && (target->len < 2 || target == afl->queue_cur)) { - - target = target->next; - ++afl->splicing_with; - - } - - if (!target) { goto retry_splicing_puppet; } + target = afl->queue_buf[tid]; /* Read the testcase into a new buffer. */ - - fd = open(target->fname, O_RDONLY); - - if (fd < 0) { PFATAL("Unable to open '%s'", target->fname); } - - new_buf = ck_maybe_grow(BUF_PARAMS(in_scratch), target->len); - - ck_read(fd, new_buf, target->len, target->fname); - - close(fd); + new_buf = queue_testcase_get(afl, target); /* Find a suitable splicin g location, somewhere between the first and the last differing byte. Bail out if the difference is just a single @@ -4534,10 +5085,14 @@ pacemaker_fuzzing: /* Do the thing. */ len = target->len; - memcpy(new_buf, in_buf, split_at); - swap_bufs(BUF_PARAMS(in), BUF_PARAMS(in_scratch)); - in_buf = new_buf; - out_buf = ck_maybe_grow(BUF_PARAMS(out), len); + afl->in_scratch_buf = afl_realloc(AFL_BUF_PARAM(in_scratch), len); + memcpy(afl->in_scratch_buf, in_buf, split_at); + memcpy(afl->in_scratch_buf + split_at, new_buf, len - split_at); + in_buf = afl->in_scratch_buf; + afl_swap_bufs(AFL_BUF_PARAM(in), AFL_BUF_PARAM(in_scratch)); + + out_buf = afl_realloc(AFL_BUF_PARAM(out), len); + if (unlikely(!out_buf)) { PFATAL("alloc"); } memcpy(out_buf, in_buf, len); goto havoc_stage_puppet; @@ -4551,7 +5106,7 @@ pacemaker_fuzzing: abandon_entry: abandon_entry_puppet: - if (splice_cycle >= afl->SPLICE_CYCLES_puppet) { + if ((s64)splice_cycle >= afl->SPLICE_CYCLES_puppet) { afl->SPLICE_CYCLES_puppet = (rand_below( @@ -4573,7 +5128,7 @@ pacemaker_fuzzing: // if (afl->queue_cur->favored) --afl->pending_favored; // } - munmap(orig_in, afl->queue_cur->len); + orig_in = NULL; if (afl->key_puppet == 1) { @@ -4729,7 +5284,7 @@ u8 pilot_fuzzing(afl_state_t *afl) { void pso_updating(afl_state_t *afl) { - afl->g_now += 1; + afl->g_now++; if (afl->g_now > afl->g_max) { afl->g_now = 0; } afl->w_now = (afl->w_init - afl->w_end) * (afl->g_max - afl->g_now) / (afl->g_max) + @@ -4870,5 +5425,3 @@ u8 fuzz_one(afl_state_t *afl) { } -#undef BUF_PARAMS - diff --git a/src/afl-fuzz-python.c b/src/afl-fuzz-python.c index 2044c97d..8760194c 100644 --- a/src/afl-fuzz-python.c +++ b/src/afl-fuzz-python.c @@ -30,6 +30,9 @@ static void *unsupported(afl_state_t *afl, unsigned int seed) { + (void)afl; + (void)seed; + FATAL("Python Mutator cannot be called twice yet"); return NULL; @@ -37,9 +40,7 @@ static void *unsupported(afl_state_t *afl, unsigned int seed) { /* sorry for this makro... it just fills in `&py_mutator->something_buf, &py_mutator->something_size`. */ - #define BUF_PARAMS(name) \ - (void **)&((py_mutator_t *)py_mutator)->name##_buf, \ - &((py_mutator_t *)py_mutator)->name##_size + #define BUF_PARAMS(name) (void **)&((py_mutator_t *)py_mutator)->name##_buf static size_t fuzz_py(void *py_mutator, u8 *buf, size_t buf_size, u8 **out_buf, u8 *add_buf, size_t add_buf_size, size_t max_size) { @@ -94,7 +95,8 @@ static size_t fuzz_py(void *py_mutator, u8 *buf, size_t buf_size, u8 **out_buf, mutated_size = PyByteArray_Size(py_value); - *out_buf = ck_maybe_grow(BUF_PARAMS(fuzz), mutated_size); + *out_buf = afl_realloc(BUF_PARAMS(fuzz), mutated_size); + if (unlikely(!*out_buf)) { PFATAL("alloc"); } memcpy(*out_buf, PyByteArray_AsString(py_value), mutated_size); Py_DECREF(py_value); @@ -109,8 +111,41 @@ static size_t fuzz_py(void *py_mutator, u8 *buf, size_t buf_size, u8 **out_buf, } +static const char *custom_describe_py(void * py_mutator, + size_t max_description_len) { + + PyObject *py_args, *py_value; + + py_args = PyTuple_New(1); + + PyLong_FromSize_t(max_description_len); + + /* add_buf */ + py_value = PyLong_FromSize_t(max_description_len); + if (!py_value) { + + Py_DECREF(py_args); + FATAL("Failed to convert arguments"); + + } + + PyTuple_SetItem(py_args, 0, py_value); + + py_value = PyObject_CallObject( + ((py_mutator_t *)py_mutator)->py_functions[PY_FUNC_DESCRIBE], py_args); + + Py_DECREF(py_args); + + if (py_value != NULL) { return PyBytes_AsString(py_value); } + + return NULL; + +} + static py_mutator_t *init_py_module(afl_state_t *afl, u8 *module_name) { + (void)afl; + if (!module_name) { return NULL; } py_mutator_t *py = calloc(1, sizeof(py_mutator_t)); @@ -130,6 +165,18 @@ static py_mutator_t *init_py_module(afl_state_t *afl, u8 *module_name) { PyObject * py_module = py->py_module; PyObject **py_functions = py->py_functions; + // initialize the post process buffer; ensures it's always valid + PyObject *unused_bytes = PyByteArray_FromStringAndSize("OHAI", 4); + if (!unused_bytes) { FATAL("allocation failed!"); } + if (PyObject_GetBuffer(unused_bytes, &py->post_process_buf, PyBUF_SIMPLE) == + -1) { + + FATAL("buffer initialization failed"); + + } + + Py_DECREF(unused_bytes); + if (py_module != NULL) { u8 py_notrim = 0, py_idx; @@ -140,6 +187,10 @@ static py_mutator_t *init_py_module(afl_state_t *afl, u8 *module_name) { py_functions[PY_FUNC_FUZZ] = PyObject_GetAttrString(py_module, "fuzz"); if (!py_functions[PY_FUNC_FUZZ]) py_functions[PY_FUNC_FUZZ] = PyObject_GetAttrString(py_module, "mutate"); + py_functions[PY_FUNC_DESCRIBE] = + PyObject_GetAttrString(py_module, "describe"); + py_functions[PY_FUNC_FUZZ_COUNT] = + PyObject_GetAttrString(py_module, "fuzz_count"); if (!py_functions[PY_FUNC_FUZZ]) WARNF("fuzz function not found in python module"); py_functions[PY_FUNC_POST_PROCESS] = @@ -157,6 +208,8 @@ static py_mutator_t *init_py_module(afl_state_t *afl, u8 *module_name) { PyObject_GetAttrString(py_module, "queue_get"); py_functions[PY_FUNC_QUEUE_NEW_ENTRY] = PyObject_GetAttrString(py_module, "queue_new_entry"); + py_functions[PY_FUNC_INTROSPECTION] = + PyObject_GetAttrString(py_module, "introspection"); py_functions[PY_FUNC_DEINIT] = PyObject_GetAttrString(py_module, "deinit"); if (!py_functions[PY_FUNC_DEINIT]) FATAL("deinit function not found in python module"); @@ -165,27 +218,20 @@ static py_mutator_t *init_py_module(afl_state_t *afl, u8 *module_name) { if (!py_functions[py_idx] || !PyCallable_Check(py_functions[py_idx])) { - if (py_idx == PY_FUNC_POST_PROCESS) { - - // Implenting the post_process API is optional for now - if (PyErr_Occurred()) { PyErr_Print(); } - - } else if (py_idx >= PY_FUNC_INIT_TRIM && py_idx <= PY_FUNC_TRIM) { + if (py_idx >= PY_FUNC_INIT_TRIM && py_idx <= PY_FUNC_TRIM) { // Implementing the trim API is optional for now if (PyErr_Occurred()) { PyErr_Print(); } py_notrim = 1; - } else if ((py_idx >= PY_FUNC_HAVOC_MUTATION) && + } else if (py_idx >= PY_OPTIONAL) { - (py_idx <= PY_FUNC_QUEUE_NEW_ENTRY)) { + // Only _init and _deinit are not optional currently - // Implenting the havoc and queue API is optional for now if (PyErr_Occurred()) { PyErr_Print(); } } else { - if (PyErr_Occurred()) { PyErr_Print(); } fprintf(stderr, "Cannot find/call function with index %d in external " "Python module.\n", @@ -213,6 +259,7 @@ static py_mutator_t *init_py_module(afl_state_t *afl, u8 *module_name) { PyErr_Print(); fprintf(stderr, "Failed to load \"%s\"\n", module_name); + free(py); return NULL; } @@ -247,6 +294,8 @@ void finalize_py_module(void *py_mutator) { static void init_py(afl_state_t *afl, py_mutator_t *py_mutator, unsigned int seed) { + (void)afl; + PyObject *py_args, *py_value; /* Provide the init function a seed for the Python RNG */ @@ -309,8 +358,6 @@ struct custom_mutator *load_custom_mutator_py(afl_state_t *afl, struct custom_mutator *mutator; mutator = ck_alloc(sizeof(struct custom_mutator)); - mutator->post_process_buf = NULL; - mutator->post_process_size = 0; mutator->name = module_name; ACTF("Loading Python mutator library from '%s'...", module_name); @@ -326,9 +373,13 @@ struct custom_mutator *load_custom_mutator_py(afl_state_t *afl, if (py_functions[PY_FUNC_DEINIT]) { mutator->afl_custom_deinit = deinit_py; } - /* "afl_custom_fuzz" should not be NULL, but the interface of Python mutator - is quite different from the custom mutator. */ - mutator->afl_custom_fuzz = fuzz_py; + if (py_functions[PY_FUNC_FUZZ]) { mutator->afl_custom_fuzz = fuzz_py; } + + if (py_functions[PY_FUNC_DESCRIBE]) { + + mutator->afl_custom_describe = custom_describe_py; + + } if (py_functions[PY_FUNC_POST_PROCESS]) { @@ -342,6 +393,12 @@ struct custom_mutator *load_custom_mutator_py(afl_state_t *afl, } + if (py_functions[PY_FUNC_FUZZ_COUNT]) { + + mutator->afl_custom_fuzz_count = fuzz_count_py; + + } + if (py_functions[PY_FUNC_POST_TRIM]) { mutator->afl_custom_post_trim = post_trim_py; @@ -375,6 +432,15 @@ struct custom_mutator *load_custom_mutator_py(afl_state_t *afl, } + #ifdef INTROSPECTION + if (py_functions[PY_FUNC_INTROSPECTION]) { + + mutator->afl_custom_introspection = introspection_py; + + } + + #endif + OKF("Python mutator '%s' installed successfully.", module_name); /* Initialize the custom mutator */ @@ -387,10 +453,13 @@ struct custom_mutator *load_custom_mutator_py(afl_state_t *afl, size_t post_process_py(void *py_mutator, u8 *buf, size_t buf_size, u8 **out_buf) { - size_t py_out_buf_size; PyObject * py_args, *py_value; py_mutator_t *py = (py_mutator_t *)py_mutator; + // buffer returned previously must be released; initialized during init + // so we don't need to do comparisons + PyBuffer_Release(&py->post_process_buf); + py_args = PyTuple_New(1); py_value = PyByteArray_FromStringAndSize(buf, buf_size); if (!py_value) { @@ -410,16 +479,20 @@ size_t post_process_py(void *py_mutator, u8 *buf, size_t buf_size, if (py_value != NULL) { - py_out_buf_size = PyByteArray_Size(py_value); + if (PyObject_GetBuffer(py_value, &py->post_process_buf, PyBUF_SIMPLE) == + -1) { - ck_maybe_grow(BUF_PARAMS(post_process), py_out_buf_size); + PyErr_Print(); + FATAL( + "Python custom mutator: post_process call return value not a " + "bytes-like object"); + + } - memcpy(py->post_process_buf, PyByteArray_AsString(py_value), - py_out_buf_size); Py_DECREF(py_value); - *out_buf = py->post_process_buf; - return py_out_buf_size; + *out_buf = (u8 *)py->post_process_buf.buf; + return py->post_process_buf.len; } else { @@ -468,6 +541,44 @@ s32 init_trim_py(void *py_mutator, u8 *buf, size_t buf_size) { } +u32 fuzz_count_py(void *py_mutator, const u8 *buf, size_t buf_size) { + + PyObject *py_args, *py_value; + + py_args = PyTuple_New(1); + py_value = PyByteArray_FromStringAndSize(buf, buf_size); + if (!py_value) { + + Py_DECREF(py_args); + FATAL("Failed to convert arguments"); + + } + + PyTuple_SetItem(py_args, 0, py_value); + + py_value = PyObject_CallObject( + ((py_mutator_t *)py_mutator)->py_functions[PY_FUNC_FUZZ_COUNT], py_args); + Py_DECREF(py_args); + + if (py_value != NULL) { + + #if PY_MAJOR_VERSION >= 3 + u32 retcnt = (u32)PyLong_AsLong(py_value); + #else + u32 retcnt = PyInt_AsLong(py_value); + #endif + Py_DECREF(py_value); + return retcnt; + + } else { + + PyErr_Print(); + FATAL("Call failed"); + + } + +} + s32 post_trim_py(void *py_mutator, u8 success) { PyObject *py_args, *py_value; @@ -520,7 +631,8 @@ size_t trim_py(void *py_mutator, u8 **out_buf) { if (py_value != NULL) { ret = PyByteArray_Size(py_value); - *out_buf = ck_maybe_grow(BUF_PARAMS(trim), ret); + *out_buf = afl_realloc(BUF_PARAMS(trim), ret); + if (unlikely(!*out_buf)) { PFATAL("alloc"); } memcpy(*out_buf, PyByteArray_AsString(py_value), ret); Py_DECREF(py_value); @@ -585,7 +697,8 @@ size_t havoc_mutation_py(void *py_mutator, u8 *buf, size_t buf_size, } else { /* A new buf is needed... */ - *out_buf = ck_maybe_grow(BUF_PARAMS(havoc), mutated_size); + *out_buf = afl_realloc(BUF_PARAMS(havoc), mutated_size); + if (unlikely(!*out_buf)) { PFATAL("alloc"); } } @@ -629,6 +742,28 @@ u8 havoc_mutation_probability_py(void *py_mutator) { } +const char *introspection_py(void *py_mutator) { + + PyObject *py_args, *py_value; + + py_args = PyTuple_New(0); + py_value = PyObject_CallObject( + ((py_mutator_t *)py_mutator)->py_functions[PY_FUNC_INTROSPECTION], + py_args); + Py_DECREF(py_args); + + if (py_value == NULL) { + + return NULL; + + } else { + + return PyByteArray_AsString(py_value); + + } + +} + u8 queue_get_py(void *py_mutator, const u8 *filename) { PyObject *py_args, *py_value; diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 38e95ac8..b2f88205 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -25,8 +25,217 @@ #include "afl-fuzz.h" #include <limits.h> #include <ctype.h> +#include <math.h> -#define BUF_PARAMS(name) (void **)&afl->name##_buf, &afl->name##_size +/* select next queue entry based on alias algo - fast! */ + +inline u32 select_next_queue_entry(afl_state_t *afl) { + + u32 s = rand_below(afl, afl->queued_paths); + double p = rand_next_percent(afl); + /* + fprintf(stderr, "select: p=%f s=%u ... p < prob[s]=%f ? s=%u : alias[%u]=%u" + " ==> %u\n", p, s, afl->alias_probability[s], s, s, afl->alias_table[s], p < + afl->alias_probability[s] ? s : afl->alias_table[s]); + */ + return (p < afl->alias_probability[s] ? s : afl->alias_table[s]); + +} + +double compute_weight(afl_state_t *afl, struct queue_entry *q, + double avg_exec_us, double avg_bitmap_size, + double avg_top_size) { + + double weight = 1.0; + + if (likely(afl->schedule >= FAST && afl->schedule <= RARE)) { + + u32 hits = afl->n_fuzz[q->n_fuzz_entry]; + if (likely(hits)) { weight *= log10(hits) + 1; } + + } + + if (likely(afl->schedule < RARE)) { weight *= (avg_exec_us / q->exec_us); } + weight *= (log(q->bitmap_size) / avg_bitmap_size); + weight *= (1 + (q->tc_ref / avg_top_size)); + if (unlikely(q->favored)) weight *= 5; + + return weight; + +} + +/* create the alias table that allows weighted random selection - expensive */ + +void create_alias_table(afl_state_t *afl) { + + u32 n = afl->queued_paths, i = 0, a, g; + double sum = 0; + + afl->alias_table = + (u32 *)afl_realloc((void **)&afl->alias_table, n * sizeof(u32)); + afl->alias_probability = (double *)afl_realloc( + (void **)&afl->alias_probability, n * sizeof(double)); + double *P = (double *)afl_realloc(AFL_BUF_PARAM(out), n * sizeof(double)); + int * S = (u32 *)afl_realloc(AFL_BUF_PARAM(out_scratch), n * sizeof(u32)); + int * L = (u32 *)afl_realloc(AFL_BUF_PARAM(in_scratch), n * sizeof(u32)); + + if (!P || !S || !L || !afl->alias_table || !afl->alias_probability) { + + FATAL("could not acquire memory for alias table"); + + } + + memset((void *)afl->alias_table, 0, n * sizeof(u32)); + memset((void *)afl->alias_probability, 0, n * sizeof(double)); + + if (likely(afl->schedule < RARE)) { + + double avg_exec_us = 0.0; + double avg_bitmap_size = 0.0; + double avg_top_size = 0.0; + u32 active = 0; + + for (i = 0; i < n; i++) { + + struct queue_entry *q = afl->queue_buf[i]; + + // disabled entries might have timings and bitmap values + if (likely(!q->disabled)) { + + avg_exec_us += q->exec_us; + avg_bitmap_size += log(q->bitmap_size); + avg_top_size += q->tc_ref; + ++active; + + } + + } + + avg_exec_us /= active; + avg_bitmap_size /= active; + avg_top_size /= active; + + for (i = 0; i < n; i++) { + + struct queue_entry *q = afl->queue_buf[i]; + + if (likely(!q->disabled)) { + + q->weight = + compute_weight(afl, q, avg_exec_us, avg_bitmap_size, avg_top_size); + q->perf_score = calculate_score(afl, q); + sum += q->weight; + + } + + } + + for (i = 0; i < n; i++) { + + // weight is always 0 for disabled entries + P[i] = (afl->queue_buf[i]->weight * n) / sum; + + } + + } else { + + for (i = 0; i < n; i++) { + + struct queue_entry *q = afl->queue_buf[i]; + + if (likely(!q->disabled)) { q->perf_score = calculate_score(afl, q); } + + sum += q->perf_score; + + } + + for (i = 0; i < n; i++) { + + // perf_score is always 0 for disabled entries + P[i] = (afl->queue_buf[i]->perf_score * n) / sum; + + } + + } + + int nS = 0, nL = 0, s; + for (s = (s32)n - 1; s >= 0; --s) { + + if (P[s] < 1) { + + S[nS++] = s; + + } else { + + L[nL++] = s; + + } + + } + + while (nS && nL) { + + a = S[--nS]; + g = L[--nL]; + afl->alias_probability[a] = P[a]; + afl->alias_table[a] = g; + P[g] = P[g] + P[a] - 1; + if (P[g] < 1) { + + S[nS++] = g; + + } else { + + L[nL++] = g; + + } + + } + + while (nL) + afl->alias_probability[L[--nL]] = 1; + + while (nS) + afl->alias_probability[S[--nS]] = 1; + + /* + #ifdef INTROSPECTION + u8 fn[PATH_MAX]; + snprintf(fn, PATH_MAX, "%s/introspection_corpus.txt", afl->out_dir); + FILE *f = fopen(fn, "a"); + if (f) { + + for (i = 0; i < n; i++) { + + struct queue_entry *q = afl->queue_buf[i]; + fprintf( + f, + "entry=%u name=%s favored=%s variable=%s disabled=%s len=%u " + "exec_us=%u " + "bitmap_size=%u bitsmap_size=%u tops=%u weight=%f perf_score=%f\n", + i, q->fname, q->favored ? "true" : "false", + q->var_behavior ? "true" : "false", q->disabled ? "true" : "false", + q->len, (u32)q->exec_us, q->bitmap_size, q->bitsmap_size, q->tc_ref, + q->weight, q->perf_score); + + } + + fprintf(f, "\n"); + fclose(f); + + } + + #endif + */ + /* + fprintf(stderr, " entry alias probability perf_score weight + filename\n"); for (u32 i = 0; i < n; ++i) fprintf(stderr, " %5u %5u %11u + %0.9f %0.9f %s\n", i, afl->alias_table[i], afl->alias_probability[i], + afl->queue_buf[i]->perf_score, afl->queue_buf[i]->weight, + afl->queue_buf[i]->fname); + */ + +} /* Mark deterministic checks as done for a particular queue entry. We use the .state file to avoid repeating deterministic fuzzing when resuming aborted @@ -78,9 +287,9 @@ void mark_as_variable(afl_state_t *afl, struct queue_entry *q) { void mark_as_redundant(afl_state_t *afl, struct queue_entry *q, u8 state) { - u8 fn[PATH_MAX]; + if (likely(state == q->fs_redundant)) { return; } - if (state == q->fs_redundant) { return; } + u8 fn[PATH_MAX]; q->fs_redundant = state; @@ -105,16 +314,22 @@ void mark_as_redundant(afl_state_t *afl, struct queue_entry *q, u8 state) { /* check if ascii or UTF-8 */ -static u8 check_if_text(struct queue_entry *q) { +static u8 check_if_text(afl_state_t *afl, struct queue_entry *q) { if (q->len < AFL_TXT_MIN_LEN) return 0; - u8 buf[MAX_FILE]; - s32 fd, len = q->len, offset = 0, ascii = 0, utf8 = 0, comp; + u8 * buf; + int fd; + u32 len = q->len, offset = 0, ascii = 0, utf8 = 0; + ssize_t comp; + if (len >= MAX_FILE) len = MAX_FILE - 1; if ((fd = open(q->fname, O_RDONLY)) < 0) return 0; - if ((comp = read(fd, buf, len)) != len) return 0; + buf = afl_realloc(AFL_BUF_PARAM(in_scratch), len + 1); + comp = read(fd, buf, len); close(fd); + if (comp != (ssize_t)len) return 0; + buf[len] = 0; while (offset < len) { @@ -138,7 +353,8 @@ static u8 check_if_text(struct queue_entry *q) { } // non-overlong 2-byte - if (((0xC2 <= buf[offset + 0] && buf[offset + 0] <= 0xDF) && + if (len - offset > 1 && + ((0xC2 <= buf[offset + 0] && buf[offset + 0] <= 0xDF) && (0x80 <= buf[offset + 1] && buf[offset + 1] <= 0xBF))) { offset += 2; @@ -149,18 +365,19 @@ static u8 check_if_text(struct queue_entry *q) { } // excluding overlongs - if ((buf[offset + 0] == 0xE0 && - (0xA0 <= buf[offset + 1] && buf[offset + 1] <= 0xBF) && - (0x80 <= buf[offset + 2] && - buf[offset + 2] <= 0xBF)) || // straight 3-byte - (((0xE1 <= buf[offset + 0] && buf[offset + 0] <= 0xEC) || - buf[offset + 0] == 0xEE || buf[offset + 0] == 0xEF) && - (0x80 <= buf[offset + 1] && buf[offset + 1] <= 0xBF) && - (0x80 <= buf[offset + 2] && - buf[offset + 2] <= 0xBF)) || // excluding surrogates - (buf[offset + 0] == 0xED && - (0x80 <= buf[offset + 1] && buf[offset + 1] <= 0x9F) && - (0x80 <= buf[offset + 2] && buf[offset + 2] <= 0xBF))) { + if ((len - offset > 2) && + ((buf[offset + 0] == 0xE0 && + (0xA0 <= buf[offset + 1] && buf[offset + 1] <= 0xBF) && + (0x80 <= buf[offset + 2] && + buf[offset + 2] <= 0xBF)) || // straight 3-byte + (((0xE1 <= buf[offset + 0] && buf[offset + 0] <= 0xEC) || + buf[offset + 0] == 0xEE || buf[offset + 0] == 0xEF) && + (0x80 <= buf[offset + 1] && buf[offset + 1] <= 0xBF) && + (0x80 <= buf[offset + 2] && + buf[offset + 2] <= 0xBF)) || // excluding surrogates + (buf[offset + 0] == 0xED && + (0x80 <= buf[offset + 1] && buf[offset + 1] <= 0x9F) && + (0x80 <= buf[offset + 2] && buf[offset + 2] <= 0xBF)))) { offset += 3; utf8++; @@ -170,19 +387,20 @@ static u8 check_if_text(struct queue_entry *q) { } // planes 1-3 - if ((buf[offset + 0] == 0xF0 && - (0x90 <= buf[offset + 1] && buf[offset + 1] <= 0xBF) && - (0x80 <= buf[offset + 2] && buf[offset + 2] <= 0xBF) && - (0x80 <= buf[offset + 3] && - buf[offset + 3] <= 0xBF)) || // planes 4-15 - ((0xF1 <= buf[offset + 0] && buf[offset + 0] <= 0xF3) && - (0x80 <= buf[offset + 1] && buf[offset + 1] <= 0xBF) && - (0x80 <= buf[offset + 2] && buf[offset + 2] <= 0xBF) && - (0x80 <= buf[offset + 3] && buf[offset + 3] <= 0xBF)) || // plane 16 - (buf[offset + 0] == 0xF4 && - (0x80 <= buf[offset + 1] && buf[offset + 1] <= 0x8F) && - (0x80 <= buf[offset + 2] && buf[offset + 2] <= 0xBF) && - (0x80 <= buf[offset + 3] && buf[offset + 3] <= 0xBF))) { + if ((len - offset > 3) && + ((buf[offset + 0] == 0xF0 && + (0x90 <= buf[offset + 1] && buf[offset + 1] <= 0xBF) && + (0x80 <= buf[offset + 2] && buf[offset + 2] <= 0xBF) && + (0x80 <= buf[offset + 3] && + buf[offset + 3] <= 0xBF)) || // planes 4-15 + ((0xF1 <= buf[offset + 0] && buf[offset + 0] <= 0xF3) && + (0x80 <= buf[offset + 1] && buf[offset + 1] <= 0xBF) && + (0x80 <= buf[offset + 2] && buf[offset + 2] <= 0xBF) && + (0x80 <= buf[offset + 3] && buf[offset + 3] <= 0xBF)) || // plane 16 + (buf[offset + 0] == 0xF4 && + (0x80 <= buf[offset + 1] && buf[offset + 1] <= 0x8F) && + (0x80 <= buf[offset + 2] && buf[offset + 2] <= 0xBF) && + (0x80 <= buf[offset + 3] && buf[offset + 3] <= 0xBF)))) { offset += 4; utf8++; @@ -215,37 +433,39 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { q->len = len; q->depth = afl->cur_depth + 1; q->passed_det = passed_det; - q->n_fuzz = 1; q->trace_mini = NULL; + q->testcase_buf = NULL; + q->mother = afl->queue_cur; + +#ifdef INTROSPECTION + q->bitsmap_size = afl->bitsmap_size; +#endif if (q->depth > afl->max_depth) { afl->max_depth = q->depth; } if (afl->queue_top) { - afl->queue_top->next = q; afl->queue_top = q; } else { - afl->q_prev100 = afl->queue = afl->queue_top = q; + afl->queue = afl->queue_top = q; } + if (likely(q->len > 4)) afl->ready_for_splicing_count++; + ++afl->queued_paths; + ++afl->active_paths; ++afl->pending_not_fuzzed; afl->cycles_wo_finds = 0; - if (!(afl->queued_paths % 100)) { - - afl->q_prev100->next_100 = q; - afl->q_prev100 = q; - - } - - struct queue_entry **queue_buf = ck_maybe_grow( - BUF_PARAMS(queue), afl->queued_paths * sizeof(struct queue_entry *)); + struct queue_entry **queue_buf = afl_realloc( + AFL_BUF_PARAM(queue), afl->queued_paths * sizeof(struct queue_entry *)); + if (unlikely(!queue_buf)) { PFATAL("alloc"); } queue_buf[afl->queued_paths - 1] = q; + q->id = afl->queued_paths - 1; afl->last_path_time = get_cur_time(); @@ -269,7 +489,7 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { } /* only redqueen currently uses is_ascii */ - if (afl->shm.cmplog_mode) q->is_ascii = check_if_text(q); + if (afl->shm.cmplog_mode) q->is_ascii = check_if_text(afl, q); } @@ -277,15 +497,16 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { void destroy_queue(afl_state_t *afl) { - struct queue_entry *q = afl->queue, *n; + u32 i; + + for (i = 0; i < afl->queued_paths; i++) { - while (q) { + struct queue_entry *q; - n = q->next; + q = afl->queue_buf[i]; ck_free(q->fname); ck_free(q->trace_mini); ck_free(q); - q = n; } @@ -308,8 +529,10 @@ void update_bitmap_score(afl_state_t *afl, struct queue_entry *q) { u64 fav_factor; u64 fuzz_p2; - if (unlikely(afl->schedule >= FAST && afl->schedule <= RARE)) - fuzz_p2 = next_pow2(q->n_fuzz); + if (unlikely(afl->schedule >= FAST && afl->schedule < RARE)) + fuzz_p2 = 0; // Skip the fuzz_p2 comparison + else if (unlikely(afl->schedule == RARE)) + fuzz_p2 = next_pow2(afl->n_fuzz[q->n_fuzz_entry]); else fuzz_p2 = q->fuzz_level; @@ -335,7 +558,8 @@ void update_bitmap_score(afl_state_t *afl, struct queue_entry *q) { u64 top_rated_fav_factor; u64 top_rated_fuzz_p2; if (unlikely(afl->schedule >= FAST && afl->schedule <= RARE)) - top_rated_fuzz_p2 = next_pow2(afl->top_rated[i]->n_fuzz); + top_rated_fuzz_p2 = + next_pow2(afl->n_fuzz[afl->top_rated[i]->n_fuzz_entry]); else top_rated_fuzz_p2 = afl->top_rated[i]->fuzz_level; @@ -416,12 +640,11 @@ void update_bitmap_score(afl_state_t *afl, struct queue_entry *q) { void cull_queue(afl_state_t *afl) { - struct queue_entry *q; - u32 len = (afl->fsrv.map_size >> 3); - u32 i; - u8 * temp_v = afl->map_tmp_buf; + if (likely(!afl->score_changed || afl->non_instrumented_mode)) { return; } - if (afl->non_instrumented_mode || !afl->score_changed) { return; } + u32 len = (afl->fsrv.map_size >> 3); + u32 i; + u8 *temp_v = afl->map_tmp_buf; afl->score_changed = 0; @@ -430,12 +653,9 @@ void cull_queue(afl_state_t *afl) { afl->queued_favored = 0; afl->pending_favored = 0; - q = afl->queue; - - while (q) { + for (i = 0; i < afl->queued_paths; i++) { - q->favored = 0; - q = q->next; + afl->queue_buf[i]->favored = 0; } @@ -474,12 +694,13 @@ void cull_queue(afl_state_t *afl) { } - q = afl->queue; + for (i = 0; i < afl->queued_paths; i++) { - while (q) { + if (likely(!afl->queue_buf[i]->disabled)) { - mark_as_redundant(afl, q, !q->favored); - q = q->next; + mark_as_redundant(afl, afl->queue_buf[i], !afl->queue_buf[i]->favored); + + } } @@ -505,7 +726,7 @@ u32 calculate_score(afl_state_t *afl, struct queue_entry *q) { // Longer execution time means longer work on the input, the deeper in // coverage, the better the fuzzing, right? -mh - if (afl->schedule >= RARE && likely(!afl->fixed_seed)) { + if (likely(afl->schedule < RARE) && likely(!afl->fixed_seed)) { if (q->exec_us * 0.1 > avg_exec_us) { @@ -606,11 +827,9 @@ u32 calculate_score(afl_state_t *afl, struct queue_entry *q) { } - u64 fuzz = q->n_fuzz; - u64 fuzz_total; - - u32 n_paths, fuzz_mu; - u32 factor = 1; + u32 n_paths; + double factor = 1.0; + long double fuzz_mu; switch (afl->schedule) { @@ -625,60 +844,85 @@ u32 calculate_score(afl_state_t *afl, struct queue_entry *q) { break; case COE: - fuzz_total = 0; + fuzz_mu = 0.0; n_paths = 0; - struct queue_entry *queue_it = afl->queue; - while (queue_it) { + // Don't modify perf_score for unfuzzed seeds + if (q->fuzz_level == 0) break; + + u32 i; + for (i = 0; i < afl->queued_paths; i++) { - fuzz_total += queue_it->n_fuzz; - n_paths++; - queue_it = queue_it->next; + if (likely(!afl->queue_buf[i]->disabled)) { + + fuzz_mu += log2(afl->n_fuzz[afl->queue_buf[i]->n_fuzz_entry]); + n_paths++; + + } } if (unlikely(!n_paths)) { FATAL("Queue state corrupt"); } - fuzz_mu = fuzz_total / n_paths; - if (fuzz <= fuzz_mu) { + fuzz_mu = fuzz_mu / n_paths; - if (q->fuzz_level < 16) { + if (log2(afl->n_fuzz[q->n_fuzz_entry]) > fuzz_mu) { - factor = ((u32)(1 << q->fuzz_level)); + /* Never skip favourites */ + if (!q->favored) factor = 0; - } else { + break; - factor = MAX_FACTOR; + } - } + // Fall through + case FAST: - } else { + // Don't modify unfuzzed seeds + if (q->fuzz_level == 0) break; - factor = 0; + switch ((u32)log2(afl->n_fuzz[q->n_fuzz_entry])) { - } + case 0 ... 1: + factor = 4; + break; - break; + case 2 ... 3: + factor = 3; + break; - case FAST: - if (q->fuzz_level < 16) { + case 4: + factor = 2; + break; - factor = ((u32)(1 << q->fuzz_level)) / (fuzz == 0 ? 1 : fuzz); + case 5: + break; - } else { + case 6: + if (!q->favored) factor = 0.8; + break; - factor = MAX_FACTOR / (fuzz == 0 ? 1 : next_pow2(fuzz)); + case 7: + if (!q->favored) factor = 0.6; + break; + + default: + if (!q->favored) factor = 0.4; + break; } + if (q->favored) factor *= 1.15; + break; case LIN: - factor = q->fuzz_level / (fuzz == 0 ? 1 : fuzz); + factor = q->fuzz_level / (afl->n_fuzz[q->n_fuzz_entry] + 1); break; case QUAD: - factor = q->fuzz_level * q->fuzz_level / (fuzz == 0 ? 1 : fuzz); + factor = + q->fuzz_level * q->fuzz_level / (afl->n_fuzz[q->n_fuzz_entry] + 1); break; case MMOPT: @@ -703,8 +947,8 @@ u32 calculate_score(afl_state_t *afl, struct queue_entry *q) { perf_score += (q->tc_ref * 10); // the more often fuzz result paths are equal to this queue entry, // reduce its value - perf_score *= - (1 - (double)((double)q->n_fuzz / (double)afl->fsrv.total_execs)); + perf_score *= (1 - (double)((double)afl->n_fuzz[q->n_fuzz_entry] / + (double)afl->fsrv.total_execs)); break; @@ -713,7 +957,7 @@ u32 calculate_score(afl_state_t *afl, struct queue_entry *q) { } - if (unlikely(afl->schedule >= FAST && afl->schedule <= RARE)) { + if (unlikely(afl->schedule >= EXPLOIT && afl->schedule <= QUAD)) { if (factor > MAX_FACTOR) { factor = MAX_FACTOR; } perf_score *= factor / POWER_BETA; @@ -725,7 +969,7 @@ u32 calculate_score(afl_state_t *afl, struct queue_entry *q) { perf_score *= 2; - } else if (perf_score < 1) { + } else if (afl->schedule != COE && perf_score < 1) { // Add a lower bound to AFLFast's energy assignment strategies perf_score = 1; @@ -744,3 +988,286 @@ u32 calculate_score(afl_state_t *afl, struct queue_entry *q) { } +/* after a custom trim we need to reload the testcase from disk */ + +inline void queue_testcase_retake(afl_state_t *afl, struct queue_entry *q, + u32 old_len) { + + if (likely(q->testcase_buf)) { + + u32 len = q->len; + + if (len != old_len) { + + afl->q_testcase_cache_size = afl->q_testcase_cache_size + len - old_len; + q->testcase_buf = realloc(q->testcase_buf, len); + + if (unlikely(!q->testcase_buf)) { + + PFATAL("Unable to malloc '%s' with len %u", q->fname, len); + + } + + } + + int fd = open(q->fname, O_RDONLY); + + if (unlikely(fd < 0)) { PFATAL("Unable to open '%s'", q->fname); } + + ck_read(fd, q->testcase_buf, len, q->fname); + close(fd); + + } + +} + +/* after a normal trim we need to replace the testcase with the new data */ + +inline void queue_testcase_retake_mem(afl_state_t *afl, struct queue_entry *q, + u8 *in, u32 len, u32 old_len) { + + if (likely(q->testcase_buf)) { + + u32 is_same = in == q->testcase_buf; + + if (likely(len != old_len)) { + + u8 *ptr = realloc(q->testcase_buf, len); + + if (likely(ptr)) { + + q->testcase_buf = ptr; + afl->q_testcase_cache_size = afl->q_testcase_cache_size + len - old_len; + + } + + } + + if (unlikely(!is_same)) { memcpy(q->testcase_buf, in, len); } + + } + +} + +/* Returns the testcase buf from the file behind this queue entry. + Increases the refcount. */ + +inline u8 *queue_testcase_get(afl_state_t *afl, struct queue_entry *q) { + + u32 len = q->len; + + /* first handle if no testcase cache is configured */ + + if (unlikely(!afl->q_testcase_max_cache_size)) { + + u8 *buf; + + if (unlikely(q == afl->queue_cur)) { + + buf = afl_realloc((void **)&afl->testcase_buf, len); + + } else { + + buf = afl_realloc((void **)&afl->splicecase_buf, len); + + } + + if (unlikely(!buf)) { + + PFATAL("Unable to malloc '%s' with len %u", q->fname, len); + + } + + int fd = open(q->fname, O_RDONLY); + + if (unlikely(fd < 0)) { PFATAL("Unable to open '%s'", q->fname); } + + ck_read(fd, buf, len, q->fname); + close(fd); + return buf; + + } + + /* now handle the testcase cache */ + + if (unlikely(!q->testcase_buf)) { + + /* Buf not cached, let's load it */ + u32 tid = afl->q_testcase_max_cache_count; + static u32 do_once = 0; // because even threaded we would want this. WIP + + while (unlikely( + afl->q_testcase_cache_size + len >= afl->q_testcase_max_cache_size || + afl->q_testcase_cache_count >= afl->q_testcase_max_cache_entries - 1)) { + + /* We want a max number of entries to the cache that we learn. + Very simple: once the cache is filled by size - that is the max. */ + + if (unlikely(afl->q_testcase_cache_size + len >= + afl->q_testcase_max_cache_size && + (afl->q_testcase_cache_count < + afl->q_testcase_max_cache_entries && + afl->q_testcase_max_cache_count < + afl->q_testcase_max_cache_entries) && + !do_once)) { + + if (afl->q_testcase_max_cache_count > afl->q_testcase_cache_count) { + + afl->q_testcase_max_cache_entries = + afl->q_testcase_max_cache_count + 1; + + } else { + + afl->q_testcase_max_cache_entries = afl->q_testcase_cache_count + 1; + + } + + do_once = 1; + // release unneeded memory + u8 *ptr = ck_realloc( + afl->q_testcase_cache, + (afl->q_testcase_max_cache_entries + 1) * sizeof(size_t)); + + if (ptr) { afl->q_testcase_cache = (struct queue_entry **)ptr; } + + } + + /* Cache full. We neet to evict one or more to map one. + Get a random one which is not in use */ + + do { + + // if the cache (MB) is not enough for the queue then this gets + // undesirable because q_testcase_max_cache_count grows sometimes + // although the number of items in the cache will not change hence + // more and more loops + tid = rand_below(afl, afl->q_testcase_max_cache_count); + + } while (afl->q_testcase_cache[tid] == NULL || + + afl->q_testcase_cache[tid] == afl->queue_cur); + + struct queue_entry *old_cached = afl->q_testcase_cache[tid]; + free(old_cached->testcase_buf); + old_cached->testcase_buf = NULL; + afl->q_testcase_cache_size -= old_cached->len; + afl->q_testcase_cache[tid] = NULL; + --afl->q_testcase_cache_count; + ++afl->q_testcase_evictions; + if (tid < afl->q_testcase_smallest_free) + afl->q_testcase_smallest_free = tid; + + } + + if (unlikely(tid >= afl->q_testcase_max_cache_entries)) { + + // uh we were full, so now we have to search from start + tid = afl->q_testcase_smallest_free; + + } + + // we need this while loop in case there were ever previous evictions but + // not in this call. + while (unlikely(afl->q_testcase_cache[tid] != NULL)) + ++tid; + + /* Map the test case into memory. */ + + int fd = open(q->fname, O_RDONLY); + + if (unlikely(fd < 0)) { PFATAL("Unable to open '%s'", q->fname); } + + q->testcase_buf = malloc(len); + + if (unlikely(!q->testcase_buf)) { + + PFATAL("Unable to malloc '%s' with len %u", q->fname, len); + + } + + ck_read(fd, q->testcase_buf, len, q->fname); + close(fd); + + /* Register testcase as cached */ + afl->q_testcase_cache[tid] = q; + afl->q_testcase_cache_size += len; + ++afl->q_testcase_cache_count; + if (likely(tid >= afl->q_testcase_max_cache_count)) { + + afl->q_testcase_max_cache_count = tid + 1; + + } else if (unlikely(tid == afl->q_testcase_smallest_free)) { + + afl->q_testcase_smallest_free = tid + 1; + + } + + } + + return q->testcase_buf; + +} + +/* Adds the new queue entry to the cache. */ + +inline void queue_testcase_store_mem(afl_state_t *afl, struct queue_entry *q, + u8 *mem) { + + u32 len = q->len; + + if (unlikely(afl->q_testcase_cache_size + len >= + afl->q_testcase_max_cache_size || + afl->q_testcase_cache_count >= + afl->q_testcase_max_cache_entries - 1)) { + + // no space? will be loaded regularly later. + return; + + } + + u32 tid; + + if (unlikely(afl->q_testcase_max_cache_count >= + afl->q_testcase_max_cache_entries)) { + + // uh we were full, so now we have to search from start + tid = afl->q_testcase_smallest_free; + + } else { + + tid = afl->q_testcase_max_cache_count; + + } + + while (unlikely(afl->q_testcase_cache[tid] != NULL)) + ++tid; + + /* Map the test case into memory. */ + + q->testcase_buf = malloc(len); + + if (unlikely(!q->testcase_buf)) { + + PFATAL("Unable to malloc '%s' with len %u", q->fname, len); + + } + + memcpy(q->testcase_buf, mem, len); + + /* Register testcase as cached */ + afl->q_testcase_cache[tid] = q; + afl->q_testcase_cache_size += len; + ++afl->q_testcase_cache_count; + + if (likely(tid >= afl->q_testcase_max_cache_count)) { + + afl->q_testcase_max_cache_count = tid + 1; + + } else if (unlikely(tid == afl->q_testcase_smallest_free)) { + + afl->q_testcase_smallest_free = tid + 1; + + } + +} + diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 57e60c3d..9bfbf95b 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -28,13 +28,39 @@ #include "afl-fuzz.h" #include "cmplog.h" -///// Colorization +//#define _DEBUG +//#define CMPLOG_INTROSPECTION + +// CMP attribute enum +enum { + + IS_EQUAL = 1, // arithemtic equal comparison + IS_GREATER = 2, // arithmetic greater comparison + IS_LESSER = 4, // arithmetic lesser comparison + IS_FP = 8, // is a floating point, not an integer + /* --- below are internal settings, not from target cmplog */ + IS_FP_MOD = 16, // arithemtic changed floating point + IS_INT_MOD = 32, // arithmetic changed interger + IS_TRANSFORM = 64 // transformed integer + +}; + +// CMPLOG LVL +enum { + + LVL1 = 1, // Integer solving + LVL2 = 2, // unused except for setting the queue entry + LVL3 = 4 // expensive tranformations + +}; struct range { u32 start; u32 end; struct range *next; + struct range *prev; + u8 ok; }; @@ -44,6 +70,8 @@ static struct range *add_range(struct range *ranges, u32 start, u32 end) { r->start = start; r->end = end; r->next = ranges; + r->ok = 0; + if (likely(ranges)) ranges->prev = r; return r; } @@ -51,136 +79,320 @@ static struct range *add_range(struct range *ranges, u32 start, u32 end) { static struct range *pop_biggest_range(struct range **ranges) { struct range *r = *ranges; - struct range *prev = NULL; struct range *rmax = NULL; - struct range *prev_rmax = NULL; u32 max_size = 0; while (r) { - u32 s = r->end - r->start; - if (s >= max_size) { + if (!r->ok) { + + u32 s = 1 + r->end - r->start; + + if (s >= max_size) { - max_size = s; - prev_rmax = prev; - rmax = r; + max_size = s; + rmax = r; + + } } - prev = r; r = r->next; } - if (rmax) { + return rmax; + +} - if (prev_rmax) { +#ifdef _DEBUG +// static int logging = 0; +static void dump(char *txt, u8 *buf, u32 len) { - prev_rmax->next = rmax->next; + u32 i; + fprintf(stderr, "DUMP %s %016llx ", txt, hash64(buf, len, HASH_CONST)); + for (i = 0; i < len; i++) + fprintf(stderr, "%02x", buf[i]); + fprintf(stderr, "\n"); - } else { +} - *ranges = rmax->next; +static void dump_file(char *path, char *name, u32 counter, u8 *buf, u32 len) { - } + char fn[4096]; + if (!path) path = "."; + snprintf(fn, sizeof(fn), "%s/%s%d", path, name, counter); + int fd = open(fn, O_RDWR | O_CREAT | O_TRUNC, 0644); + if (fd >= 0) { - } + write(fd, buf, len); + close(fd); - return rmax; + } } +#endif + static u8 get_exec_checksum(afl_state_t *afl, u8 *buf, u32 len, u64 *cksum) { if (unlikely(common_fuzz_stuff(afl, buf, len))) { return 1; } *cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); + return 0; } -static void rand_replace(afl_state_t *afl, u8 *buf, u32 len) { +/* replace everything with different values but stay in the same type */ +static void type_replace(afl_state_t *afl, u8 *buf, u32 len) { u32 i; + u8 c; for (i = 0; i < len; ++i) { - buf[i] = rand_below(afl, 256); + // wont help for UTF or non-latin charsets + do { + + switch (buf[i]) { + + case 'A' ... 'F': + c = 'A' + rand_below(afl, 1 + 'F' - 'A'); + break; + case 'a' ... 'f': + c = 'a' + rand_below(afl, 1 + 'f' - 'a'); + break; + case '0': + c = '1'; + break; + case '1': + c = '0'; + break; + case '2' ... '9': + c = '2' + rand_below(afl, 1 + '9' - '2'); + break; + case 'G' ... 'Z': + c = 'G' + rand_below(afl, 1 + 'Z' - 'G'); + break; + case 'g' ... 'z': + c = 'g' + rand_below(afl, 1 + 'z' - 'g'); + break; + case '!' ... '*': + c = '!' + rand_below(afl, 1 + '*' - '!'); + break; + case ',' ... '.': + c = ',' + rand_below(afl, 1 + '.' - ','); + break; + case ':' ... '@': + c = ':' + rand_below(afl, 1 + '@' - ':'); + break; + case '[' ... '`': + c = '[' + rand_below(afl, 1 + '`' - '['); + break; + case '{' ... '~': + c = '{' + rand_below(afl, 1 + '~' - '{'); + break; + case '+': + c = '/'; + break; + case '/': + c = '+'; + break; + case ' ': + c = '\t'; + break; + case '\t': + c = ' '; + break; + case '\r': + c = '\n'; + break; + case '\n': + c = '\r'; + break; + case 0: + c = 1; + break; + case 1: + c = 0; + break; + case 0xff: + c = 0; + break; + default: + if (buf[i] < 32) { + + c = (buf[i] ^ 0x1f); + + } else { + + c = (buf[i] ^ 0x7f); // we keep the highest bit + + } + + } + + } while (c == buf[i]); + + buf[i] = c; } } -static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, u64 exec_cksum) { +static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, + struct tainted **taints) { - struct range *ranges = add_range(NULL, 0, len); - u8 * backup = ck_alloc_nozero(len); + struct range * ranges = add_range(NULL, 0, len - 1), *rng; + struct tainted *taint = NULL; + u8 * backup = ck_alloc_nozero(len); + u8 * changed = ck_alloc_nozero(len); - u8 needs_write = 0; +#if defined(_DEBUG) || defined(CMPLOG_INTROSPECTION) + u64 start_time = get_cur_time(); +#endif - u64 orig_hit_cnt, new_hit_cnt; + u32 screen_update = 1000000 / afl->queue_cur->exec_us; + u64 orig_hit_cnt, new_hit_cnt, exec_cksum; orig_hit_cnt = afl->queued_paths + afl->unique_crashes; afl->stage_name = "colorization"; afl->stage_short = "colorization"; - afl->stage_max = 1000; - - struct range *rng = NULL; + afl->stage_max = (len << 1); afl->stage_cur = 0; + + // in colorization we do not classify counts, hence we have to calculate + // the original checksum. + if (unlikely(get_exec_checksum(afl, buf, len, &exec_cksum))) { + + goto checksum_fail; + + } + + memcpy(backup, buf, len); + memcpy(changed, buf, len); + type_replace(afl, changed, len); + while ((rng = pop_biggest_range(&ranges)) != NULL && afl->stage_cur < afl->stage_max) { - u32 s = rng->end - rng->start; + u32 s = 1 + rng->end - rng->start; + + memcpy(buf + rng->start, changed + rng->start, s); + + u64 cksum = 0; + u64 start_us = get_cur_time_us(); + if (unlikely(get_exec_checksum(afl, buf, len, &cksum))) { - if (s != 0) { + goto checksum_fail; - /* Range not empty */ + } + + u64 stop_us = get_cur_time_us(); - memcpy(backup, buf + rng->start, s); - rand_replace(afl, buf + rng->start, s); + /* Discard if the mutations change the path or if it is too decremental + in speed - how could the same path have a much different speed + though ...*/ + if (cksum != exec_cksum || + (unlikely(stop_us - start_us > 3 * afl->queue_cur->exec_us) && + likely(!afl->fixed_seed))) { - u64 cksum; - u64 start_us = get_cur_time_us(); - if (unlikely(get_exec_checksum(afl, buf, len, &cksum))) { + memcpy(buf + rng->start, backup + rng->start, s); - goto checksum_fail; + if (s > 1) { // to not add 0 size ranges + + ranges = add_range(ranges, rng->start, rng->start - 1 + s / 2); + ranges = add_range(ranges, rng->start + s / 2, rng->end); } - u64 stop_us = get_cur_time_us(); + if (ranges == rng) { + + ranges = rng->next; + if (ranges) { ranges->prev = NULL; } - /* Discard if the mutations change the paths or if it is too decremental - in speed */ - if (cksum != exec_cksum || - (stop_us - start_us > 2 * afl->queue_cur->exec_us)) { + } else if (rng->next) { - ranges = add_range(ranges, rng->start, rng->start + s / 2); - ranges = add_range(ranges, rng->start + s / 2 + 1, rng->end); - memcpy(buf + rng->start, backup, s); + rng->prev->next = rng->next; + rng->next->prev = rng->prev; } else { - needs_write = 1; + if (rng->prev) { rng->prev->next = NULL; } } + free(rng); + + } else { + + rng->ok = 1; + } - ck_free(rng); - rng = NULL; - ++afl->stage_cur; + if (++afl->stage_cur % screen_update) { show_stats(afl); }; } - if (afl->stage_cur < afl->stage_max) { afl->queue_cur->fully_colorized = 1; } + rng = ranges; + while (rng) { - new_hit_cnt = afl->queued_paths + afl->unique_crashes; - afl->stage_finds[STAGE_COLORIZATION] += new_hit_cnt - orig_hit_cnt; - afl->stage_cycles[STAGE_COLORIZATION] += afl->stage_cur; - ck_free(backup); + rng = rng->next; + + } + + u32 i = 1; + u32 positions = 0; + while (i) { + + restart: + i = 0; + struct range *r = NULL; + u32 pos = (u32)-1; + rng = ranges; + + while (rng) { + + if (rng->ok == 1 && rng->start < pos) { + + if (taint && taint->pos + taint->len == rng->start) { + + taint->len += (1 + rng->end - rng->start); + positions += (1 + rng->end - rng->start); + rng->ok = 2; + goto restart; + + } else { + + r = rng; + pos = rng->start; + + } - ck_free(rng); - rng = NULL; + } + + rng = rng->next; + + } + if (r) { + + struct tainted *t = ck_alloc_nozero(sizeof(struct tainted)); + t->pos = r->start; + t->len = 1 + r->end - r->start; + positions += (1 + r->end - r->start); + if (likely(taint)) { taint->prev = t; } + t->next = taint; + t->prev = NULL; + taint = t; + r->ok = 2; + i = 1; + + } + + } + + /* temporary: clean ranges */ while (ranges) { rng = ranges; @@ -190,38 +402,77 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, u64 exec_cksum) { } - // save the input with the high entropy + new_hit_cnt = afl->queued_paths + afl->unique_crashes; - if (needs_write) { +#if defined(_DEBUG) || defined(CMPLOG_INTROSPECTION) + FILE *f = stderr; + #ifndef _DEBUG + if (afl->not_on_tty) { - s32 fd; + char fn[4096]; + snprintf(fn, sizeof(fn), "%s/introspection_cmplog.txt", afl->out_dir); + f = fopen(fn, "a"); - if (afl->no_unlink) { + } - fd = open(afl->queue_cur->fname, O_WRONLY | O_CREAT | O_TRUNC, 0600); + #endif - } else { + if (f) { - unlink(afl->queue_cur->fname); /* ignore errors */ - fd = open(afl->queue_cur->fname, O_WRONLY | O_CREAT | O_EXCL, 0600); + fprintf( + f, + "Colorization: fname=%s len=%u ms=%llu result=%u execs=%u found=%llu " + "taint=%u\n", + afl->queue_cur->fname, len, get_cur_time() - start_time, + afl->queue_cur->colorized, afl->stage_cur, new_hit_cnt - orig_hit_cnt, + positions); - } + #ifndef _DEBUG + if (afl->not_on_tty) { fclose(f); } + #endif + + } - if (fd < 0) { PFATAL("Unable to create '%s'", afl->queue_cur->fname); } +#endif - ck_write(fd, buf, len, afl->queue_cur->fname); - afl->queue_cur->len = len; // no-op, just to be 100% safe + if (taint) { - close(fd); + if (afl->colorize_success && + (len / positions == 1 && positions > CMPLOG_POSITIONS_MAX && + afl->active_paths / afl->colorize_success > CMPLOG_CORPUS_PERCENT)) { + +#ifdef _DEBUG + fprintf(stderr, "Colorization unsatisfactory\n"); +#endif + + *taints = NULL; + + struct tainted *t; + while (taint) { + + t = taint->next; + ck_free(taint); + taint = t; + + } + + } else { + + *taints = taint; + ++afl->colorize_success; + + } } + afl->stage_finds[STAGE_COLORIZATION] += new_hit_cnt - orig_hit_cnt; + afl->stage_cycles[STAGE_COLORIZATION] += afl->stage_cur; + ck_free(backup); + ck_free(changed); + return 0; checksum_fail: - if (rng) { ck_free(rng); } - ck_free(backup); - while (ranges) { rng = ranges; @@ -231,7 +482,8 @@ checksum_fail: } - // TODO: clang notices a _potential_ leak of mem pointed to by rng + ck_free(backup); + ck_free(changed); return 1; @@ -245,12 +497,19 @@ static u8 its_fuzz(afl_state_t *afl, u8 *buf, u32 len, u8 *status) { orig_hit_cnt = afl->queued_paths + afl->unique_crashes; +#ifdef _DEBUG + dump("DATA", buf, len); +#endif + if (unlikely(common_fuzz_stuff(afl, buf, len))) { return 1; } new_hit_cnt = afl->queued_paths + afl->unique_crashes; if (unlikely(new_hit_cnt != orig_hit_cnt)) { +#ifdef _DEBUG + fprintf(stderr, "NEW FIND\n"); +#endif *status = 1; } else { @@ -263,62 +522,213 @@ static u8 its_fuzz(afl_state_t *afl, u8 *buf, u32 len, u8 *status) { } -static long long strntoll(const char *str, size_t sz, char **end, int base) { +//#ifdef CMPLOG_SOLVE_TRANSFORM +static int strntoll(const char *str, size_t sz, char **end, int base, + long long *out) { char buf[64]; long long ret; const char *beg = str; - for (; beg && sz && *beg == ' '; beg++, sz--) - ; - - if (!sz || sz >= sizeof(buf)) { + if (!str || !sz) { return 1; } - if (end) *end = (char *)str; - return 0; + for (; beg && sz && *beg == ' '; beg++, sz--) {}; - } + if (!sz) return 1; + if (sz >= sizeof(buf)) sz = sizeof(buf) - 1; memcpy(buf, beg, sz); buf[sz] = '\0'; ret = strtoll(buf, end, base); - if (ret == LLONG_MIN || ret == LLONG_MAX) return ret; + if ((ret == LLONG_MIN || ret == LLONG_MAX) && errno == ERANGE) return 1; if (end) *end = (char *)beg + (*end - buf); - return ret; + *out = ret; + + return 0; } -static unsigned long long strntoull(const char *str, size_t sz, char **end, - int base) { +static int strntoull(const char *str, size_t sz, char **end, int base, + unsigned long long *out) { char buf[64]; unsigned long long ret; const char * beg = str; + if (!str || !sz) { return 1; } + for (; beg && sz && *beg == ' '; beg++, sz--) ; - if (!sz || sz >= sizeof(buf)) { - - if (end) *end = (char *)str; - return 0; - - } + if (!sz) return 1; + if (sz >= sizeof(buf)) sz = sizeof(buf) - 1; memcpy(buf, beg, sz); buf[sz] = '\0'; ret = strtoull(buf, end, base); + if (ret == ULLONG_MAX && errno == ERANGE) return 1; if (end) *end = (char *)beg + (*end - buf); + *out = ret; + + return 0; + +} + +static u8 hex_table_up[16] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; +static u8 hex_table_low[16] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; +static u8 hex_table[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, + 0, 0, 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13, 14, 15}; + +// tests 2 bytes at location +static int is_hex(const char *str) { + + u32 i; + + for (i = 0; i < 2; i++) { + + switch (str[i]) { + + case '0' ... '9': + case 'A' ... 'F': + case 'a' ... 'f': + break; + default: + return 0; + + } + + } + + return 1; + +} + +#ifdef CMPLOG_SOLVE_TRANSFORM_BASE64 +// tests 4 bytes at location +static int is_base64(const char *str) { + + u32 i; + + for (i = 0; i < 4; i++) { + + switch (str[i]) { + + case '0' ... '9': + case 'A' ... 'Z': + case 'a' ... 'z': + case '+': + case '/': + case '=': + break; + default: + return 0; + + } + + } + + return 1; + +} + +static u8 base64_encode_table[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static u8 base64_decode_table[] = { + + 62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51}; + +static u32 from_base64(u8 *src, u8 *dst, u32 dst_len) { + + u32 i, j, v; + u32 len = ((dst_len / 3) << 2); + u32 ret = 0; + + for (i = 0, j = 0; i < len; i += 4, j += 3) { + + v = base64_decode_table[src[i] - 43]; + v = (v << 6) | base64_decode_table[src[i + 1] - 43]; + v = src[i + 2] == '=' ? v << 6 + : (v << 6) | base64_decode_table[src[i + 2] - 43]; + v = src[i + 3] == '=' ? v << 6 + : (v << 6) | base64_decode_table[src[i + 3] - 43]; + + dst[j] = (v >> 16) & 0xFF; + ++ret; + + if (src[i + 2] != '=') { + + dst[j + 1] = (v >> 8) & 0xFF; + ++ret; + + } + + if (src[i + 3] != '=') { + + dst[j + 2] = v & 0xFF; + ++ret; + + } + + } + return ret; } -#define BUF_PARAMS(name) (void **)&afl->name##_buf, &afl->name##_size +static void to_base64(u8 *src, u8 *dst, u32 dst_len) { + + u32 i, j, v; + u32 len = (dst_len >> 2) * 3; + + for (i = 0, j = 0; i < len; i += 3, j += 4) { + + v = src[i]; + v = i + 1 < len ? v << 8 | src[i + 1] : v << 8; + v = i + 2 < len ? v << 8 | src[i + 2] : v << 8; + + dst[j] = base64_encode_table[(v >> 18) & 0x3F]; + dst[j + 1] = base64_encode_table[(v >> 12) & 0x3F]; + if (i + 1 < len) { + + dst[j + 2] = base64_encode_table[(v >> 6) & 0x3F]; + + } else { + + dst[j + 2] = '='; + + } + + if (i + 2 < len) { + + dst[j + 3] = base64_encode_table[v & 0x3F]; + + } else { + + dst[j + 3] = '='; + + } + + } + +} + +#endif + +//#endif static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, - u64 pattern, u64 repl, u64 o_pattern, u32 idx, - u8 *orig_buf, u8 *buf, u32 len, u8 do_reverse, - u8 *status) { + u64 pattern, u64 repl, u64 o_pattern, + u64 changed_val, u8 attr, u32 idx, u32 taint_len, + u8 *orig_buf, u8 *buf, u8 *cbuf, u32 len, + u8 do_reverse, u8 lvl, u8 *status) { u64 *buf_64 = (u64 *)&buf[idx]; u32 *buf_32 = (u32 *)&buf[idx]; @@ -329,74 +739,468 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, u16 *o_buf_16 = (u16 *)&orig_buf[idx]; u8 * o_buf_8 = &orig_buf[idx]; - u32 its_len = len - idx; - // *status = 0; + u32 its_len = MIN(len - idx, taint_len); - u8 * endptr; - u8 use_num = 0, use_unum = 0; - unsigned long long unum; - long long num; - if (afl->queue_cur->is_ascii) { + // fprintf(stderr, + // "Encode: %llx->%llx into %llx(<-%llx) at idx=%u " + // "taint_len=%u shape=%u attr=%u\n", + // o_pattern, pattern, repl, changed_val, idx, taint_len, + // h->shape + 1, attr); - endptr = buf_8; - num = strntoll(buf_8, len - idx, (char **)&endptr, 0); - if (endptr == buf_8) { + //#ifdef CMPLOG_SOLVE_TRANSFORM + // reverse atoi()/strnu?toll() is expensive, so we only to it in lvl 3 + if (afl->cmplog_enable_transform && (lvl & LVL3)) { - unum = strntoull(buf_8, len - idx, (char **)&endptr, 0); - if (endptr == buf_8) use_unum = 1; + u8 * endptr; + u8 use_num = 0, use_unum = 0; + unsigned long long unum; + long long num; - } else + if (afl->queue_cur->is_ascii) { - use_num = 1; + endptr = buf_8; + if (strntoll(buf_8, len - idx, (char **)&endptr, 0, &num)) { - } + if (!strntoull(buf_8, len - idx, (char **)&endptr, 0, &unum)) + use_unum = 1; + + } else + + use_num = 1; + + } + +#ifdef _DEBUG + if (idx == 0) + fprintf(stderr, "ASCII is=%u use_num=%u use_unum=%u idx=%u %llx==%llx\n", + afl->queue_cur->is_ascii, use_num, use_unum, idx, num, pattern); +#endif + + // num is likely not pattern as atoi("AAA") will be zero... + if (use_num && ((u64)num == pattern || !num)) { + + u8 tmp_buf[32]; + size_t num_len = snprintf(tmp_buf, sizeof(tmp_buf), "%lld", repl); + size_t old_len = endptr - buf_8; + + u8 *new_buf = afl_realloc((void **)&afl->out_scratch_buf, len + num_len); + if (unlikely(!new_buf)) { PFATAL("alloc"); } + + memcpy(new_buf, buf, idx); + memcpy(new_buf + idx, tmp_buf, num_len); + memcpy(new_buf + idx + num_len, buf_8 + old_len, len - idx - old_len); + + if (new_buf[idx + num_len] >= '0' && new_buf[idx + num_len] <= '9') { + + new_buf[idx + num_len] = ' '; + + } + + if (unlikely(its_fuzz(afl, new_buf, len, status))) { return 1; } + + } else if (use_unum && (unum == pattern || !unum)) { - if (use_num && num == pattern) { + u8 tmp_buf[32]; + size_t num_len = snprintf(tmp_buf, sizeof(tmp_buf), "%llu", repl); + size_t old_len = endptr - buf_8; - size_t old_len = endptr - buf_8; - size_t num_len = snprintf(NULL, 0, "%lld", num); + u8 *new_buf = afl_realloc((void **)&afl->out_scratch_buf, len + num_len); + if (unlikely(!new_buf)) { PFATAL("alloc"); } - u8 *new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), len + num_len); - memcpy(new_buf, buf, idx); + memcpy(new_buf, buf, idx); + memcpy(new_buf + idx, tmp_buf, num_len); + memcpy(new_buf + idx + num_len, buf_8 + old_len, len - idx - old_len); - snprintf(new_buf + idx, num_len, "%lld", num); - memcpy(new_buf + idx + num_len, buf_8 + old_len, len - idx - old_len); + if (new_buf[idx + num_len] >= '0' && new_buf[idx + num_len] <= '9') { - if (unlikely(its_fuzz(afl, new_buf, len, status))) { return 1; } + new_buf[idx + num_len] = ' '; - } else if (use_unum && unum == pattern) { + } + + if (unlikely(its_fuzz(afl, new_buf, len, status))) { return 1; } + + } + + // Try to identify transform magic + if (pattern != o_pattern && repl == changed_val && attr <= IS_EQUAL) { + + u64 b_val, o_b_val, mask; + u8 bytes; + + switch (SHAPE_BYTES(h->shape)) { + + case 0: + case 1: + bytes = 1; + break; + case 2: + bytes = 2; + break; + case 3: + case 4: + bytes = 4; + break; + default: + bytes = 8; + + } + + // necessary for preventing heap access overflow + bytes = MIN(bytes, len - idx); + + switch (bytes) { + + case 0: // cannot happen + b_val = o_b_val = mask = 0; // keep the linters happy + break; + case 1: { + + u8 *ptr = (u8 *)&buf[idx]; + u8 *o_ptr = (u8 *)&orig_buf[idx]; + b_val = (u64)(*ptr); + o_b_val = (u64)(*o_ptr % 0x100); + mask = 0xff; + break; + + } + + case 2: + case 3: { + + u16 *ptr = (u16 *)&buf[idx]; + u16 *o_ptr = (u16 *)&orig_buf[idx]; + b_val = (u64)(*ptr); + o_b_val = (u64)(*o_ptr); + mask = 0xffff; + break; + + } + + case 4: + case 5: + case 6: + case 7: { + + u32 *ptr = (u32 *)&buf[idx]; + u32 *o_ptr = (u32 *)&orig_buf[idx]; + b_val = (u64)(*ptr); + o_b_val = (u64)(*o_ptr); + mask = 0xffffffff; + break; + + } + + default: { + + u64 *ptr = (u64 *)&buf[idx]; + u64 *o_ptr = (u64 *)&orig_buf[idx]; + b_val = (u64)(*ptr); + o_b_val = (u64)(*o_ptr); + mask = 0xffffffffffffffff; + + } + + } + + // test for arithmetic, eg. "if ((user_val - 0x1111) == 0x1234) ..." + s64 diff = pattern - b_val; + s64 o_diff = o_pattern - o_b_val; + /* + fprintf(stderr, "DIFF1 idx=%03u shape=%02u %llx-%llx=%lx\n", idx, + h->shape + 1, o_pattern, o_b_val, o_diff); + fprintf(stderr, "DIFF1 %016llx %llx-%llx=%lx\n", repl, pattern, + b_val, diff);*/ + if (diff == o_diff && diff) { + + // this could be an arithmetic transformation + + u64 new_repl = (u64)((s64)repl - diff); + // fprintf(stderr, "SAME DIFF %llx->%llx\n", repl, new_repl); + + if (unlikely(cmp_extend_encoding( + afl, h, pattern, new_repl, o_pattern, repl, IS_TRANSFORM, idx, + taint_len, orig_buf, buf, cbuf, len, 1, lvl, status))) { + + return 1; + + } + + // if (*status == 1) { fprintf(stderr, "FOUND!\n"); } + + } + + // test for XOR, eg. "if ((user_val ^ 0xabcd) == 0x1234) ..." + if (*status != 1) { + + diff = pattern ^ b_val; + s64 o_diff = o_pattern ^ o_b_val; + + /* fprintf(stderr, "DIFF2 idx=%03u shape=%02u %llx-%llx=%lx\n", + idx, h->shape + 1, o_pattern, o_b_val, o_diff); fprintf(stderr, + "DIFF2 %016llx %llx-%llx=%lx\n", repl, pattern, b_val, diff);*/ + if (diff == o_diff && diff) { + + // this could be a XOR transformation + + u64 new_repl = (u64)((s64)repl ^ diff); + // fprintf(stderr, "SAME DIFF %llx->%llx\n", repl, new_repl); + + if (unlikely(cmp_extend_encoding( + afl, h, pattern, new_repl, o_pattern, repl, IS_TRANSFORM, idx, + taint_len, orig_buf, buf, cbuf, len, 1, lvl, status))) { + + return 1; + + } + + // if (*status == 1) { fprintf(stderr, "FOUND!\n"); } + + } + + } + + // test for to lowercase, eg. "new_val = (user_val | 0x2020) ..." + if (*status != 1) { + + if ((b_val | (0x2020202020202020 & mask)) == (pattern & mask)) { + + diff = 1; + + } else { + + diff = 0; - size_t old_len = endptr - buf_8; - size_t num_len = snprintf(NULL, 0, "%llu", unum); + } - u8 *new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), len + num_len); - memcpy(new_buf, buf, idx); + if ((o_b_val | (0x2020202020202020 & mask)) == (o_pattern & mask)) { - snprintf(new_buf + idx, num_len, "%llu", unum); - memcpy(new_buf + idx + num_len, buf_8 + old_len, len - idx - old_len); + o_diff = 1; - if (unlikely(its_fuzz(afl, new_buf, len, status))) { return 1; } + } else { + + diff = 0; + + } + + /* fprintf(stderr, "DIFF3 idx=%03u shape=%02u %llx-%llx=%lx\n", + idx, h->shape + 1, o_pattern, o_b_val, o_diff); fprintf(stderr, + "DIFF3 %016llx %llx-%llx=%lx\n", repl, pattern, b_val, diff);*/ + if (o_diff && diff) { + + // this could be a lower to upper + + u64 new_repl = (repl & (0x5f5f5f5f5f5f5f5f & mask)); + // fprintf(stderr, "SAME DIFF %llx->%llx\n", repl, new_repl); + + if (unlikely(cmp_extend_encoding( + afl, h, pattern, new_repl, o_pattern, repl, IS_TRANSFORM, idx, + taint_len, orig_buf, buf, cbuf, len, 1, lvl, status))) { + + return 1; + + } + + // if (*status == 1) { fprintf(stderr, "FOUND!\n"); } + + } + + } + + // test for to uppercase, eg. "new_val = (user_val | 0x5f5f) ..." + if (*status != 1) { + + if ((b_val & (0x5f5f5f5f5f5f5f5f & mask)) == (pattern & mask)) { + + diff = 1; + + } else { + + diff = 0; + + } + + if ((o_b_val & (0x5f5f5f5f5f5f5f5f & mask)) == (o_pattern & mask)) { + + o_diff = 1; + + } else { + + o_diff = 0; + + } + + /* fprintf(stderr, "DIFF4 idx=%03u shape=%02u %llx-%llx=%lx\n", + idx, h->shape + 1, o_pattern, o_b_val, o_diff); fprintf(stderr, + "DIFF4 %016llx %llx-%llx=%lx\n", repl, pattern, b_val, diff);*/ + if (o_diff && diff) { + + // this could be a lower to upper + + u64 new_repl = (repl | (0x2020202020202020 & mask)); + // fprintf(stderr, "SAME DIFF %llx->%llx\n", repl, new_repl); + + if (unlikely(cmp_extend_encoding( + afl, h, pattern, new_repl, o_pattern, repl, IS_TRANSFORM, idx, + taint_len, orig_buf, buf, cbuf, len, 1, lvl, status))) { + + return 1; + + } + + // if (*status == 1) { fprintf(stderr, "FOUND!\n"); } + + } + + } + + *status = 0; + + } } - if (SHAPE_BYTES(h->shape) >= 8 && *status != 1) { + //#endif - if (its_len >= 8 && *buf_64 == pattern && *o_buf_64 == o_pattern) { + // we only allow this for ascii2integer (above) so leave if this is the case + if (unlikely(pattern == o_pattern)) { return 0; } - *buf_64 = repl; - if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } - *buf_64 = pattern; + if ((lvl & LVL1) || attr >= IS_FP_MOD) { + + if (SHAPE_BYTES(h->shape) >= 8 && *status != 1) { + + // if (its_len >= 8) + // fprintf(stderr, + // "TestU64: %u>=8 (idx=%u attr=%u) %llx==%llx" + // " %llx==%llx <= %llx<-%llx\n", + // its_len, idx, attr, *buf_64, pattern, *o_buf_64, o_pattern, + // repl, changed_val); + + // if this is an fcmp (attr & 8 == 8) then do not compare the patterns - + // due to a bug in llvm dynamic float bitcasts do not work :( + // the value 16 means this is a +- 1.0 test case + if (its_len >= 8 && ((*buf_64 == pattern && *o_buf_64 == o_pattern) || + attr >= IS_FP_MOD)) { + + u64 tmp_64 = *buf_64; + *buf_64 = repl; + if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } +#ifdef CMPLOG_COMBINE + if (*status == 1) { memcpy(cbuf + idx, buf_64, 8); } +#endif + *buf_64 = tmp_64; + + // fprintf(stderr, "Status=%u\n", *status); + + } + + // reverse encoding + if (do_reverse && *status != 1) { + + if (unlikely(cmp_extend_encoding(afl, h, SWAP64(pattern), SWAP64(repl), + SWAP64(o_pattern), SWAP64(changed_val), + attr, idx, taint_len, orig_buf, buf, + cbuf, len, 0, lvl, status))) { + + return 1; + + } + + } } - // reverse encoding - if (do_reverse && *status != 1) { + if (SHAPE_BYTES(h->shape) >= 4 && *status != 1) { - if (unlikely(cmp_extend_encoding(afl, h, SWAP64(pattern), SWAP64(repl), - SWAP64(o_pattern), idx, orig_buf, buf, - len, 0, status))) { + // if (its_len >= 4 && (attr <= 1 || attr >= 8)) + // fprintf(stderr, + // "TestU32: %u>=4 (idx=%u attr=%u) %x==%x" + // " %x==%x <= %x<-%x\n", + // its_len, idx, attr, *buf_32, (u32)pattern, *o_buf_32, + // (u32)o_pattern, (u32)repl, (u32)changed_val); - return 1; + if (its_len >= 4 && + ((*buf_32 == (u32)pattern && *o_buf_32 == (u32)o_pattern) || + attr >= IS_FP_MOD)) { + + u32 tmp_32 = *buf_32; + *buf_32 = (u32)repl; + if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } +#ifdef CMPLOG_COMBINE + if (*status == 1) { memcpy(cbuf + idx, buf_32, 4); } +#endif + *buf_32 = tmp_32; + + // fprintf(stderr, "Status=%u\n", *status); + + } + + // reverse encoding + if (do_reverse && *status != 1) { + + if (unlikely(cmp_extend_encoding(afl, h, SWAP32(pattern), SWAP32(repl), + SWAP32(o_pattern), SWAP32(changed_val), + attr, idx, taint_len, orig_buf, buf, + cbuf, len, 0, lvl, status))) { + + return 1; + + } + + } + + } + + if (SHAPE_BYTES(h->shape) >= 2 && *status != 1) { + + if (its_len >= 2 && + ((*buf_16 == (u16)pattern && *o_buf_16 == (u16)o_pattern) || + attr >= IS_FP_MOD)) { + + u16 tmp_16 = *buf_16; + *buf_16 = (u16)repl; + if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } +#ifdef CMPLOG_COMBINE + if (*status == 1) { memcpy(cbuf + idx, buf_16, 2); } +#endif + *buf_16 = tmp_16; + + } + + // reverse encoding + if (do_reverse && *status != 1) { + + if (unlikely(cmp_extend_encoding(afl, h, SWAP16(pattern), SWAP16(repl), + SWAP16(o_pattern), SWAP16(changed_val), + attr, idx, taint_len, orig_buf, buf, + cbuf, len, 0, lvl, status))) { + + return 1; + + } + + } + + } + + if (*status != 1) { // u8 + + // if (its_len >= 1) + // fprintf(stderr, + // "TestU8: %u>=1 (idx=%u attr=%u) %x==%x %x==%x <= %x<-%x\n", + // its_len, idx, attr, *buf_8, (u8)pattern, *o_buf_8, + // (u8)o_pattern, (u8)repl, (u8)changed_val); + + if (its_len >= 1 && + ((*buf_8 == (u8)pattern && *o_buf_8 == (u8)o_pattern) || + attr >= IS_FP_MOD)) { + + u8 tmp_8 = *buf_8; + *buf_8 = (u8)repl; + if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } +#ifdef CMPLOG_COMBINE + if (*status == 1) { cbuf[idx] = *buf_8; } +#endif + *buf_8 = tmp_8; } @@ -404,23 +1208,109 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } - if (SHAPE_BYTES(h->shape) >= 4 && *status != 1) { + // here we add and subract 1 from the value, but only if it is not an + // == or != comparison + // Bits: 1 = Equal, 2 = Greater, 4 = Lesser, 8 = Float + // 16 = modified float, 32 = modified integer (modified = wont match + // in original buffer) - if (its_len >= 4 && *buf_32 == (u32)pattern && - *o_buf_32 == (u32)o_pattern) { + //#ifdef CMPLOG_SOLVE_ARITHMETIC + if (!afl->cmplog_enable_arith || lvl < LVL3 || attr == IS_TRANSFORM) { - *buf_32 = (u32)repl; - if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } - *buf_32 = pattern; + return 0; + + } + + if (!(attr & (IS_GREATER | IS_LESSER)) || SHAPE_BYTES(h->shape) < 4) { + + return 0; + + } + + // transform >= to < and <= to > + if ((attr & IS_EQUAL) && (attr & (IS_GREATER | IS_LESSER))) { + + if (attr & 2) { + + attr += 2; + + } else { + + attr -= 2; } - // reverse encoding - if (do_reverse && *status != 1) { + } + + // lesser/greater FP comparison + if (attr >= IS_FP && attr < IS_FP_MOD) { + + u64 repl_new; + + if (attr & IS_GREATER) { - if (unlikely(cmp_extend_encoding(afl, h, SWAP32(pattern), SWAP32(repl), - SWAP32(o_pattern), idx, orig_buf, buf, - len, 0, status))) { + if (SHAPE_BYTES(h->shape) == 4 && its_len >= 4) { + + float *f = (float *)&repl; + float g = *f; + g += 1.0; + u32 *r = (u32 *)&g; + repl_new = (u32)*r; + + } else if (SHAPE_BYTES(h->shape) == 8 && its_len >= 8) { + + double *f = (double *)&repl; + double g = *f; + g += 1.0; + + u64 *r = (u64 *)&g; + repl_new = *r; + + } else { + + return 0; + + } + + changed_val = repl_new; + + if (unlikely(cmp_extend_encoding( + afl, h, pattern, repl_new, o_pattern, changed_val, 16, idx, + taint_len, orig_buf, buf, cbuf, len, 1, lvl, status))) { + + return 1; + + } + + } else { + + if (SHAPE_BYTES(h->shape) == 4) { + + float *f = (float *)&repl; + float g = *f; + g -= 1.0; + u32 *r = (u32 *)&g; + repl_new = (u32)*r; + + } else if (SHAPE_BYTES(h->shape) == 8) { + + double *f = (double *)&repl; + double g = *f; + g -= 1.0; + u64 *r = (u64 *)&g; + repl_new = *r; + + } else { + + return 0; + + } + + changed_val = repl_new; + + if (unlikely(cmp_extend_encoding( + afl, h, pattern, repl_new, o_pattern, changed_val, 16, idx, + taint_len, orig_buf, buf, cbuf, len, 1, lvl, status))) { return 1; @@ -428,25 +1318,62 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } - } + // transform double to float, llvm likes to do that internally ... + if (SHAPE_BYTES(h->shape) == 8 && its_len >= 4) { - if (SHAPE_BYTES(h->shape) >= 2 && *status != 1) { + double *f = (double *)&repl; + float g = (float)*f; + repl_new = 0; +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + memcpy((char *)&repl_new, (char *)&g, 4); +#else + memcpy(((char *)&repl_new) + 4, (char *)&g, 4); +#endif + changed_val = repl_new; + h->shape = 3; // modify shape - if (its_len >= 2 && *buf_16 == (u16)pattern && - *o_buf_16 == (u16)o_pattern) { + // fprintf(stderr, "DOUBLE2FLOAT %llx\n", repl_new); - *buf_16 = (u16)repl; - if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } - *buf_16 = (u16)pattern; + if (unlikely(cmp_extend_encoding( + afl, h, pattern, repl_new, o_pattern, changed_val, 16, idx, + taint_len, orig_buf, buf, cbuf, len, 1, lvl, status))) { + + h->shape = 7; // recover shape + return 1; + + } + + h->shape = 7; // recover shape } - // reverse encoding - if (do_reverse && *status != 1) { + } + + else if (attr < IS_FP) { - if (unlikely(cmp_extend_encoding(afl, h, SWAP16(pattern), SWAP16(repl), - SWAP16(o_pattern), idx, orig_buf, buf, - len, 0, status))) { + // lesser/greater integer comparison + + u64 repl_new; + + if (attr & IS_GREATER) { + + repl_new = repl + 1; + changed_val = repl_new; + if (unlikely(cmp_extend_encoding( + afl, h, pattern, repl_new, o_pattern, changed_val, 32, idx, + taint_len, orig_buf, buf, cbuf, len, 1, lvl, status))) { + + return 1; + + } + + } else { + + repl_new = repl - 1; + changed_val = repl_new; + if (unlikely(cmp_extend_encoding( + afl, h, pattern, repl_new, o_pattern, changed_val, 32, idx, + taint_len, orig_buf, buf, cbuf, len, 1, lvl, status))) { return 1; @@ -456,13 +1383,92 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } - if (SHAPE_BYTES(h->shape) >= 1 && *status != 1) { + //#endif /* CMPLOG_SOLVE_ARITHMETIC + + return 0; + +} - if (its_len >= 1 && *buf_8 == (u8)pattern && *o_buf_8 == (u8)o_pattern) { +#ifdef WORD_SIZE_64 + +static u8 cmp_extend_encodingN(afl_state_t *afl, struct cmp_header *h, + u128 pattern, u128 repl, u128 o_pattern, + u128 changed_val, u8 attr, u32 idx, + u32 taint_len, u8 *orig_buf, u8 *buf, u8 *cbuf, + u32 len, u8 do_reverse, u8 lvl, u8 *status) { + + u8 *ptr = (u8 *)&buf[idx]; + u8 *o_ptr = (u8 *)&orig_buf[idx]; + u8 *p = (u8 *)&pattern; + u8 *o_p = (u8 *)&o_pattern; + u8 *r = (u8 *)&repl; + u8 backup[16]; + u32 its_len = MIN(len - idx, taint_len); + u32 shape = h->shape + 1; + #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + size_t off = 0; + #else + size_t off = 16 - shape; + #endif + + if (its_len >= shape) { + + #ifdef _DEBUG + fprintf(stderr, "TestUN: %u>=%u (len=%u idx=%u attr=%u off=%lu) (%u) ", + its_len, shape, len, idx, attr, off, do_reverse); + u32 i; + u8 *o_r = (u8 *)&changed_val; + for (i = 0; i < shape; i++) + fprintf(stderr, "%02x", ptr[i]); + fprintf(stderr, "=="); + for (i = 0; i < shape; i++) + fprintf(stderr, "%02x", p[off + i]); + fprintf(stderr, " "); + for (i = 0; i < shape; i++) + fprintf(stderr, "%02x", o_ptr[i]); + fprintf(stderr, "=="); + for (i = 0; i < shape; i++) + fprintf(stderr, "%02x", o_p[off + i]); + fprintf(stderr, " <= "); + for (i = 0; i < shape; i++) + fprintf(stderr, "%02x", r[off + i]); + fprintf(stderr, "<-"); + for (i = 0; i < shape; i++) + fprintf(stderr, "%02x", o_r[off + i]); + fprintf(stderr, "\n"); + #endif + + if (!memcmp(ptr, p + off, shape) && !memcmp(o_ptr, o_p + off, shape)) { + + memcpy(backup, ptr, shape); + memcpy(ptr, r + off, shape); - *buf_8 = (u8)repl; if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } - *buf_8 = (u8)pattern; + + #ifdef CMPLOG_COMBINE + if (*status == 1) { memcpy(cbuf + idx, r, shape); } + #endif + + memcpy(ptr, backup, shape); + + #ifdef _DEBUG + fprintf(stderr, "Status=%u\n", *status); + #endif + + } + + // reverse encoding + if (do_reverse && *status != 1) { + + if (unlikely(cmp_extend_encodingN( + afl, h, SWAPN(pattern, (shape << 3)), SWAPN(repl, (shape << 3)), + SWAPN(o_pattern, (shape << 3)), SWAPN(changed_val, (shape << 3)), + attr, idx, taint_len, orig_buf, buf, cbuf, len, 0, lvl, + status))) { + + return 1; + + } } @@ -472,6 +1478,8 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } +#endif + static void try_to_add_to_dict(afl_state_t *afl, u64 v, u8 shape) { u8 *b = (u8 *)&v; @@ -486,7 +1494,7 @@ static void try_to_add_to_dict(afl_state_t *afl, u64 v, u8 shape) { } else if (b[k] == 0xff) { - ++cons_0; + ++cons_ff; } else { @@ -498,7 +1506,7 @@ static void try_to_add_to_dict(afl_state_t *afl, u64 v, u8 shape) { } - maybe_add_auto((u8 *)afl, (u8 *)&v, shape); + maybe_add_auto(afl, (u8 *)&v, shape); u64 rev; switch (shape) { @@ -507,43 +1515,108 @@ static void try_to_add_to_dict(afl_state_t *afl, u64 v, u8 shape) { break; case 2: rev = SWAP16((u16)v); - maybe_add_auto((u8 *)afl, (u8 *)&rev, shape); + maybe_add_auto(afl, (u8 *)&rev, shape); break; case 4: rev = SWAP32((u32)v); - maybe_add_auto((u8 *)afl, (u8 *)&rev, shape); + maybe_add_auto(afl, (u8 *)&rev, shape); break; case 8: rev = SWAP64(v); - maybe_add_auto((u8 *)afl, (u8 *)&rev, shape); + maybe_add_auto(afl, (u8 *)&rev, shape); break; } } -static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u32 len) { +#ifdef WORD_SIZE_64 +static void try_to_add_to_dictN(afl_state_t *afl, u128 v, u8 size) { - struct cmp_header *h = &afl->shm.cmp_map->headers[key]; - u32 i, j, idx; + u8 *b = (u8 *)&v; + + u32 k; + u8 cons_ff = 0, cons_0 = 0; + #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + u32 off = 0; + for (k = 0; k < size; ++k) { + + #else + u32 off = 16 - size; + for (k = 16 - size; k < 16; ++k) { + + #endif + if (b[k] == 0) { + + ++cons_0; + + } else if (b[k] == 0xff) { + + ++cons_ff; + + } else { + + cons_0 = cons_ff = 0; + + } + + } + + maybe_add_auto(afl, (u8 *)&v + off, size); + u128 rev = SWAPN(v, size); + maybe_add_auto(afl, (u8 *)&rev + off, size); - u32 loggeds = h->hits; - if (h->hits > CMP_MAP_H) { loggeds = CMP_MAP_H; } +} + +#endif - u8 status = 0; - // opt not in the paper - u32 fails; - u8 found_one = 0; +#define SWAPA(_x) ((_x & 0xf8) + ((_x & 7) ^ 0x07)) + +static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, + u32 len, u32 lvl, struct tainted *taint) { + + struct cmp_header *h = &afl->shm.cmp_map->headers[key]; + struct tainted * t; + u32 i, j, idx, taint_len, loggeds; + u32 have_taint = 1; + u8 status = 0, found_one = 0; /* loop cmps are useless, detect and ignore them */ +#ifdef WORD_SIZE_64 + u32 is_n = 0; + u128 s128_v0 = 0, s128_v1 = 0, orig_s128_v0 = 0, orig_s128_v1 = 0; +#endif u64 s_v0, s_v1; u8 s_v0_fixed = 1, s_v1_fixed = 1; u8 s_v0_inc = 1, s_v1_inc = 1; u8 s_v0_dec = 1, s_v1_dec = 1; - for (i = 0; i < loggeds; ++i) { + if (h->hits > CMP_MAP_H) { - fails = 0; + loggeds = CMP_MAP_H; + + } else { + + loggeds = h->hits; + + } + +#ifdef WORD_SIZE_64 + switch (SHAPE_BYTES(h->shape)) { + + case 1: + case 2: + case 4: + case 8: + break; + default: + is_n = 1; + + } + +#endif + + for (i = 0; i < loggeds; ++i) { struct cmp_operands *o = &afl->shm.cmp_map->log[key][i]; @@ -580,55 +1653,176 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u32 len) { } - for (idx = 0; idx < len && fails < 8; ++idx) { +#ifdef _DEBUG + fprintf(stderr, "Handling: %llx->%llx vs %llx->%llx attr=%u shape=%u\n", + orig_o->v0, o->v0, orig_o->v1, o->v1, h->attribute, + SHAPE_BYTES(h->shape)); +#endif + + t = taint; + while (t->next) { + + t = t->next; + + } + +#ifdef WORD_SIZE_64 + if (unlikely(is_n)) { + + s128_v0 = ((u128)o->v0) + (((u128)o->v0_128) << 64); + s128_v1 = ((u128)o->v1) + (((u128)o->v1_128) << 64); + orig_s128_v0 = ((u128)orig_o->v0) + (((u128)orig_o->v0_128) << 64); + orig_s128_v1 = ((u128)orig_o->v1) + (((u128)orig_o->v1_128) << 64); + + } + +#endif + + for (idx = 0; idx < len; ++idx) { + + if (have_taint) { + + if (!t || idx < t->pos) { + + continue; + + } else { + + taint_len = t->pos + t->len - idx; + + if (idx == t->pos + t->len - 1) { t = t->prev; } + + } + + } else { + + taint_len = len - idx; + + } status = 0; - if (unlikely(cmp_extend_encoding(afl, h, o->v0, o->v1, orig_o->v0, idx, - orig_buf, buf, len, 1, &status))) { - return 1; +#ifdef WORD_SIZE_64 + if (is_n) { // _ExtInt special case including u128 + + if (s128_v0 != orig_s128_v0 && orig_s128_v0 != orig_s128_v1) { + + if (unlikely(cmp_extend_encodingN( + afl, h, s128_v0, s128_v1, orig_s128_v0, orig_s128_v1, + h->attribute, idx, taint_len, orig_buf, buf, cbuf, len, 1, + lvl, &status))) { + + return 1; + + } + + } + + if (status == 1) { + + found_one = 1; + break; + + } + + if (s128_v1 != orig_s128_v1 && orig_s128_v1 != orig_s128_v0) { + + if (unlikely(cmp_extend_encodingN( + afl, h, s128_v1, s128_v0, orig_s128_v1, orig_s128_v0, + SWAPA(h->attribute), idx, taint_len, orig_buf, buf, cbuf, len, + 1, lvl, &status))) { + + return 1; + + } + + } + + if (status == 1) { + + found_one = 1; + break; + + } } - if (status == 2) { +#endif + + // even for u128 and _ExtInt we do cmp_extend_encoding() because + // if we got here their own special trials failed and it might just be + // a cast from e.g. u64 to u128 from the input data. + + if ((o->v0 != orig_o->v0 || lvl >= LVL3) && orig_o->v0 != orig_o->v1) { - ++fails; + if (unlikely(cmp_extend_encoding( + afl, h, o->v0, o->v1, orig_o->v0, orig_o->v1, h->attribute, idx, + taint_len, orig_buf, buf, cbuf, len, 1, lvl, &status))) { - } else if (status == 1) { + return 1; + } + + } + + if (status == 1) { + + found_one = 1; break; } status = 0; - if (unlikely(cmp_extend_encoding(afl, h, o->v1, o->v0, orig_o->v1, idx, - orig_buf, buf, len, 1, &status))) { + if ((o->v1 != orig_o->v1 || lvl >= LVL3) && orig_o->v0 != orig_o->v1) { - return 1; + if (unlikely(cmp_extend_encoding(afl, h, o->v1, o->v0, orig_o->v1, + orig_o->v0, SWAPA(h->attribute), idx, + taint_len, orig_buf, buf, cbuf, len, 1, + lvl, &status))) { - } + return 1; - if (status == 2) { + } - ++fails; + } - } else if (status == 1) { + if (status == 1) { + found_one = 1; break; } } - if (status == 1) { found_one = 1; } +#ifdef _DEBUG + fprintf(stderr, + "END: %llx->%llx vs %llx->%llx attr=%u i=%u found=%u " + "isN=%u size=%u\n", + orig_o->v0, o->v0, orig_o->v1, o->v1, h->attribute, i, found_one, + is_n, SHAPE_BYTES(h->shape)); +#endif // If failed, add to dictionary - if (fails == 8) { + if (!found_one) { if (afl->pass_stats[key].total == 0) { - try_to_add_to_dict(afl, o->v0, SHAPE_BYTES(h->shape)); - try_to_add_to_dict(afl, o->v1, SHAPE_BYTES(h->shape)); +#ifdef WORD_SIZE_64 + if (unlikely(is_n)) { + + try_to_add_to_dictN(afl, s128_v0, SHAPE_BYTES(h->shape)); + try_to_add_to_dictN(afl, s128_v1, SHAPE_BYTES(h->shape)); + + } else + +#endif + { + + try_to_add_to_dict(afl, o->v0, SHAPE_BYTES(h->shape)); + try_to_add_to_dict(afl, o->v1, SHAPE_BYTES(h->shape)); + + } } @@ -658,53 +1852,455 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u32 len) { } -static u8 rtn_extend_encoding(afl_state_t *afl, struct cmp_header *h, - u8 *pattern, u8 *repl, u8 *o_pattern, u32 idx, - u8 *orig_buf, u8 *buf, u32 len, u8 *status) { +static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, + u8 *o_pattern, u8 *changed_val, u8 plen, u32 idx, + u32 taint_len, u8 *orig_buf, u8 *buf, u8 *cbuf, + u32 len, u8 lvl, u8 *status) { - u32 i; - u32 its_len = MIN(32, len - idx); +#ifndef CMPLOG_COMBINE + (void)(cbuf); +#endif + //#ifndef CMPLOG_SOLVE_TRANSFORM + // (void)(changed_val); + //#endif - u8 save[32]; - memcpy(save, &buf[idx], its_len); + u8 save[40]; + u32 saved_idx = idx, pre, from = 0, to = 0, i, j; + u32 its_len = MIN((u32)plen, len - idx); + its_len = MIN(its_len, taint_len); + u32 saved_its_len = its_len; - *status = 0; + if (lvl & LVL3) { - for (i = 0; i < its_len; ++i) { + u32 max_to = MIN(4U, idx); + if (!(lvl & LVL1) && max_to) { from = 1; } + to = max_to; - if (pattern[idx + i] != buf[idx + i] || - o_pattern[idx + i] != orig_buf[idx + i] || *status == 1) { + } - break; + memcpy(save, &buf[saved_idx - to], its_len + to); + (void)(j); + +#ifdef _DEBUG + fprintf(stderr, "RTN T idx=%u lvl=%02x ", idx, lvl); + for (j = 0; j < 8; j++) + fprintf(stderr, "%02x", orig_buf[idx + j]); + fprintf(stderr, " -> "); + for (j = 0; j < 8; j++) + fprintf(stderr, "%02x", o_pattern[j]); + fprintf(stderr, " <= "); + for (j = 0; j < 8; j++) + fprintf(stderr, "%02x", repl[j]); + fprintf(stderr, "\n"); + fprintf(stderr, " "); + for (j = 0; j < 8; j++) + fprintf(stderr, "%02x", buf[idx + j]); + fprintf(stderr, " -> "); + for (j = 0; j < 8; j++) + fprintf(stderr, "%02x", pattern[j]); + fprintf(stderr, " <= "); + for (j = 0; j < 8; j++) + fprintf(stderr, "%02x", changed_val[j]); + fprintf(stderr, "\n"); +#endif + + // Try to match the replace value up to 4 bytes before the current idx. + // This allows matching of eg.: + // if (memcmp(user_val, "TEST") == 0) + // if (memcmp(user_val, "TEST-VALUE") == 0) ... + // We only do this in lvl 3, otherwise we only do direct matching + + for (pre = from; pre <= to; pre++) { + + if (*status != 1 && (!pre || !memcmp(buf + saved_idx - pre, repl, pre))) { + + idx = saved_idx - pre; + its_len = saved_its_len + pre; + + for (i = 0; i < its_len; ++i) { + + if ((pattern[i] != buf[idx + i] && o_pattern[i] != orig_buf[idx + i]) || + *status == 1) { + + break; + + } + + buf[idx + i] = repl[i]; + + if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } + +#ifdef CMPLOG_COMBINE + if (*status == 1) { memcpy(cbuf + idx, &buf[idx], i); } +#endif + + } + + memcpy(&buf[idx], save + to - pre, i); + + } + + } + + //#ifdef CMPLOG_SOLVE_TRANSFORM + + if (*status == 1) return 0; + + if (afl->cmplog_enable_transform && (lvl & LVL3)) { + + u32 toupper = 0, tolower = 0, xor = 0, arith = 0, tohex = 0, fromhex = 0; +#ifdef CMPLOG_SOLVE_TRANSFORM_BASE64 + u32 tob64 = 0, fromb64 = 0; +#endif + u32 from_0 = 0, from_x = 0, from_X = 0, from_slash = 0, from_up = 0; + u32 to_0 = 0, to_x = 0, to_slash = 0, to_up = 0; + u8 xor_val[32], arith_val[32], tmp[48]; + + idx = saved_idx; + its_len = saved_its_len; + + memcpy(save, &buf[idx], its_len); + + for (i = 0; i < its_len; ++i) { + + xor_val[i] = pattern[i] ^ buf[idx + i]; + arith_val[i] = pattern[i] - buf[idx + i]; + + if (i == 0) { + + if (orig_buf[idx] == '0') { + + from_0 = 1; + + } else if (orig_buf[idx] == '\\') { + + from_slash = 1; + + } + + if (repl[0] == '0') { + + to_0 = 1; + + } else if (repl[0] == '\\') { + + to_slash = 1; + + } + + } else if (i == 1) { + + if (orig_buf[idx + 1] == 'x') { + + from_x = 1; + + } else if (orig_buf[idx + 1] == 'X') { + + from_X = from_x = 1; + + } + + if (repl[1] == 'x' || repl[1] == 'X') { to_x = 1; } + + } + + if (i < 16 && is_hex(repl + (i << 1))) { + + ++tohex; + + if (!to_up) { + + if (repl[i << 1] >= 'A' && repl[i << 1] <= 'F') + to_up = 1; + else if (repl[i << 1] >= 'a' && repl[i << 1] <= 'f') + to_up = 2; + if (repl[(i << 1) + 1] >= 'A' && repl[(i << 1) + 1] <= 'F') + to_up = 1; + else if (repl[(i << 1) + 1] >= 'a' && repl[(i << 1) + 1] <= 'f') + to_up = 2; + + } + + } + + if ((i % 2)) { + + if (len > idx + i && is_hex(orig_buf + idx + i)) { + + fromhex += 2; + + if (!from_up) { + + if (orig_buf[idx + i] >= 'A' && orig_buf[idx + i] <= 'F') + from_up = 1; + else if (orig_buf[idx + i] >= 'a' && orig_buf[idx + i] <= 'f') + from_up = 2; + if (orig_buf[idx + i - 1] >= 'A' && orig_buf[idx + i - 1] <= 'F') + from_up = 1; + else if (orig_buf[idx + i - 1] >= 'a' && + orig_buf[idx + i - 1] <= 'f') + from_up = 2; + + } + + } + + } + +#ifdef CMPLOG_SOLVE_TRANSFORM_BASE64 + if (i % 3 == 2 && i < 24) { + + if (is_base64(repl + ((i / 3) << 2))) tob64 += 3; + + } + + if (i % 4 == 3 && i < 24) { + + if (is_base64(orig_buf + idx + i - 3)) fromb64 += 4; + + } + +#endif + + if ((o_pattern[i] ^ orig_buf[idx + i]) == xor_val[i] && xor_val[i]) { + + ++xor; + + } + + if ((o_pattern[i] - orig_buf[idx + i]) == arith_val[i] && arith_val[i]) { + + ++arith; + + } + + if ((buf[idx + i] | 0x20) == pattern[i] && + (orig_buf[idx + i] | 0x20) == o_pattern[i]) { + + ++tolower; + + } + + if ((buf[idx + i] & 0x5a) == pattern[i] && + (orig_buf[idx + i] & 0x5a) == o_pattern[i]) { + + ++toupper; + + } + +#ifdef _DEBUG + fprintf(stderr, + "RTN idx=%u loop=%u xor=%u arith=%u tolower=%u toupper=%u " + "tohex=%u fromhex=%u to_0=%u to_slash=%u to_x=%u " + "from_0=%u from_slash=%u from_x=%u\n", + idx, i, xor, arith, tolower, toupper, tohex, fromhex, to_0, + to_slash, to_x, from_0, from_slash, from_x); + #ifdef CMPLOG_SOLVE_TRANSFORM_BASE64 + fprintf(stderr, "RTN idx=%u loop=%u tob64=%u from64=%u\n", tob64, + fromb64); + #endif +#endif + +#ifdef CMPLOG_SOLVE_TRANSFORM_BASE64 + // input is base64 and converted to binary? convert repl to base64! + if ((i % 4) == 3 && i < 24 && fromb64 > i) { + + to_base64(repl, tmp, i + 1); + memcpy(buf + idx, tmp, i + 1); + if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } + // fprintf(stderr, "RTN ATTEMPT fromb64 %u result %u\n", fromb64, + // *status); + + } + + // input is converted to base64? decode repl with base64! + if ((i % 3) == 2 && i < 24 && tob64 > i) { + + u32 olen = from_base64(repl, tmp, i + 1); + memcpy(buf + idx, tmp, olen); + if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } + // fprintf(stderr, "RTN ATTEMPT tob64 %u idx=%u result %u\n", tob64, + // idx, *status); + + } + +#endif + + // input is converted to hex? convert repl to binary! + if (i < 16 && tohex > i) { + + u32 off; + if (to_slash + to_x + to_0 == 2) { + + off = 2; + + } else { + + off = 0; + + } + + for (j = 0; j <= i; j++) + tmp[j] = (hex_table[repl[off + (j << 1)] - '0'] << 4) + + hex_table[repl[off + (j << 1) + 1] - '0']; + + memcpy(buf + idx, tmp, i + 1); + if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } + // fprintf(stderr, "RTN ATTEMPT tohex %u result %u\n", tohex, *status); + + } + + // input is hex and converted to binary? convert repl to hex! + if (i && (i % 2) && i < 16 && fromhex && + fromhex + from_slash + from_x + from_0 > i) { + + u8 off = 0; + if (from_slash && from_x) { + + tmp[0] = '\\'; + if (from_X) { + + tmp[1] = 'X'; + + } else { + + tmp[1] = 'x'; + + } + + off = 2; + + } else if (from_0 && from_x) { + + tmp[0] = '0'; + if (from_X) { + + tmp[1] = 'X'; + + } else { + + tmp[1] = 'x'; + + } + + off = 2; + + } + + if (to_up == 1) { + + for (j = 0; j <= (i >> 1); j++) { + + tmp[off + (j << 1)] = hex_table_up[repl[j] >> 4]; + tmp[off + (j << 1) + 1] = hex_table_up[repl[j] % 16]; + + } + + } else { + + for (j = 0; j <= (i >> 1); j++) { + + tmp[off + (j << 1)] = hex_table_low[repl[j] >> 4]; + tmp[off + (j << 1) + 1] = hex_table_low[repl[j] % 16]; + + } + + } + + memcpy(buf + idx, tmp, i + 1 + off); + if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } + // fprintf(stderr, "RTN ATTEMPT fromhex %u result %u\n", fromhex, + // *status); + memcpy(buf + idx + i, save + i, i + 1 + off); + + } + + if (xor > i) { + + for (j = 0; j <= i; j++) + buf[idx + j] = repl[j] ^ xor_val[j]; + if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } + // fprintf(stderr, "RTN ATTEMPT xor %u result %u\n", xor, *status); + + } + + if (arith > i && *status != 1) { + + for (j = 0; j <= i; j++) + buf[idx + j] = repl[j] - arith_val[j]; + if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } + // fprintf(stderr, "RTN ATTEMPT arith %u result %u\n", arith, *status); + + } + + if (toupper > i && *status != 1) { + + for (j = 0; j <= i; j++) + buf[idx + j] = repl[j] | 0x20; + if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } + // fprintf(stderr, "RTN ATTEMPT toupper %u result %u\n", toupper, + // *status); + + } + + if (tolower > i && *status != 1) { + + for (j = 0; j <= i; j++) + buf[idx + j] = repl[j] & 0x5f; + if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } + // fprintf(stderr, "RTN ATTEMPT tolower %u result %u\n", tolower, + // *status); + + } + +#ifdef CMPLOG_COMBINE + if (*status == 1) { memcpy(cbuf + idx, &buf[idx], i + 1); } +#endif + + if ((i >= 7 && + (i >= xor&&i >= arith &&i >= tolower &&i >= toupper &&i > tohex &&i > + (fromhex + from_0 + from_x + from_slash + 1) +#ifdef CMPLOG_SOLVE_TRANSFORM_BASE64 + && i > tob64 + 3 && i > fromb64 + 4 +#endif + )) || + repl[i] != changed_val[i] || *status == 1) { + + break; + + } } - buf[idx + i] = repl[idx + i]; - if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } + memcpy(&buf[idx], save, i); } - memcpy(&buf[idx], save, i); + //#endif + return 0; } -static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u32 len) { +static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, + u32 len, u8 lvl, struct tainted *taint) { + struct tainted * t; struct cmp_header *h = &afl->shm.cmp_map->headers[key]; - u32 i, j, idx; + u32 i, j, idx, have_taint = 1, taint_len, loggeds; + u8 status = 0, found_one = 0; - u32 loggeds = h->hits; - if (h->hits > CMP_MAP_RTN_H) { loggeds = CMP_MAP_RTN_H; } + if (h->hits > CMP_MAP_RTN_H) { - u8 status = 0; - // opt not in the paper - u32 fails = 0; - u8 found_one = 0; + loggeds = CMP_MAP_RTN_H; - for (i = 0; i < loggeds; ++i) { + } else { - fails = 0; + loggeds = h->hits; + + } + + for (i = 0; i < loggeds; ++i) { struct cmpfn_operands *o = &((struct cmpfn_operands *)afl->shm.cmp_map->log[key])[i]; @@ -724,53 +2320,92 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u32 len) { } - for (idx = 0; idx < len && fails < 8; ++idx) { + /* + struct cmp_header *hh = &afl->orig_cmp_map->headers[key]; + fprintf(stderr, "RTN N hits=%u id=%u shape=%u attr=%u v0=", h->hits, h->id, + h->shape, h->attribute); for (j = 0; j < 8; j++) fprintf(stderr, "%02x", + o->v0[j]); fprintf(stderr, " v1="); for (j = 0; j < 8; j++) fprintf(stderr, + "%02x", o->v1[j]); fprintf(stderr, "\nRTN O hits=%u id=%u shape=%u attr=%u + o0=", hh->hits, hh->id, hh->shape, hh->attribute); for (j = 0; j < 8; j++) + fprintf(stderr, "%02x", orig_o->v0[j]); + fprintf(stderr, " o1="); + for (j = 0; j < 8; j++) + fprintf(stderr, "%02x", orig_o->v1[j]); + fprintf(stderr, "\n"); + */ + + t = taint; + while (t->next) { + + t = t->next; - if (unlikely(rtn_extend_encoding(afl, h, o->v0, o->v1, orig_o->v0, idx, - orig_buf, buf, len, &status))) { + } - return 1; + for (idx = 0; idx < len; ++idx) { - } + if (have_taint) { - if (status == 2) { + if (!t || idx < t->pos) { - ++fails; + continue; - } else if (status == 1) { + } else { - break; + taint_len = t->pos + t->len - idx; + + if (idx == t->pos + t->len - 1) { t = t->prev; } + + } + + } else { + + taint_len = len - idx; } - if (unlikely(rtn_extend_encoding(afl, h, o->v1, o->v0, orig_o->v1, idx, - orig_buf, buf, len, &status))) { + status = 0; + + if (unlikely(rtn_extend_encoding( + afl, o->v0, o->v1, orig_o->v0, orig_o->v1, SHAPE_BYTES(h->shape), + idx, taint_len, orig_buf, buf, cbuf, len, lvl, &status))) { return 1; } - if (status == 2) { + if (status == 1) { + + found_one = 1; + break; + + } - ++fails; + status = 0; - } else if (status == 1) { + if (unlikely(rtn_extend_encoding( + afl, o->v1, o->v0, orig_o->v1, orig_o->v0, SHAPE_BYTES(h->shape), + idx, taint_len, orig_buf, buf, cbuf, len, lvl, &status))) { + return 1; + + } + + if (status == 1) { + + found_one = 1; break; } } - if (status == 1) { found_one = 1; } - // If failed, add to dictionary - if (fails == 8) { + if (!found_one && (lvl & LVL1)) { - if (afl->pass_stats[key].total == 0) { + if (unlikely(!afl->pass_stats[key].total)) { - maybe_add_auto((u8 *)afl, o->v0, SHAPE_BYTES(h->shape)); - maybe_add_auto((u8 *)afl, o->v1, SHAPE_BYTES(h->shape)); + maybe_add_auto(afl, o->v0, SHAPE_BYTES(h->shape)); + maybe_add_auto(afl, o->v1, SHAPE_BYTES(h->shape)); } @@ -796,54 +2431,147 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u32 len) { ///// Input to State stage // afl->queue_cur->exec_cksum -u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len, - u64 exec_cksum) { +u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { u8 r = 1; - if (afl->orig_cmp_map == NULL) { + if (unlikely(!afl->pass_stats)) { - afl->orig_cmp_map = ck_alloc_nozero(sizeof(struct cmp_map)); + afl->pass_stats = ck_alloc(sizeof(struct afl_pass_stat) * CMP_MAP_W); } - if (afl->pass_stats == NULL) { + struct tainted *taint = NULL; - afl->pass_stats = ck_alloc(sizeof(struct afl_pass_stat) * CMP_MAP_W); + if (!afl->queue_cur->taint || !afl->queue_cur->cmplog_colorinput) { + + if (unlikely(colorization(afl, buf, len, &taint))) { return 1; } + + // no taint? still try, create a dummy to prevent again colorization + if (!taint) { + +#ifdef _DEBUG + fprintf(stderr, "TAINT FAILED\n"); +#endif + afl->queue_cur->colorized = CMPLOG_LVL_MAX; + return 0; + + } + +#ifdef _DEBUG + else if (taint->pos == 0 && taint->len == len) { + + fprintf(stderr, "TAINT FULL\n"); + + } + +#endif + + } else { + + buf = afl->queue_cur->cmplog_colorinput; + taint = afl->queue_cur->taint; + + } + + struct tainted *t = taint; + + while (t) { + +#ifdef _DEBUG + fprintf(stderr, "T: idx=%u len=%u\n", t->pos, t->len); +#endif + t = t->next; + + } + +#if defined(_DEBUG) || defined(CMPLOG_INTROSPECTION) + u64 start_time = get_cur_time(); + u32 cmp_locations = 0; +#endif + + // Generate the cmplog data + + // manually clear the full cmp_map + memset(afl->shm.cmp_map, 0, sizeof(struct cmp_map)); + if (unlikely(common_fuzz_cmplog_stuff(afl, orig_buf, len))) { + + afl->queue_cur->colorized = CMPLOG_LVL_MAX; + while (taint) { + + t = taint->next; + ck_free(taint); + taint = t; + + } + + return 1; } - // do it manually, forkserver clear only afl->fsrv.trace_bits - memset(afl->shm.cmp_map->headers, 0, sizeof(afl->shm.cmp_map->headers)); + if (unlikely(!afl->orig_cmp_map)) { - if (unlikely(common_fuzz_cmplog_stuff(afl, buf, len))) { return 1; } + afl->orig_cmp_map = ck_alloc_nozero(sizeof(struct cmp_map)); + + } memcpy(afl->orig_cmp_map, afl->shm.cmp_map, sizeof(struct cmp_map)); + memset(afl->shm.cmp_map->headers, 0, sizeof(struct cmp_header) * CMP_MAP_W); + if (unlikely(common_fuzz_cmplog_stuff(afl, buf, len))) { - if (unlikely(colorization(afl, buf, len, exec_cksum))) { return 1; } + afl->queue_cur->colorized = CMPLOG_LVL_MAX; + while (taint) { - // do it manually, forkserver clear only afl->fsrv.trace_bits - memset(afl->shm.cmp_map->headers, 0, sizeof(afl->shm.cmp_map->headers)); + t = taint->next; + ck_free(taint); + taint = t; - if (unlikely(common_fuzz_cmplog_stuff(afl, buf, len))) { return 1; } + } + + return 1; + + } + +#ifdef _DEBUG + dump("ORIG", orig_buf, len); + dump("NEW ", buf, len); +#endif + + // Start insertion loop u64 orig_hit_cnt, new_hit_cnt; u64 orig_execs = afl->fsrv.total_execs; orig_hit_cnt = afl->queued_paths + afl->unique_crashes; + u64 screen_update = 100000 / afl->queue_cur->exec_us, + execs = afl->fsrv.total_execs; afl->stage_name = "input-to-state"; afl->stage_short = "its"; afl->stage_max = 0; afl->stage_cur = 0; + u32 lvl = (afl->queue_cur->colorized ? 0 : LVL1) + + (afl->cmplog_lvl == CMPLOG_LVL_MAX ? LVL3 : 0); + +#ifdef CMPLOG_COMBINE + u8 *cbuf = afl_realloc((void **)&afl->in_scratch_buf, len + 128); + memcpy(cbuf, orig_buf, len); + u8 *virgin_backup = afl_realloc((void **)&afl->ex_buf, afl->shm.map_size); + memcpy(virgin_backup, afl->virgin_bits, afl->shm.map_size); +#else + u8 *cbuf = NULL; +#endif + u32 k; for (k = 0; k < CMP_MAP_W; ++k) { if (!afl->shm.cmp_map->headers[k].hits) { continue; } - if (afl->pass_stats[k].total && - (rand_below(afl, afl->pass_stats[k].total) >= - afl->pass_stats[k].faileds || - afl->pass_stats[k].total == 0xff)) { + if (afl->pass_stats[k].faileds >= CMPLOG_FAIL_MAX || + afl->pass_stats[k].total >= CMPLOG_FAIL_MAX) { + +#ifdef _DEBUG + fprintf(stderr, "DISABLED %u\n", k); +#endif afl->shm.cmp_map->headers[k].hits = 0; // ignore this cmp @@ -851,12 +2579,13 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len, if (afl->shm.cmp_map->headers[k].type == CMP_TYPE_INS) { - afl->stage_max += MIN((u32)afl->shm.cmp_map->headers[k].hits, CMP_MAP_H); + afl->stage_max += + MIN((u32)(afl->shm.cmp_map->headers[k].hits), (u32)CMP_MAP_H); } else { afl->stage_max += - MIN((u32)afl->shm.cmp_map->headers[k].hits, CMP_MAP_RTN_H); + MIN((u32)(afl->shm.cmp_map->headers[k].hits), (u32)CMP_MAP_RTN_H); } @@ -866,13 +2595,37 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len, if (!afl->shm.cmp_map->headers[k].hits) { continue; } +#if defined(_DEBUG) || defined(CMPLOG_INTROSPECTION) + ++cmp_locations; +#endif + if (afl->shm.cmp_map->headers[k].type == CMP_TYPE_INS) { - if (unlikely(cmp_fuzz(afl, k, orig_buf, buf, len))) { goto exit_its; } + if (unlikely(cmp_fuzz(afl, k, orig_buf, buf, cbuf, len, lvl, taint))) { - } else { + goto exit_its; + + } + + } else if ((lvl & LVL1) + + //#ifdef CMPLOG_SOLVE_TRANSFORM + || ((lvl & LVL3) && afl->cmplog_enable_transform) + //#endif + ) { + + if (unlikely(rtn_fuzz(afl, k, orig_buf, buf, cbuf, len, lvl, taint))) { + + goto exit_its; + + } + + } + + if (afl->fsrv.total_execs - execs > screen_update) { - if (unlikely(rtn_fuzz(afl, k, orig_buf, buf, len))) { goto exit_its; } + execs = afl->fsrv.total_execs; + show_stats(afl); } @@ -881,11 +2634,122 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len, r = 0; exit_its: + + if (afl->cmplog_lvl == CMPLOG_LVL_MAX) { + + afl->queue_cur->colorized = CMPLOG_LVL_MAX; + + ck_free(afl->queue_cur->cmplog_colorinput); + while (taint) { + + t = taint->next; + ck_free(taint); + taint = t; + + } + + afl->queue_cur->taint = NULL; + + } else { + + afl->queue_cur->colorized = LVL2; + + if (!afl->queue_cur->taint) { afl->queue_cur->taint = taint; } + + if (!afl->queue_cur->cmplog_colorinput) { + + afl->queue_cur->cmplog_colorinput = ck_alloc_nozero(len); + memcpy(afl->queue_cur->cmplog_colorinput, buf, len); + memcpy(buf, orig_buf, len); + + } + + } + +#ifdef CMPLOG_COMBINE + if (afl->queued_paths + afl->unique_crashes > orig_hit_cnt + 1) { + + // copy the current virgin bits so we can recover the information + u8 *virgin_save = afl_realloc((void **)&afl->eff_buf, afl->shm.map_size); + memcpy(virgin_save, afl->virgin_bits, afl->shm.map_size); + // reset virgin bits to the backup previous to redqueen + memcpy(afl->virgin_bits, virgin_backup, afl->shm.map_size); + + u8 status = 0; + its_fuzz(afl, cbuf, len, &status); + + // now combine with the saved virgin bits + #ifdef WORD_SIZE_64 + u64 *v = (u64 *)afl->virgin_bits; + u64 *s = (u64 *)virgin_save; + u32 i; + for (i = 0; i < (afl->shm.map_size >> 3); i++) { + + v[i] &= s[i]; + + } + + #else + u32 *v = (u32 *)afl->virgin_bits; + u32 *s = (u32 *)virgin_save; + u32 i; + for (i = 0; i < (afl->shm.map_size >> 2); i++) { + + v[i] &= s[i]; + + } + + #endif + + #ifdef _DEBUG + dump("COMB", cbuf, len); + if (status == 1) { + + fprintf(stderr, "NEW CMPLOG_COMBINED\n"); + + } else { + + fprintf(stderr, "NO new combined\n"); + + } + + #endif + + } + +#endif + new_hit_cnt = afl->queued_paths + afl->unique_crashes; afl->stage_finds[STAGE_ITS] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_ITS] += afl->fsrv.total_execs - orig_execs; - memcpy(orig_buf, buf, len); +#if defined(_DEBUG) || defined(CMPLOG_INTROSPECTION) + FILE *f = stderr; + #ifndef _DEBUG + if (afl->not_on_tty) { + + char fn[4096]; + snprintf(fn, sizeof(fn), "%s/introspection_cmplog.txt", afl->out_dir); + f = fopen(fn, "a"); + + } + + #endif + + if (f) { + + fprintf(f, + "Cmplog: fname=%s len=%u ms=%llu result=%u finds=%llu entries=%u\n", + afl->queue_cur->fname, len, get_cur_time() - start_time, r, + new_hit_cnt - orig_hit_cnt, cmp_locations); + + #ifndef _DEBUG + if (afl->not_on_tty) { fclose(f); } + #endif + + } + +#endif return r; diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 6e3be72b..0b84a542 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -28,6 +28,9 @@ #include <sys/time.h> #include <signal.h> #include <limits.h> +#if !defined NAME_MAX + #define NAME_MAX _XOPEN_NAME_MAX +#endif #include "cmplog.h" @@ -38,8 +41,8 @@ u64 time_spent_working = 0; /* Execute target application, monitoring for timeouts. Return status information. The called program will update afl->fsrv->trace_bits. */ -fsrv_run_result_t fuzz_run_target(afl_state_t *afl, afl_forkserver_t *fsrv, - u32 timeout) { +fsrv_run_result_t __attribute__((hot)) +fuzz_run_target(afl_state_t *afl, afl_forkserver_t *fsrv, u32 timeout) { #ifdef PROFILING static u64 time_spent_start = 0; @@ -62,8 +65,6 @@ fsrv_run_result_t fuzz_run_target(afl_state_t *afl, afl_forkserver_t *fsrv, time_spent_start = (spec.tv_sec * 1000000000) + spec.tv_nsec; #endif - // TODO: Don't classify for faults? - classify_counts(fsrv); return res; } @@ -72,13 +73,15 @@ fsrv_run_result_t fuzz_run_target(afl_state_t *afl, afl_forkserver_t *fsrv, old file is unlinked and a new one is created. Otherwise, afl->fsrv.out_fd is rewound and truncated. */ -void write_to_testcase(afl_state_t *afl, void *mem, u32 len) { +void __attribute__((hot)) +write_to_testcase(afl_state_t *afl, void *mem, u32 len) { #ifdef _AFL_DOCUMENT_MUTATIONS s32 doc_fd; char fn[PATH_MAX]; snprintf(fn, PATH_MAX, "%s/mutations/%09u:%s", afl->out_dir, - afl->document_counter++, describe_op(afl, 0)); + afl->document_counter++, + describe_op(afl, 0, NAME_MAX - strlen("000000000:"))); if ((doc_fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600)) >= 0) { @@ -92,9 +95,9 @@ void write_to_testcase(afl_state_t *afl, void *mem, u32 len) { if (unlikely(afl->custom_mutators_count)) { - u8 * new_buf = NULL; ssize_t new_size = len; - void * new_mem = mem; + u8 * new_mem = mem; + u8 * new_buf = NULL; LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { @@ -136,24 +139,88 @@ void write_to_testcase(afl_state_t *afl, void *mem, u32 len) { /* The same, but with an adjustable gap. Used for trimming. */ -static void write_with_gap(afl_state_t *afl, void *mem, u32 len, u32 skip_at, +static void write_with_gap(afl_state_t *afl, u8 *mem, u32 len, u32 skip_at, u32 skip_len) { s32 fd = afl->fsrv.out_fd; u32 tail_len = len - skip_at - skip_len; + /* + This memory is used to carry out the post_processing(if present) after copying + the testcase by removing the gaps. This can break though + */ + u8 *mem_trimmed = afl_realloc(AFL_BUF_PARAM(out_scratch), len - skip_len + 1); + if (unlikely(!mem_trimmed)) { PFATAL("alloc"); } + + ssize_t new_size = len - skip_len; + u8 * new_mem = mem; + + bool post_process_skipped = true; + + if (unlikely(afl->custom_mutators_count)) { + + u8 *new_buf = NULL; + new_mem = mem_trimmed; + + LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { + + if (el->afl_custom_post_process) { + + // We copy into the mem_trimmed only if we actually have custom mutators + // *with* post_processing installed + + if (post_process_skipped) { + + if (skip_at) { memcpy(mem_trimmed, (u8 *)mem, skip_at); } + + if (tail_len) { + + memcpy(mem_trimmed + skip_at, (u8 *)mem + skip_at + skip_len, + tail_len); + + } + + post_process_skipped = false; + + } + + new_size = + el->afl_custom_post_process(el->data, new_mem, new_size, &new_buf); + + if (unlikely(!new_buf || (new_size <= 0))) { + + FATAL("Custom_post_process failed (ret: %lu)", + (long unsigned)new_size); + + } + + } + + new_mem = new_buf; + + }); + + } + if (afl->fsrv.shmem_fuzz) { - if (skip_at) { memcpy(afl->fsrv.shmem_fuzz, mem, skip_at); } + if (!post_process_skipped) { - if (tail_len) { + // If we did post_processing, copy directly from the new_mem buffer - memcpy(afl->fsrv.shmem_fuzz + skip_at, (u8 *)mem + skip_at + skip_len, - tail_len); + memcpy(afl->fsrv.shmem_fuzz, new_mem, new_size); } - *afl->fsrv.shmem_fuzz_len = len - skip_len; + else { + + memcpy(afl->fsrv.shmem_fuzz, mem, skip_at); + + memcpy(afl->fsrv.shmem_fuzz, mem + skip_at + skip_len, tail_len); + + } + + *afl->fsrv.shmem_fuzz_len = new_size; #ifdef _DEBUG if (afl->debug) { @@ -163,10 +230,10 @@ static void write_with_gap(afl_state_t *afl, void *mem, u32 len, u32 skip_at, hash64(afl->fsrv.shmem_fuzz, *afl->fsrv.shmem_fuzz_len, 0xa5b35705), *afl->fsrv.shmem_fuzz_len); fprintf(stderr, "SHM :"); - for (int i = 0; i < *afl->fsrv.shmem_fuzz_len; i++) + for (u32 i = 0; i < *afl->fsrv.shmem_fuzz_len; i++) fprintf(stderr, "%02x", afl->fsrv.shmem_fuzz[i]); fprintf(stderr, "\nORIG:"); - for (int i = 0; i < *afl->fsrv.shmem_fuzz_len; i++) + for (u32 i = 0; i < *afl->fsrv.shmem_fuzz_len; i++) fprintf(stderr, "%02x", (u8)((u8 *)mem)[i]); fprintf(stderr, "\n"); @@ -178,7 +245,7 @@ static void write_with_gap(afl_state_t *afl, void *mem, u32 len, u32 skip_at, } else if (afl->fsrv.out_file) { - if (afl->no_unlink) { + if (unlikely(afl->no_unlink)) { fd = open(afl->fsrv.out_file, O_WRONLY | O_CREAT | O_TRUNC, 0600); @@ -197,18 +264,21 @@ static void write_with_gap(afl_state_t *afl, void *mem, u32 len, u32 skip_at, } - if (skip_at) { ck_write(fd, mem, skip_at, afl->fsrv.out_file); } + if (!post_process_skipped) { + + ck_write(fd, new_mem, new_size, afl->fsrv.out_file); + + } else { - u8 *memu8 = mem; - if (tail_len) { + ck_write(fd, mem, skip_at, afl->fsrv.out_file); - ck_write(fd, memu8 + skip_at + skip_len, tail_len, afl->fsrv.out_file); + ck_write(fd, mem + skip_at + skip_len, tail_len, afl->fsrv.out_file); } if (!afl->fsrv.out_file) { - if (ftruncate(fd, len - skip_len)) { PFATAL("ftruncate() failed"); } + if (ftruncate(fd, new_size)) { PFATAL("ftruncate() failed"); } lseek(fd, 0, SEEK_SET); } else { @@ -226,11 +296,11 @@ static void write_with_gap(afl_state_t *afl, void *mem, u32 len, u32 skip_at, u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem, u32 handicap, u8 from_queue) { + if (unlikely(afl->shm.cmplog_mode)) { q->exec_cksum = 0; } + u8 fault = 0, new_bits = 0, var_detected = 0, hnb = 0, first_run = (q->exec_cksum == 0); - - u64 start_us, stop_us; - + u64 start_us, stop_us, diff_us; s32 old_sc = afl->stage_cur, old_sm = afl->stage_max; u32 use_tmout = afl->fsrv.exec_tmout; u8 *old_sn = afl->stage_name; @@ -264,7 +334,7 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem, } afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon, - afl->afl_env.afl_debug_child_output); + afl->afl_env.afl_debug_child); if (afl->fsrv.support_shmem_fuzz && !afl->fsrv.use_shmem_fuzz) { @@ -310,6 +380,11 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem, } +#ifdef INTROSPECTION + if (unlikely(!q->bitsmap_size)) q->bitsmap_size = afl->bitsmap_size; +#endif + + classify_counts(&afl->fsrv); cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); if (q->exec_cksum != cksum) { @@ -326,6 +401,8 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem, unlikely(afl->first_trace[i] != afl->fsrv.trace_bits[i])) { afl->var_bytes[i] = 1; + // ignore the variable edge by setting it to fully discovered + afl->virgin_bits[i] = 0; } @@ -345,15 +422,32 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem, } - stop_us = get_cur_time_us(); + if (unlikely(afl->fixed_seed)) { + + diff_us = (u64)(afl->fsrv.exec_tmout - 1) * (u64)afl->stage_max; + + } else { + + stop_us = get_cur_time_us(); + diff_us = stop_us - start_us; + if (unlikely(!diff_us)) { ++diff_us; } - afl->total_cal_us += stop_us - start_us; + } + + afl->total_cal_us += diff_us; afl->total_cal_cycles += afl->stage_max; /* OK, let's collect some stats about the performance of this test case. This is used for fuzzing air time calculations in calculate_score(). */ - q->exec_us = (stop_us - start_us) / afl->stage_max; + if (unlikely(!afl->stage_max)) { + + // Pretty sure this cannot happen, yet scan-build complains. + FATAL("BUG: stage_max should not be 0 here! Please report this condition."); + + } + + q->exec_us = diff_us / afl->stage_max; q->bitmap_size = count_bytes(afl, afl->fsrv.trace_bits); q->handicap = handicap; q->cal_failed = 0; @@ -414,7 +508,7 @@ void sync_fuzzers(afl_state_t *afl) { DIR * sd; struct dirent *sd_ent; u32 sync_cnt = 0, synced = 0, entries = 0; - u8 path[PATH_MAX + 256]; + u8 path[PATH_MAX + 1 + NAME_MAX]; sd = opendir(afl->sync_dir); if (!sd) { PFATAL("Unable to open '%s'", afl->sync_dir); } @@ -517,9 +611,10 @@ void sync_fuzzers(afl_state_t *afl) { u8 entry[12]; sprintf(entry, "id:%06u", next_min_accept); + while (m < n) { - if (memcmp(namelist[m]->d_name, entry, 9)) { + if (strncmp(namelist[m]->d_name, entry, 9)) { m++; @@ -532,9 +627,8 @@ void sync_fuzzers(afl_state_t *afl) { } if (m >= n) { goto close_sync; } // nothing new - o = n - 1; - while (o >= m) { + for (o = m; o < n; o++) { s32 fd; struct stat st; @@ -542,7 +636,6 @@ void sync_fuzzers(afl_state_t *afl) { snprintf(path, sizeof(path), "%s/%s", qd_path, namelist[o]->d_name); afl->syncing_case = next_min_accept; next_min_accept++; - o--; /* Allow this to fail in case the other fuzzer is resuming or so... */ @@ -604,7 +697,7 @@ void sync_fuzzers(afl_state_t *afl) { // same time. If so, the first temporary main node running again will demote // themselves so this is not an issue - u8 path[PATH_MAX]; + // u8 path2[PATH_MAX]; afl->is_main_node = 1; sprintf(path, "%s/is_main_node", afl->out_dir); int fd = open(path, O_CREAT | O_RDWR, 0644); @@ -614,6 +707,8 @@ void sync_fuzzers(afl_state_t *afl) { if (afl->foreign_sync_cnt) read_foreign_testcases(afl, 0); + afl->last_sync_time = get_cur_time(); + } /* Trim all new test cases to save cycles when doing deterministic checks. The @@ -622,6 +717,8 @@ void sync_fuzzers(afl_state_t *afl) { u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) { + u32 orig_len = q->len; + /* Custom mutator trimmer */ if (afl->custom_mutators_count) { @@ -639,6 +736,12 @@ u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) { }); + if (orig_len != q->len || custom_trimmed) { + + queue_testcase_retake(afl, q, orig_len); + + } + if (custom_trimmed) return trimmed_case; } @@ -663,12 +766,12 @@ u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) { len_p2 = next_pow2(q->len); - remove_len = MAX(len_p2 / TRIM_START_STEPS, TRIM_MIN_BYTES); + remove_len = MAX(len_p2 / TRIM_START_STEPS, (u32)TRIM_MIN_BYTES); /* Continue until the number of steps gets too high or the stepover gets too small. */ - while (remove_len >= MAX(len_p2 / TRIM_END_STEPS, TRIM_MIN_BYTES)) { + while (remove_len >= MAX(len_p2 / TRIM_END_STEPS, (u32)TRIM_MIN_BYTES)) { u32 remove_pos = remove_len; @@ -687,13 +790,14 @@ u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) { write_with_gap(afl, in_buf, q->len, remove_pos, trim_avail); fault = fuzz_run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout); - ++afl->trim_execs; if (afl->stop_soon || fault == FSRV_RUN_ERROR) { goto abort_trimming; } /* Note that we don't keep track of crashes or hangs here; maybe TODO? */ + ++afl->trim_execs; + classify_counts(&afl->fsrv); cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); /* If the deletion had no impact on the trace, make it permanent. This @@ -745,22 +849,35 @@ u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) { s32 fd; - if (afl->no_unlink) { + if (unlikely(afl->no_unlink)) { fd = open(q->fname, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (fd < 0) { PFATAL("Unable to create '%s'", q->fname); } + + u32 written = 0; + while (written < q->len) { + + ssize_t result = write(fd, in_buf, q->len - written); + if (result > 0) written += result; + + } + } else { unlink(q->fname); /* ignore errors */ fd = open(q->fname, O_WRONLY | O_CREAT | O_EXCL, 0600); - } + if (fd < 0) { PFATAL("Unable to create '%s'", q->fname); } + + ck_write(fd, in_buf, q->len, q->fname); - if (fd < 0) { PFATAL("Unable to create '%s'", q->fname); } + } - ck_write(fd, in_buf, q->len, q->fname); close(fd); + queue_testcase_retake_mem(afl, q, in_buf, q->len, orig_len); + memcpy(afl->fsrv.trace_bits, afl->clean_trace, afl->fsrv.map_size); update_bitmap_score(afl, q); @@ -777,7 +894,8 @@ abort_trimming: error conditions, returning 1 if it's time to bail out. This is a helper function for fuzz_one(). */ -u8 common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) { +u8 __attribute__((hot)) +common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) { u8 fault; diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 66280ed1..3d36e712 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -30,9 +30,9 @@ s8 interesting_8[] = {INTERESTING_8}; s16 interesting_16[] = {INTERESTING_8, INTERESTING_16}; s32 interesting_32[] = {INTERESTING_8, INTERESTING_16, INTERESTING_32}; -char *power_names[POWER_SCHEDULES_NUM] = {"explore", "exploit", "fast", - "coe", "lin", "quad", - "rare", "mmopt", "seek"}; +char *power_names[POWER_SCHEDULES_NUM] = {"explore", "mmopt", "exploit", + "fast", "coe", "lin", + "quad", "rare", "seek"}; /* Initialize MOpt "globals" for this afl state */ @@ -87,13 +87,27 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { afl->w_end = 0.3; afl->g_max = 5000; afl->period_pilot_tmp = 5000.0; - afl->schedule = EXPLORE; /* Power schedule (default: EXPLORE)*/ + afl->schedule = FAST; /* Power schedule (default: FAST) */ afl->havoc_max_mult = HAVOC_MAX_MULT; afl->clear_screen = 1; /* Window resized? */ afl->havoc_div = 1; /* Cycle count divisor for havoc */ afl->stage_name = "init"; /* Name of the current fuzz stage */ afl->splicing_with = -1; /* Splicing with which test case? */ + afl->cpu_to_bind = -1; + afl->havoc_stack_pow2 = HAVOC_STACK_POW2; + afl->cal_cycles = CAL_CYCLES; + afl->cal_cycles_long = CAL_CYCLES_LONG; + afl->hang_tmout = EXEC_TIMEOUT; + afl->stats_update_freq = 1; + afl->stats_avg_exec = 0; + afl->skip_deterministic = 1; + afl->cmplog_lvl = 1; +#ifndef NO_SPLICING + afl->use_splicing = 1; +#endif + afl->q_testcase_max_cache_size = TESTCASE_CACHE_SIZE * 1048576UL; + afl->q_testcase_max_cache_entries = 64 * 1024; #ifdef HAVE_AFFINITY afl->cpu_aff = -1; /* Selected CPU core */ @@ -111,48 +125,16 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { afl->fsrv.use_stdin = 1; afl->fsrv.map_size = map_size; - afl->fsrv.function_opt = (u8 *)afl; - afl->fsrv.function_ptr = &maybe_add_auto; - - afl->cal_cycles = CAL_CYCLES; - afl->cal_cycles_long = CAL_CYCLES_LONG; - + // afl_state_t is not available in forkserver.c + afl->fsrv.afl_ptr = (void *)afl; + afl->fsrv.add_extra_func = (void (*)(void *, u8 *, u32)) & add_extra; afl->fsrv.exec_tmout = EXEC_TIMEOUT; - afl->hang_tmout = EXEC_TIMEOUT; - afl->fsrv.mem_limit = MEM_LIMIT; - - afl->stats_update_freq = 1; - afl->fsrv.dev_urandom_fd = -1; afl->fsrv.dev_null_fd = -1; - afl->fsrv.child_pid = -1; afl->fsrv.out_dir_fd = -1; - afl->cmplog_prev_timed_out = 0; - - /* statis file */ - afl->last_bitmap_cvg = 0; - afl->last_stability = 0; - afl->last_eps = 0; - - /* plot file saves from last run */ - afl->plot_prev_qp = 0; - afl->plot_prev_pf = 0; - afl->plot_prev_pnf = 0; - afl->plot_prev_ce = 0; - afl->plot_prev_md = 0; - afl->plot_prev_qc = 0; - afl->plot_prev_uc = 0; - afl->plot_prev_uh = 0; - - afl->stats_last_stats_ms = 0; - afl->stats_last_plot_ms = 0; - afl->stats_last_ms = 0; - afl->stats_last_execs = 0; - afl->stats_avg_exec = -1; - init_mopt_globals(afl); list_append(&afl_states, afl); @@ -173,6 +155,14 @@ void read_afl_environment(afl_state_t *afl, char **envp) { WARNF("Potentially mistyped AFL environment variable: %s", env); issue_detected = 1; + } else if (strncmp(env, "USE_", 4) == 0) { + + WARNF( + "Potentially mistyped AFL environment variable: %s, did you mean " + "AFL_%s?", + env, env); + issue_detected = 1; + } else if (strncmp(env, "AFL_", 4) == 0) { int i = 0, match = 0; @@ -246,6 +236,13 @@ void read_afl_environment(afl_state_t *afl, char **envp) { afl->afl_env.afl_custom_mutator_only = get_afl_env(afl_environment_variables[i]) ? 1 : 0; + } else if (!strncmp(env, "AFL_CMPLOG_ONLY_NEW", + + afl_environment_variable_len)) { + + afl->afl_env.afl_cmplog_only_new = + get_afl_env(afl_environment_variables[i]) ? 1 : 0; + } else if (!strncmp(env, "AFL_NO_UI", afl_environment_variable_len)) { afl->afl_env.afl_no_ui = @@ -279,11 +276,13 @@ void read_afl_environment(afl_state_t *afl, char **envp) { afl->afl_env.afl_bench_until_crash = get_afl_env(afl_environment_variables[i]) ? 1 : 0; - } else if (!strncmp(env, "AFL_DEBUG_CHILD_OUTPUT", + } else if (!strncmp(env, "AFL_DEBUG_CHILD", + afl_environment_variable_len) || + !strncmp(env, "AFL_DEBUG_CHILD_OUTPUT", afl_environment_variable_len)) { - afl->afl_env.afl_debug_child_output = + afl->afl_env.afl_debug_child = get_afl_env(afl_environment_variables[i]) ? 1 : 0; } else if (!strncmp(env, "AFL_AUTORESUME", @@ -314,6 +313,13 @@ void read_afl_environment(afl_state_t *afl, char **envp) { afl->afl_env.afl_cal_fast = get_afl_env(afl_environment_variables[i]) ? 1 : 0; + } else if (!strncmp(env, "AFL_STATSD", + + afl_environment_variable_len)) { + + afl->afl_env.afl_statsd = + get_afl_env(afl_environment_variables[i]) ? 1 : 0; + } else if (!strncmp(env, "AFL_TMPDIR", afl_environment_variable_len)) { @@ -347,6 +353,86 @@ void read_afl_environment(afl_state_t *afl, char **envp) { afl->afl_env.afl_preload = (u8 *)get_afl_env(afl_environment_variables[i]); + } else if (!strncmp(env, "AFL_MAX_DET_EXTRAS", + + afl_environment_variable_len)) { + + afl->afl_env.afl_max_det_extras = + (u8 *)get_afl_env(afl_environment_variables[i]); + + } else if (!strncmp(env, "AFL_FORKSRV_INIT_TMOUT", + + afl_environment_variable_len)) { + + afl->afl_env.afl_forksrv_init_tmout = + (u8 *)get_afl_env(afl_environment_variables[i]); + + } else if (!strncmp(env, "AFL_TESTCACHE_SIZE", + + afl_environment_variable_len)) { + + afl->afl_env.afl_testcache_size = + (u8 *)get_afl_env(afl_environment_variables[i]); + + } else if (!strncmp(env, "AFL_TESTCACHE_ENTRIES", + + afl_environment_variable_len)) { + + afl->afl_env.afl_testcache_entries = + (u8 *)get_afl_env(afl_environment_variables[i]); + + } else if (!strncmp(env, "AFL_STATSD_HOST", + + afl_environment_variable_len)) { + + afl->afl_env.afl_statsd_host = + (u8 *)get_afl_env(afl_environment_variables[i]); + + } else if (!strncmp(env, "AFL_STATSD_PORT", + + afl_environment_variable_len)) { + + afl->afl_env.afl_statsd_port = + (u8 *)get_afl_env(afl_environment_variables[i]); + + } else if (!strncmp(env, "AFL_STATSD_TAGS_FLAVOR", + + afl_environment_variable_len)) { + + afl->afl_env.afl_statsd_tags_flavor = + (u8 *)get_afl_env(afl_environment_variables[i]); + + } else if (!strncmp(env, "AFL_CRASH_EXITCODE", + + afl_environment_variable_len)) { + + afl->afl_env.afl_crash_exitcode = + (u8 *)get_afl_env(afl_environment_variables[i]); + +#if defined USE_COLOR && !defined ALWAYS_COLORED + + } else if (!strncmp(env, "AFL_NO_COLOR", + + afl_environment_variable_len)) { + + afl->afl_env.afl_statsd_tags_flavor = + (u8 *)get_afl_env(afl_environment_variables[i]); + + } else if (!strncmp(env, "AFL_NO_COLOUR", + + afl_environment_variable_len)) { + + afl->afl_env.afl_statsd_tags_flavor = + (u8 *)get_afl_env(afl_environment_variables[i]); +#endif + + } else if (!strncmp(env, "AFL_KILL_SIGNAL", + + afl_environment_variable_len)) { + + afl->afl_env.afl_kill_signal = + (u8 *)get_afl_env(afl_environment_variables[i]); + } } else { @@ -400,6 +486,8 @@ void read_afl_environment(afl_state_t *afl, char **envp) { WARNF("Mistyped AFL environment variable: %s", env); issue_detected = 1; + print_suggested_envs(env); + } } @@ -419,13 +507,13 @@ void afl_state_deinit(afl_state_t *afl) { if (afl->pass_stats) { ck_free(afl->pass_stats); } if (afl->orig_cmp_map) { ck_free(afl->orig_cmp_map); } - if (afl->queue_buf) { free(afl->queue_buf); } - if (afl->out_buf) { free(afl->out_buf); } - if (afl->out_scratch_buf) { free(afl->out_scratch_buf); } - if (afl->eff_buf) { free(afl->eff_buf); } - if (afl->in_buf) { free(afl->in_buf); } - if (afl->in_scratch_buf) { free(afl->in_scratch_buf); } - if (afl->ex_buf) { free(afl->ex_buf); } + afl_free(afl->queue_buf); + afl_free(afl->out_buf); + afl_free(afl->out_scratch_buf); + afl_free(afl->eff_buf); + afl_free(afl->in_buf); + afl_free(afl->in_scratch_buf); + afl_free(afl->ex_buf); ck_free(afl->virgin_bits); ck_free(afl->virgin_tmout); @@ -453,8 +541,8 @@ void afl_states_stop(void) { LIST_FOREACH(&afl_states, afl_state_t, { - if (el->fsrv.child_pid > 0) kill(el->fsrv.child_pid, SIGKILL); - if (el->fsrv.fsrv_pid > 0) kill(el->fsrv.fsrv_pid, SIGKILL); + if (el->fsrv.child_pid > 0) kill(el->fsrv.child_pid, el->fsrv.kill_signal); + if (el->fsrv.fsrv_pid > 0) kill(el->fsrv.fsrv_pid, el->fsrv.kill_signal); }); diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 7b30b5ea..99059a2d 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -24,32 +24,180 @@ */ #include "afl-fuzz.h" +#include "envs.h" #include <limits.h> -/* Update stats file for unattended monitoring. */ +/* Write fuzzer setup file */ -void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, - double eps) { +void write_setup_file(afl_state_t *afl, u32 argc, char **argv) { + + u8 fn[PATH_MAX]; + snprintf(fn, PATH_MAX, "%s/fuzzer_setup", afl->out_dir); + FILE *f = create_ffile(fn); + u32 i; + + fprintf(f, "# environment variables:\n"); + u32 s_afl_env = (u32)sizeof(afl_environment_variables) / + sizeof(afl_environment_variables[0]) - + 1U; + + for (i = 0; i < s_afl_env; ++i) { + + char *val; + if ((val = getenv(afl_environment_variables[i])) != NULL) { + + fprintf(f, "%s=%s\n", afl_environment_variables[i], val); + + } + + } + + fprintf(f, "# command line:\n"); + + size_t j; + for (i = 0; i < argc; ++i) { + + if (i) fprintf(f, " "); +#ifdef __ANDROID__ + if (memchr(argv[i], '\'', sizeof(argv[i]))) { + +#else + if (index(argv[i], '\'')) { -#ifndef __HAIKU__ - struct rusage rus; #endif - unsigned long long int cur_time = get_cur_time(); - u8 fn[PATH_MAX]; - s32 fd; - FILE * f; - u32 t_bytes = count_non_255_bytes(afl, afl->virgin_bits); + fprintf(f, "'"); + for (j = 0; j < strlen(argv[i]); j++) + if (argv[i][j] == '\'') + fprintf(f, "'\"'\"'"); + else + fprintf(f, "%c", argv[i][j]); + fprintf(f, "'"); + + } else { + + fprintf(f, "'%s'", argv[i]); + + } + } + + fprintf(f, "\n"); + + fclose(f); + (void)(afl_environment_deprecated); + +} + +/* load some of the existing stats file when resuming.*/ +void load_stats_file(afl_state_t *afl) { + + FILE *f; + u8 buf[MAX_LINE]; + u8 * lptr; + u8 fn[PATH_MAX]; + u32 lineno = 0; snprintf(fn, PATH_MAX, "%s/fuzzer_stats", afl->out_dir); + f = fopen(fn, "r"); + if (!f) { + + WARNF("Unable to load stats file '%s'", fn); + return; + + } + + while ((lptr = fgets(buf, MAX_LINE, f))) { + + lineno++; + u8 *lstartptr = lptr; + u8 *rptr = lptr + strlen(lptr) - 1; + u8 keystring[MAX_LINE]; + while (*lptr != ':' && lptr < rptr) { + + lptr++; + + } + + if (*lptr == '\n' || !*lptr) { + + WARNF("Unable to read line %d of stats file", lineno); + continue; + + } - fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (*lptr == ':') { + + *lptr = 0; + strcpy(keystring, lstartptr); + lptr++; + char *nptr; + switch (lineno) { + + case 3: + if (!strcmp(keystring, "run_time ")) + afl->prev_run_time = 1000 * strtoull(lptr, &nptr, 10); + break; + case 5: + if (!strcmp(keystring, "cycles_done ")) + afl->queue_cycle = + strtoull(lptr, &nptr, 10) ? strtoull(lptr, &nptr, 10) + 1 : 0; + break; + case 7: + if (!strcmp(keystring, "execs_done ")) + afl->fsrv.total_execs = strtoull(lptr, &nptr, 10); + break; + case 10: + if (!strcmp(keystring, "paths_total ")) + afl->queued_paths = strtoul(lptr, &nptr, 10); + break; + case 12: + if (!strcmp(keystring, "paths_found ")) + afl->queued_discovered = strtoul(lptr, &nptr, 10); + break; + case 13: + if (!strcmp(keystring, "paths_imported ")) + afl->queued_imported = strtoul(lptr, &nptr, 10); + break; + case 14: + if (!strcmp(keystring, "max_depth ")) + afl->max_depth = strtoul(lptr, &nptr, 10); + break; + case 21: + if (!strcmp(keystring, "unique_crashes ")) + afl->unique_crashes = strtoull(lptr, &nptr, 10); + break; + case 22: + if (!strcmp(keystring, "unique_hangs ")) + afl->unique_hangs = strtoull(lptr, &nptr, 10); + break; + default: + break; - if (fd < 0) { PFATAL("Unable to create '%s'", fn); } + } + + } + + } + + return; + +} + +/* Update stats file for unattended monitoring. */ + +void write_stats_file(afl_state_t *afl, u32 t_bytes, double bitmap_cvg, + double stability, double eps) { - f = fdopen(fd, "w"); +#ifndef __HAIKU__ + struct rusage rus; +#endif - if (!f) { PFATAL("fdopen() failed"); } + u64 cur_time = get_cur_time(); + u8 fn[PATH_MAX]; + FILE *f; + + snprintf(fn, PATH_MAX, "%s/fuzzer_stats", afl->out_dir); + f = create_ffile(fn); /* Keep last values in case we're called from another context where exec/sec stats and such are not readily available. */ @@ -71,8 +219,8 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, cur_time - afl->last_avg_exec_update >= 60000))) { afl->last_avg_execs_saved = - (float)(1000 * (afl->fsrv.total_execs - afl->last_avg_execs)) / - (float)(cur_time - afl->last_avg_exec_update); + (double)(1000 * (afl->fsrv.total_execs - afl->last_avg_execs)) / + (double)(cur_time - afl->last_avg_exec_update); afl->last_avg_execs = afl->fsrv.total_execs; afl->last_avg_exec_update = cur_time; @@ -116,17 +264,21 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, "edges_found : %u\n" "var_byte_count : %u\n" "havoc_expansion : %u\n" + "testcache_size : %llu\n" + "testcache_count : %u\n" + "testcache_evict : %u\n" "afl_banner : %s\n" "afl_version : " VERSION "\n" "target_mode : %s%s%s%s%s%s%s%s%s\n" "command_line : %s\n", - afl->start_time / 1000, cur_time / 1000, - (cur_time - afl->start_time) / 1000, (u32)getpid(), - afl->queue_cycle ? (afl->queue_cycle - 1) : 0, afl->cycles_wo_finds, - afl->fsrv.total_execs, + (afl->start_time - afl->prev_run_time) / 1000, cur_time / 1000, + (afl->prev_run_time + cur_time - afl->start_time) / 1000, + (u32)getpid(), afl->queue_cycle ? (afl->queue_cycle - 1) : 0, + afl->cycles_wo_finds, afl->fsrv.total_execs, afl->fsrv.total_execs / - ((double)(get_cur_time() - afl->start_time) / 1000), + ((double)(afl->prev_run_time + get_cur_time() - afl->start_time) / + 1000), afl->last_avg_execs_saved, afl->queued_paths, afl->queued_favored, afl->queued_discovered, afl->queued_imported, afl->max_depth, afl->current_entry, afl->pending_favored, afl->pending_not_fuzzed, @@ -149,7 +301,9 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, #else -1, #endif - t_bytes, afl->var_byte_count, afl->expand_havoc, afl->use_banner, + t_bytes, afl->var_byte_count, afl->expand_havoc, + afl->q_testcase_cache_size, afl->q_testcase_cache_count, + afl->q_testcase_evictions, afl->use_banner, afl->unicorn_mode ? "unicorn" : "", afl->fsrv.qemu_mode ? "qemu " : "", afl->non_instrumented_mode ? " non_instrumented " : "", @@ -163,17 +317,18 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, ? "" : "default", afl->orig_cmdline); + /* ignore errors */ if (afl->debug) { - uint32_t i = 0; + u32 i = 0; fprintf(f, "virgin_bytes :"); for (i = 0; i < afl->fsrv.map_size; i++) { if (afl->virgin_bits[i] != 0xff) { - fprintf(f, " %d[%02x]", i, afl->virgin_bits[i]); + fprintf(f, " %u[%02x]", i, afl->virgin_bits[i]); } @@ -183,7 +338,7 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, fprintf(f, "var_bytes :"); for (i = 0; i < afl->fsrv.map_size; i++) { - if (afl->var_bytes[i]) { fprintf(f, " %d", i); } + if (afl->var_bytes[i]) { fprintf(f, " %u", i); } } @@ -197,16 +352,19 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, /* Update the plot file if there is a reason to. */ -void maybe_update_plot_file(afl_state_t *afl, double bitmap_cvg, double eps) { +void maybe_update_plot_file(afl_state_t *afl, u32 t_bytes, double bitmap_cvg, + double eps) { - if (unlikely(afl->plot_prev_qp == afl->queued_paths && + if (unlikely(afl->stop_soon) || + unlikely(afl->plot_prev_qp == afl->queued_paths && afl->plot_prev_pf == afl->pending_favored && afl->plot_prev_pnf == afl->pending_not_fuzzed && afl->plot_prev_ce == afl->current_entry && afl->plot_prev_qc == afl->queue_cycle && afl->plot_prev_uc == afl->unique_crashes && afl->plot_prev_uh == afl->unique_hangs && - afl->plot_prev_md == afl->max_depth) || + afl->plot_prev_md == afl->max_depth && + afl->plot_prev_ed == afl->fsrv.total_execs) || unlikely(!afl->queue_cycle) || unlikely(get_cur_time() - afl->start_time <= 60)) { @@ -222,19 +380,21 @@ void maybe_update_plot_file(afl_state_t *afl, double bitmap_cvg, double eps) { afl->plot_prev_uc = afl->unique_crashes; afl->plot_prev_uh = afl->unique_hangs; afl->plot_prev_md = afl->max_depth; + afl->plot_prev_ed = afl->fsrv.total_execs; /* Fields in the file: unix_time, afl->cycles_done, cur_path, paths_total, paths_not_fuzzed, - favored_not_fuzzed, afl->unique_crashes, afl->unique_hangs, afl->max_depth, - execs_per_sec */ + favored_not_fuzzed, unique_crashes, unique_hangs, max_depth, + execs_per_sec, edges_found */ fprintf(afl->fsrv.plot_file, - "%llu, %llu, %u, %u, %u, %u, %0.02f%%, %llu, %llu, %u, %0.02f\n", + "%llu, %llu, %u, %u, %u, %u, %0.02f%%, %llu, %llu, %u, %0.02f, %llu, " + "%u\n", get_cur_time() / 1000, afl->queue_cycle - 1, afl->current_entry, afl->queued_paths, afl->pending_not_fuzzed, afl->pending_favored, bitmap_cvg, afl->unique_crashes, afl->unique_hangs, afl->max_depth, - eps); /* ignore errors */ + eps, afl->plot_prev_ed, t_bytes); /* ignore errors */ fflush(afl->fsrv.plot_file); @@ -311,28 +471,37 @@ void show_stats(afl_state_t *afl) { /* Calculate smoothed exec speed stats. */ - if (!afl->stats_last_execs) { + if (unlikely(!afl->stats_last_execs)) { - afl->stats_avg_exec = - ((double)afl->fsrv.total_execs) * 1000 / (cur_ms - afl->start_time); + if (likely(cur_ms != afl->start_time)) { + + afl->stats_avg_exec = ((double)afl->fsrv.total_execs) * 1000 / + (afl->prev_run_time + cur_ms - afl->start_time); + + } } else { - double cur_avg = ((double)(afl->fsrv.total_execs - afl->stats_last_execs)) * - 1000 / (cur_ms - afl->stats_last_ms); + if (likely(cur_ms != afl->stats_last_ms)) { - /* If there is a dramatic (5x+) jump in speed, reset the indicator - more quickly. */ + double cur_avg = + ((double)(afl->fsrv.total_execs - afl->stats_last_execs)) * 1000 / + (cur_ms - afl->stats_last_ms); - if (cur_avg * 5 < afl->stats_avg_exec || - cur_avg / 5 > afl->stats_avg_exec) { + /* If there is a dramatic (5x+) jump in speed, reset the indicator + more quickly. */ - afl->stats_avg_exec = cur_avg; + if (cur_avg * 5 < afl->stats_avg_exec || + cur_avg / 5 > afl->stats_avg_exec) { - } + afl->stats_avg_exec = cur_avg; - afl->stats_avg_exec = afl->stats_avg_exec * (1.0 - 1.0 / AVG_SMOOTHING) + - cur_avg * (1.0 / AVG_SMOOTHING); + } + + afl->stats_avg_exec = afl->stats_avg_exec * (1.0 - 1.0 / AVG_SMOOTHING) + + cur_avg * (1.0 / AVG_SMOOTHING); + + } } @@ -364,18 +533,31 @@ void show_stats(afl_state_t *afl) { if (cur_ms - afl->stats_last_stats_ms > STATS_UPDATE_SEC * 1000) { afl->stats_last_stats_ms = cur_ms; - write_stats_file(afl, t_byte_ratio, stab_ratio, afl->stats_avg_exec); + write_stats_file(afl, t_bytes, t_byte_ratio, stab_ratio, + afl->stats_avg_exec); save_auto(afl); write_bitmap(afl); } + if (unlikely(afl->afl_env.afl_statsd)) { + + if (cur_ms - afl->statsd_last_send_ms > STATSD_UPDATE_SEC * 1000) { + + /* reset counter, even if send failed. */ + afl->statsd_last_send_ms = cur_ms; + if (statsd_send_metric(afl)) { WARNF("could not send statsd metric."); } + + } + + } + /* Every now and then, write plot data. */ if (cur_ms - afl->stats_last_plot_ms > PLOT_UPDATE_SEC * 1000) { afl->stats_last_plot_ms = cur_ms; - maybe_update_plot_file(afl, t_byte_ratio, afl->stats_avg_exec); + maybe_update_plot_file(afl, t_bytes, t_byte_ratio, afl->stats_avg_exec); } @@ -463,6 +645,13 @@ void show_stats(afl_state_t *afl) { #define SP10 SP5 SP5 #define SP20 SP10 SP10 + /* Since `total_crashes` does not get reloaded from disk on restart, + it indicates if we found crashes this round already -> paint red. + If it's 0, but `unique_crashes` is set from a past run, paint in yellow. */ + char *crash_color = afl->total_crashes ? cLRD + : afl->unique_crashes ? cYEL + : cRST; + /* Lord, forgive me this. */ SAYF(SET_G1 bSTG bLT bH bSTOP cCYA @@ -507,7 +696,7 @@ void show_stats(afl_state_t *afl) { } - u_stringify_time_diff(time_tmp, cur_ms, afl->start_time); + u_stringify_time_diff(time_tmp, afl->prev_run_time + cur_ms, afl->start_time); SAYF(bV bSTOP " run time : " cRST "%-33s " bSTG bV bSTOP " cycles done : %s%-5s " bSTG bV "\n", time_tmp, tmp, u_stringify_int(IB(0), afl->queue_cycle - 1)); @@ -550,7 +739,7 @@ void show_stats(afl_state_t *afl) { u_stringify_time_diff(time_tmp, cur_ms, afl->last_crash_time); SAYF(bV bSTOP " last uniq crash : " cRST "%-33s " bSTG bV bSTOP " uniq crashes : %s%-6s" bSTG bV "\n", - time_tmp, afl->unique_crashes ? cLRD : cRST, tmp); + time_tmp, crash_color, tmp); sprintf(tmp, "%s%s", u_stringify_int(IB(0), afl->unique_hangs), (afl->unique_hangs >= KEEP_UNIQUE_HANG) ? "+" : ""); @@ -633,15 +822,13 @@ void show_stats(afl_state_t *afl) { SAYF(bV bSTOP " total execs : " cRST "%-20s " bSTG bV bSTOP " new crashes : %s%-22s" bSTG bV "\n", - u_stringify_int(IB(0), afl->fsrv.total_execs), - afl->unique_crashes ? cLRD : cRST, tmp); + u_stringify_int(IB(0), afl->fsrv.total_execs), crash_color, tmp); } else { SAYF(bV bSTOP " total execs : " cRST "%-20s " bSTG bV bSTOP " total crashes : %s%-22s" bSTG bV "\n", - u_stringify_int(IB(0), afl->fsrv.total_execs), - afl->unique_crashes ? cLRD : cRST, tmp); + u_stringify_int(IB(0), afl->fsrv.total_execs), crash_color, tmp); } @@ -890,19 +1077,19 @@ void show_stats(afl_state_t *afl) { if (afl->cpu_aff >= 0) { SAYF("%s" cGRA "[cpu%03u:%s%3u%%" cGRA "]\r" cRST, spacing, - MIN(afl->cpu_aff, 999), cpu_color, MIN(cur_utilization, 999)); + MIN(afl->cpu_aff, 999), cpu_color, MIN(cur_utilization, (u32)999)); } else { SAYF("%s" cGRA " [cpu:%s%3u%%" cGRA "]\r" cRST, spacing, cpu_color, - MIN(cur_utilization, 999)); + MIN(cur_utilization, (u32)999)); } #else SAYF("%s" cGRA " [cpu:%s%3u%%" cGRA "]\r" cRST, spacing, cpu_color, - MIN(cur_utilization, 999)); + MIN(cur_utilization, (u32)999)); #endif /* ^HAVE_AFFINITY */ @@ -929,11 +1116,10 @@ void show_stats(afl_state_t *afl) { void show_init_stats(afl_state_t *afl) { - struct queue_entry *q = afl->queue; - u32 min_bits = 0, max_bits = 0; + struct queue_entry *q; + u32 min_bits = 0, max_bits = 0, max_len = 0, count = 0, i; u64 min_us = 0, max_us = 0; u64 avg_us = 0; - u32 max_len = 0; u8 val_bufs[4][STRINGIFY_VAL_SIZE_MAX]; #define IB(i) val_bufs[(i)], sizeof(val_bufs[(i)]) @@ -944,7 +1130,10 @@ void show_init_stats(afl_state_t *afl) { } - while (q) { + for (i = 0; i < afl->queued_paths; i++) { + + q = afl->queue_buf[i]; + if (unlikely(q->disabled)) { continue; } if (!min_us || q->exec_us < min_us) { min_us = q->exec_us; } if (q->exec_us > max_us) { max_us = q->exec_us; } @@ -954,11 +1143,11 @@ void show_init_stats(afl_state_t *afl) { if (q->len > max_len) { max_len = q->len; } - q = q->next; + ++count; } - SAYF("\n"); + // SAYF("\n"); if (avg_us > ((afl->fsrv.qemu_mode || afl->unicorn_mode) ? 50000 : 10000)) { @@ -969,7 +1158,11 @@ void show_init_stats(afl_state_t *afl) { /* Let's keep things moving with slow binaries. */ - if (avg_us > 50000) { + if (unlikely(afl->fixed_seed)) { + + afl->havoc_div = 1; + + } else if (avg_us > 50000) { afl->havoc_div = 10; /* 0-19 execs/sec */ @@ -1020,17 +1213,18 @@ void show_init_stats(afl_state_t *afl) { OKF("Here are some useful stats:\n\n" cGRA " Test case count : " cRST - "%u favored, %u variable, %u total\n" cGRA " Bitmap range : " cRST + "%u favored, %u variable, %u ignored, %u total\n" cGRA + " Bitmap range : " cRST "%u to %u bits (average: %0.02f bits)\n" cGRA " Exec timing : " cRST "%s to %s us (average: %s us)\n", - afl->queued_favored, afl->queued_variable, afl->queued_paths, min_bits, - max_bits, + afl->queued_favored, afl->queued_variable, afl->queued_paths - count, + afl->queued_paths, min_bits, max_bits, ((double)afl->total_bitmap_size) / (afl->total_bitmap_entries ? afl->total_bitmap_entries : 1), stringify_int(IB(0), min_us), stringify_int(IB(1), max_us), stringify_int(IB(2), avg_us)); - if (!afl->timeout_given) { + if (afl->timeout_given != 1) { /* Figure out the appropriate timeout. The basic idea is: 5x average or 1x max, rounded up to EXEC_TM_ROUND ms and capped at 1 second. @@ -1039,7 +1233,11 @@ void show_init_stats(afl_state_t *afl) { random scheduler jitter is less likely to have any impact, and because our patience is wearing thin =) */ - if (avg_us > 50000) { + if (unlikely(afl->fixed_seed)) { + + afl->fsrv.exec_tmout = avg_us * 5 / 1000; + + } else if (avg_us > 50000) { afl->fsrv.exec_tmout = avg_us * 2 / 1000; @@ -1073,6 +1271,11 @@ void show_init_stats(afl_state_t *afl) { ACTF("Applying timeout settings from resumed session (%u ms).", afl->fsrv.exec_tmout); + } else { + + ACTF("-t option specified. We'll use an exec timeout of %u ms.", + afl->fsrv.exec_tmout); + } /* In non-instrumented mode, re-running every timing out test case with a @@ -1081,7 +1284,7 @@ void show_init_stats(afl_state_t *afl) { if (afl->non_instrumented_mode && !(afl->afl_env.afl_hang_tmout)) { - afl->hang_tmout = MIN(EXEC_TIMEOUT, afl->fsrv.exec_tmout * 2 + 100); + afl->hang_tmout = MIN((u32)EXEC_TIMEOUT, afl->fsrv.exec_tmout * 2 + 100); } diff --git a/src/afl-fuzz-statsd.c b/src/afl-fuzz-statsd.c new file mode 100644 index 00000000..461bbbf6 --- /dev/null +++ b/src/afl-fuzz-statsd.c @@ -0,0 +1,273 @@ +/* + * This implements rpc.statsd support, see docs/rpc_statsd.md + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <string.h> +#include <sys/types.h> +#include <netdb.h> +#include <unistd.h> +#include "afl-fuzz.h" + +#define MAX_STATSD_PACKET_SIZE 4096 +#define MAX_TAG_LEN 200 +#define METRIC_PREFIX "fuzzing" + +/* Tags format for metrics + DogStatsD: + metric.name:<value>|<type>|#key:value,key2:value2 + + InfluxDB + metric.name,key=value,key2=value2:<value>|<type> + + Librato + metric.name#key=value,key2=value2:<value>|<type> + + SignalFX + metric.name[key=value,key2=value2]:<value>|<type> + +*/ + +// after the whole metric. +#define DOGSTATSD_TAGS_FORMAT "|#banner:%s,afl_version:%s" + +// just after the metric name. +#define LIBRATO_TAGS_FORMAT "#banner=%s,afl_version=%s" +#define INFLUXDB_TAGS_FORMAT ",banner=%s,afl_version=%s" +#define SIGNALFX_TAGS_FORMAT "[banner=%s,afl_version=%s]" + +// For DogstatsD +#define STATSD_TAGS_TYPE_SUFFIX 1 +#define STATSD_TAGS_SUFFIX_METRICS \ + METRIC_PREFIX \ + ".cycle_done:%llu|g%s\n" METRIC_PREFIX \ + ".cycles_wo_finds:%llu|g%s\n" METRIC_PREFIX \ + ".execs_done:%llu|g%s\n" METRIC_PREFIX \ + ".execs_per_sec:%0.02f|g%s\n" METRIC_PREFIX \ + ".paths_total:%u|g%s\n" METRIC_PREFIX \ + ".paths_favored:%u|g%s\n" METRIC_PREFIX \ + ".paths_found:%u|g%s\n" METRIC_PREFIX \ + ".paths_imported:%u|g%s\n" METRIC_PREFIX ".max_depth:%u|g%s\n" METRIC_PREFIX \ + ".cur_path:%u|g%s\n" METRIC_PREFIX ".pending_favs:%u|g%s\n" METRIC_PREFIX \ + ".pending_total:%u|g%s\n" METRIC_PREFIX \ + ".variable_paths:%u|g%s\n" METRIC_PREFIX \ + ".unique_crashes:%llu|g%s\n" METRIC_PREFIX \ + ".unique_hangs:%llu|g%s\n" METRIC_PREFIX \ + ".total_crashes:%llu|g%s\n" METRIC_PREFIX \ + ".slowest_exec_ms:%u|g%s\n" METRIC_PREFIX \ + ".edges_found:%u|g%s\n" METRIC_PREFIX \ + ".var_byte_count:%u|g%s\n" METRIC_PREFIX ".havoc_expansion:%u|g%s\n" + +// For Librato, InfluxDB, SignalFX +#define STATSD_TAGS_TYPE_MID 2 +#define STATSD_TAGS_MID_METRICS \ + METRIC_PREFIX \ + ".cycle_done%s:%llu|g\n" METRIC_PREFIX \ + ".cycles_wo_finds%s:%llu|g\n" METRIC_PREFIX \ + ".execs_done%s:%llu|g\n" METRIC_PREFIX \ + ".execs_per_sec%s:%0.02f|g\n" METRIC_PREFIX \ + ".paths_total%s:%u|g\n" METRIC_PREFIX \ + ".paths_favored%s:%u|g\n" METRIC_PREFIX \ + ".paths_found%s:%u|g\n" METRIC_PREFIX \ + ".paths_imported%s:%u|g\n" METRIC_PREFIX ".max_depth%s:%u|g\n" METRIC_PREFIX \ + ".cur_path%s:%u|g\n" METRIC_PREFIX ".pending_favs%s:%u|g\n" METRIC_PREFIX \ + ".pending_total%s:%u|g\n" METRIC_PREFIX \ + ".variable_paths%s:%u|g\n" METRIC_PREFIX \ + ".unique_crashes%s:%llu|g\n" METRIC_PREFIX \ + ".unique_hangs%s:%llu|g\n" METRIC_PREFIX \ + ".total_crashes%s:%llu|g\n" METRIC_PREFIX \ + ".slowest_exec_ms%s:%u|g\n" METRIC_PREFIX \ + ".edges_found%s:%u|g\n" METRIC_PREFIX \ + ".var_byte_count%s:%u|g\n" METRIC_PREFIX ".havoc_expansion%s:%u|g\n" + +void statsd_setup_format(afl_state_t *afl) { + + if (afl->afl_env.afl_statsd_tags_flavor && + strcmp(afl->afl_env.afl_statsd_tags_flavor, "dogstatsd") == 0) { + + afl->statsd_tags_format = DOGSTATSD_TAGS_FORMAT; + afl->statsd_metric_format = STATSD_TAGS_SUFFIX_METRICS; + afl->statsd_metric_format_type = STATSD_TAGS_TYPE_SUFFIX; + + } else if (afl->afl_env.afl_statsd_tags_flavor && + + strcmp(afl->afl_env.afl_statsd_tags_flavor, "librato") == 0) { + + afl->statsd_tags_format = LIBRATO_TAGS_FORMAT; + afl->statsd_metric_format = STATSD_TAGS_MID_METRICS; + afl->statsd_metric_format_type = STATSD_TAGS_TYPE_MID; + + } else if (afl->afl_env.afl_statsd_tags_flavor && + + strcmp(afl->afl_env.afl_statsd_tags_flavor, "influxdb") == 0) { + + afl->statsd_tags_format = INFLUXDB_TAGS_FORMAT; + afl->statsd_metric_format = STATSD_TAGS_MID_METRICS; + afl->statsd_metric_format_type = STATSD_TAGS_TYPE_MID; + + } else if (afl->afl_env.afl_statsd_tags_flavor && + + strcmp(afl->afl_env.afl_statsd_tags_flavor, "signalfx") == 0) { + + afl->statsd_tags_format = SIGNALFX_TAGS_FORMAT; + afl->statsd_metric_format = STATSD_TAGS_MID_METRICS; + afl->statsd_metric_format_type = STATSD_TAGS_TYPE_MID; + + } else { + + // No tags at all. + afl->statsd_tags_format = ""; + // Still need to pick a format. Doesn't change anything since if will be + // replaced by the empty string anyway. + afl->statsd_metric_format = STATSD_TAGS_MID_METRICS; + afl->statsd_metric_format_type = STATSD_TAGS_TYPE_MID; + + } + +} + +int statsd_socket_init(afl_state_t *afl) { + + /* Default port and host. + Will be overwritten by AFL_STATSD_PORT and AFL_STATSD_HOST environment + variable, if they exists. + */ + u16 port = STATSD_DEFAULT_PORT; + char *host = STATSD_DEFAULT_HOST; + + if (afl->afl_env.afl_statsd_port) { + + port = atoi(afl->afl_env.afl_statsd_port); + + } + + if (afl->afl_env.afl_statsd_host) { host = afl->afl_env.afl_statsd_host; } + + int sock; + if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { + + FATAL("Failed to create socket"); + + } + + memset(&afl->statsd_server, 0, sizeof(afl->statsd_server)); + afl->statsd_server.sin_family = AF_INET; + afl->statsd_server.sin_port = htons(port); + + struct addrinfo *result; + struct addrinfo hints; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + + if ((getaddrinfo(host, NULL, &hints, &result))) { + + FATAL("Fail to getaddrinfo"); + + } + + memcpy(&(afl->statsd_server.sin_addr), + &((struct sockaddr_in *)result->ai_addr)->sin_addr, + sizeof(struct in_addr)); + freeaddrinfo(result); + + return sock; + +} + +int statsd_send_metric(afl_state_t *afl) { + + char buff[MAX_STATSD_PACKET_SIZE] = {0}; + + /* afl->statsd_sock is set once in the initialisation of afl-fuzz and reused + each time If the sendto later fail, we reset it to 0 to be able to recreates + it. + */ + if (!afl->statsd_sock) { + + afl->statsd_sock = statsd_socket_init(afl); + if (!afl->statsd_sock) { + + WARNF("Cannot create socket"); + return -1; + + } + + } + + statsd_format_metric(afl, buff, MAX_STATSD_PACKET_SIZE); + if (sendto(afl->statsd_sock, buff, strlen(buff), 0, + (struct sockaddr *)&afl->statsd_server, + sizeof(afl->statsd_server)) == -1) { + + if (!close(afl->statsd_sock)) { PFATAL("Cannot close socket"); } + afl->statsd_sock = 0; + WARNF("Cannot sendto"); + return -1; + + } + + return 0; + +} + +int statsd_format_metric(afl_state_t *afl, char *buff, size_t bufflen) { + + char tags[MAX_TAG_LEN * 2] = {0}; + if (afl->statsd_tags_format) { + + snprintf(tags, MAX_TAG_LEN * 2, afl->statsd_tags_format, afl->use_banner, + VERSION); + + } + + /* Sends multiple metrics with one UDP Packet. + bufflen will limit to the max safe size. + */ + if (afl->statsd_metric_format_type == STATSD_TAGS_TYPE_SUFFIX) { + + snprintf( + buff, bufflen, afl->statsd_metric_format, + afl->queue_cycle ? (afl->queue_cycle - 1) : 0, tags, + afl->cycles_wo_finds, tags, afl->fsrv.total_execs, tags, + afl->fsrv.total_execs / + ((double)(get_cur_time() + afl->prev_run_time - afl->start_time) / + 1000), + tags, afl->queued_paths, tags, afl->queued_favored, tags, + afl->queued_discovered, tags, afl->queued_imported, tags, + afl->max_depth, tags, afl->current_entry, tags, afl->pending_favored, + tags, afl->pending_not_fuzzed, tags, afl->queued_variable, tags, + afl->unique_crashes, tags, afl->unique_hangs, tags, afl->total_crashes, + tags, afl->slowest_exec_ms, tags, + count_non_255_bytes(afl, afl->virgin_bits), tags, afl->var_byte_count, + tags, afl->expand_havoc, tags); + + } else if (afl->statsd_metric_format_type == STATSD_TAGS_TYPE_MID) { + + snprintf( + buff, bufflen, afl->statsd_metric_format, tags, + afl->queue_cycle ? (afl->queue_cycle - 1) : 0, tags, + afl->cycles_wo_finds, tags, afl->fsrv.total_execs, tags, + afl->fsrv.total_execs / + ((double)(get_cur_time() + afl->prev_run_time - afl->start_time) / + 1000), + tags, afl->queued_paths, tags, afl->queued_favored, tags, + afl->queued_discovered, tags, afl->queued_imported, tags, + afl->max_depth, tags, afl->current_entry, tags, afl->pending_favored, + tags, afl->pending_not_fuzzed, tags, afl->queued_variable, tags, + afl->unique_crashes, tags, afl->unique_hangs, tags, afl->total_crashes, + tags, afl->slowest_exec_ms, tags, + count_non_255_bytes(afl, afl->virgin_bits), tags, afl->var_byte_count, + tags, afl->expand_havoc); + + } + + return 0; + +} + diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 5bedf6e1..ff4c5281 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -26,6 +26,7 @@ #include "afl-fuzz.h" #include "cmplog.h" #include <limits.h> +#include <stdlib.h> #ifndef USEMMAP #include <sys/mman.h> #include <sys/stat.h> @@ -40,7 +41,7 @@ extern u64 time_spent_working; static void at_exit() { - int i; + s32 i, pid1 = 0, pid2 = 0; char *list[4] = {SHM_ENV_VAR, SHM_FUZZ_ENV_VAR, CMPLOG_SHM_ENV_VAR, NULL}; char *ptr; @@ -48,10 +49,10 @@ static void at_exit() { if (ptr && *ptr) unlink(ptr); ptr = getenv("__AFL_TARGET_PID1"); - if (ptr && *ptr && (i = atoi(ptr)) > 0) kill(i, SIGKILL); + if (ptr && *ptr && (pid1 = atoi(ptr)) > 0) kill(pid1, SIGTERM); ptr = getenv("__AFL_TARGET_PID2"); - if (ptr && *ptr && (i = atoi(ptr)) > 0) kill(i, SIGKILL); + if (ptr && *ptr && (pid2 = atoi(ptr)) > 0) kill(pid2, SIGTERM); i = 0; while (list[i] != NULL) { @@ -75,11 +76,18 @@ static void at_exit() { } + int kill_signal = SIGKILL; + /* AFL_KILL_SIGNAL should already be a valid int at this point */ + if ((ptr = getenv("AFL_KILL_SIGNAL"))) { kill_signal = atoi(ptr); } + + if (pid1 > 0) { kill(pid1, kill_signal); } + if (pid2 > 0) { kill(pid2, kill_signal); } + } /* Display usage hints. */ -static void usage(afl_state_t *afl, u8 *argv0, int more_help) { +static void usage(u8 *argv0, int more_help) { SAYF( "\n%s [ options ] -- /path/to/fuzzed_app [ ... ]\n\n" @@ -89,21 +97,25 @@ static void usage(afl_state_t *afl, u8 *argv0, int more_help) { " -o dir - output directory for fuzzer findings\n\n" "Execution control settings:\n" - " -p schedule - power schedules compute a seed's performance score. " - "<explore\n" - " (default), fast, coe, lin, quad, exploit, mmopt, " - "rare, seek>\n" - " see docs/power_schedules.md\n" + " -p schedule - power schedules compute a seed's performance score:\n" + " fast(default), explore, exploit, seek, rare, mmopt, " + "coe, lin\n" + " quad -- see docs/power_schedules.md\n" " -f file - location read by the fuzzed program (default: stdin " "or @@)\n" - " -t msec - timeout for each run (auto-scaled, 50-%d ms)\n" - " -m megs - memory limit for child process (%d MB)\n" + " -t msec - timeout for each run (auto-scaled, default %u ms). " + "Add a '+'\n" + " to auto-calculate the timeout, the value being the " + "maximum.\n" + " -m megs - memory limit for child process (%u MB, 0 = no limit " + "[default])\n" " -Q - use binary-only instrumentation (QEMU mode)\n" " -U - use unicorn-based instrumentation (Unicorn mode)\n" " -W - use qemu-based instrumentation with Wine (Wine " "mode)\n\n" "Mutator settings:\n" + " -D - enable deterministic fuzzing (once per queue entry)\n" " -L minutes - use MOpt(imize) mode and set the time limit for " "entering the\n" " pacemaker mode (minutes of no new paths). 0 = " @@ -112,43 +124,57 @@ static void usage(afl_state_t *afl, u8 *argv0, int more_help) { " See docs/README.MOpt.md\n" " -c program - enable CmpLog by specifying a binary compiled for " "it.\n" - " if using QEMU, just use -c 0.\n\n" - + " if using QEMU, just use -c 0.\n" + " -l cmplog_opts - CmpLog configuration values (e.g. \"2AT\"):\n" + " 1=small files (default), 2=larger files, 3=all " + "files,\n" + " A=arithmetic solving, T=transformational solving.\n\n" "Fuzzing behavior settings:\n" - " -N - do not unlink the fuzzing input file (only for " - "devices etc.!)\n" - " -d - quick & dirty mode (skips deterministic steps)\n" + " -Z - sequential queue selection instead of weighted " + "random\n" + " -N - do not unlink the fuzzing input file (for devices " + "etc.)\n" " -n - fuzz without instrumentation (non-instrumented mode)\n" - " -x dict_file - optional fuzzer dictionary (see README.md, its really " - "good!)\n\n" + " -x dict_file - fuzzer dictionary (see README.md, specify up to 4 " + "times)\n\n" "Testing settings:\n" " -s seed - use a fixed seed for the RNG\n" - " -V seconds - fuzz for a specific time then terminate\n" - " -E execs - fuzz for a approx. no of total executions then " + " -V seconds - fuzz for a specified time then terminate\n" + " -E execs - fuzz for an approx. no. of total executions then " "terminate\n" " Note: not precise and can have several more " "executions.\n\n" "Other stuff:\n" " -M/-S id - distributed mode (see docs/parallel_fuzzing.md)\n" - " use -D to force -S secondary to perform deterministic " - "fuzzing\n" + " -M auto-sets -D, -Z (use -d to disable -D) and no " + "trimming\n" " -F path - sync to a foreign fuzzer queue directory (requires " "-M, can\n" " be specified up to %u times)\n" + " -d - skip deterministic fuzzing in -M mode\n" " -T text - text banner to show on the screen\n" " -I command - execute this command/script when a new crash is " "found\n" //" -B bitmap.txt - mutate a specific test case, use the out/fuzz_bitmap //" "file\n" " -C - crash exploration mode (the peruvian rabbit thing)\n" + " -b cpu_id - bind the fuzzing process to the specified CPU core " + "(0-...)\n" " -e ext - file extension for the fuzz test input file (if " "needed)\n\n", argv0, EXEC_TIMEOUT, MEM_LIMIT, FOREIGN_SYNCS_MAX); if (more_help > 1) { +#if defined USE_COLOR && !defined ALWAYS_COLORED + #define DYN_COLOR \ + "AFL_NO_COLOR or AFL_NO_COLOUR: switch colored console output off\n" +#else + #define DYN_COLOR +#endif + SAYF( "Environment variables used:\n" "LD_BIND_LAZY: do not set LD_BIND_NOW env var for target\n" @@ -159,34 +185,55 @@ static void usage(afl_state_t *afl, u8 *argv0, int more_help) { "AFL_AUTORESUME: resume fuzzing if directory specified by -o already exists\n" "AFL_BENCH_JUST_ONE: run the target just once\n" "AFL_BENCH_UNTIL_CRASH: exit soon when the first crashing input has been found\n" + "AFL_CMPLOG_ONLY_NEW: do not run cmplog on initial testcases (good for resumes!)\n" + "AFL_CRASH_EXITCODE: optional child exit code to be interpreted as crash\n" "AFL_CUSTOM_MUTATOR_LIBRARY: lib with afl_custom_fuzz() to mutate inputs\n" "AFL_CUSTOM_MUTATOR_ONLY: avoid AFL++'s internal mutators\n" + "AFL_CYCLE_SCHEDULES: after completing a cycle, switch to a different -p schedule\n" "AFL_DEBUG: extra debugging output for Python mode trimming\n" - "AFL_DEBUG_CHILD_OUTPUT: do not suppress stdout/stderr from target\n" + "AFL_DEBUG_CHILD: do not suppress stdout/stderr from target\n" "AFL_DISABLE_TRIM: disable the trimming of test cases\n" "AFL_DUMB_FORKSRV: use fork server without feedback from target\n" "AFL_EXIT_WHEN_DONE: exit when all inputs are run and no new finds are found\n" + "AFL_EXPAND_HAVOC_NOW: immediately enable expand havoc mode (default: after 60 minutes and a cycle without finds)\n" "AFL_FAST_CAL: limit the calibration stage to three cycles for speedup\n" "AFL_FORCE_UI: force showing the status screen (for virtual consoles)\n" + "AFL_FORKSRV_INIT_TMOUT: time spent waiting for forkserver during startup (in milliseconds)\n" "AFL_HANG_TMOUT: override timeout value (in milliseconds)\n" "AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES: don't warn about core dump handlers\n" + "AFL_IGNORE_UNKNOWN_ENVS: don't warn on unknown env vars\n" "AFL_IMPORT_FIRST: sync and import test cases from other fuzzer instances first\n" + "AFL_KILL_SIGNAL: Signal ID delivered to child processes on timeout, etc. (default: SIGKILL)\n" "AFL_MAP_SIZE: the shared memory size for that target. must be >= the size\n" " the target was compiled for\n" + "AFL_MAX_DET_EXTRAS: if more entries are in the dictionary list than this value\n" + " then they are randomly selected instead all of them being\n" + " used. Defaults to 200.\n" "AFL_NO_AFFINITY: do not check for an unused cpu core to use for fuzzing\n" "AFL_NO_ARITH: skip arithmetic mutations in deterministic stage\n" + "AFL_NO_AUTODICT: do not load an offered auto dictionary compiled into a target\n" "AFL_NO_CPU_RED: avoid red color for showing very high cpu usage\n" "AFL_NO_FORKSRV: run target via execve instead of using the forkserver\n" "AFL_NO_SNAPSHOT: do not use the snapshot feature (if the snapshot lkm is loaded)\n" "AFL_NO_UI: switch status screen off\n" + + DYN_COLOR + "AFL_PATH: path to AFL support binaries\n" "AFL_PYTHON_MODULE: mutate and trim inputs with the specified Python module\n" "AFL_QUIET: suppress forkserver status messages\n" "AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n" "AFL_SHUFFLE_QUEUE: reorder the input queue randomly on startup\n" - "AFL_SKIP_BIN_CHECK: skip the check, if the target is an excutable\n" + "AFL_SKIP_BIN_CHECK: skip the check, if the target is an executable\n" "AFL_SKIP_CPUFREQ: do not warn about variable cpu clocking\n" "AFL_SKIP_CRASHES: during initial dry run do not terminate for crashing inputs\n" + "AFL_STATSD: enables StatsD metrics collection\n" + "AFL_STATSD_HOST: change default statsd host (default 127.0.0.1)\n" + "AFL_STATSD_PORT: change default statsd port (default: 8125)\n" + "AFL_STATSD_TAGS_FLAVOR: set statsd tags format (default: disable tags)\n" + " Supported formats are: 'dogstatsd', 'librato',\n" + " 'signalfx' and 'influxdb'\n" + "AFL_TESTCACHE_SIZE: use a cache for testcases, improves performance (in MB)\n" "AFL_TMPDIR: directory to use for input file generation (ramdisk recommended)\n" //"AFL_PERSISTENT: not supported anymore -> no effect, just a warning\n" //"AFL_DEFER_FORKSRV: not supported anymore -> no effect, just a warning\n" @@ -208,7 +255,37 @@ static void usage(afl_state_t *afl, u8 *argv0, int more_help) { SAYF("Compiled without python module support\n"); #endif - SAYF("For additional help please consult %s/README.md\n\n", doc_path); +#ifdef USEMMAP + SAYF("Compiled with shm_open support.\n"); +#else + SAYF("Compiled with shmat support.\n"); +#endif + +#ifdef ASAN_BUILD + SAYF("Compiled with ASAN_BUILD\n\n"); +#endif + +#ifdef NO_SPLICING + SAYF("Compiled with NO_SPLICING\n\n"); +#endif + +#ifdef PROFILING + SAYF("Compiled with PROFILING\n\n"); +#endif + +#ifdef INTROSPECTION + SAYF("Compiled with INTROSPECTION\n\n"); +#endif + +#ifdef _DEBUG + SAYF("Compiled with _DEBUG\n\n"); +#endif + +#ifdef _AFL_DOCUMENT_MUTATIONS + SAYF("Compiled with _AFL_DOCUMENT_MUTATIONS\n\n"); +#endif + + SAYF("For additional help please consult %s/README.md :)\n\n", doc_path); exit(1); #undef PHYTON_SUPPORT @@ -235,16 +312,29 @@ static int stricmp(char const *a, char const *b) { int main(int argc, char **argv_orig, char **envp) { - s32 opt; - u64 prev_queued = 0; - u32 sync_interval_cnt = 0, seek_to, show_help = 0, map_size = MAP_SIZE; - u8 * extras_dir = 0; - u8 mem_limit_given = 0, exit_1 = 0, debug = 0; + s32 opt, i, auto_sync = 0 /*, user_set_cache = 0*/; + u64 prev_queued = 0; + u32 sync_interval_cnt = 0, seek_to = 0, show_help = 0, + map_size = get_map_size(); + u8 *extras_dir[4]; + u8 mem_limit_given = 0, exit_1 = 0, debug = 0, + extras_dir_cnt = 0 /*, have_p = 0*/; char **use_argv; struct timeval tv; struct timezone tz; + #if defined USE_COLOR && defined ALWAYS_COLORED + if (getenv("AFL_NO_COLOR") || getenv("AFL_NO_COLOUR")) { + + WARNF( + "Setting AFL_NO_COLOR has no effect (colors are configured on at " + "compile time)"); + + } + + #endif + char **argv = argv_cpy_dup(argc, argv_orig); afl_state_t *afl = calloc(1, sizeof(afl_state_t)); @@ -252,17 +342,16 @@ int main(int argc, char **argv_orig, char **envp) { if (get_afl_env("AFL_DEBUG")) { debug = afl->debug = 1; } - map_size = get_map_size(); afl_state_init(afl, map_size); afl->debug = debug; afl_fsrv_init(&afl->fsrv); - + if (debug) { afl->fsrv.debug = true; } read_afl_environment(afl, envp); if (afl->shm.map_size) { afl->fsrv.map_size = afl->shm.map_size; } exit_1 = !!afl->afl_env.afl_bench_just_one; SAYF(cCYA "afl-fuzz" VERSION cRST - " based on afl by Michal Zalewski and a big online community\n"); + " based on afl by Michal Zalewski and a large online community\n"); doc_path = access(DOC_PATH, F_OK) != 0 ? (u8 *)"docs" : (u8 *)DOC_PATH; @@ -271,16 +360,34 @@ int main(int argc, char **argv_orig, char **envp) { afl->shmem_testcase_mode = 1; // we always try to perform shmem fuzzing - while ((opt = getopt(argc, argv, - "+c:i:I:o:f:F:m:t:T:dDnCB:S:M:x:QNUWe:p:s:V:E:L:hRP:")) > - 0) { + while ((opt = getopt( + argc, argv, + "+b:B:c:CdDe:E:hi:I:f:F:l:L:m:M:nNo:p:RQs:S:t:T:UV:Wx:Z")) > 0) { switch (opt) { + case 'Z': + afl->old_seed_selection = 1; + break; + case 'I': afl->infoexec = optarg; break; + case 'b': { /* bind CPU core */ + + if (afl->cpu_to_bind != -1) FATAL("Multiple -b options not supported"); + + if (sscanf(optarg, "%d", &afl->cpu_to_bind) < 0) { + + FATAL("Bad syntax used for -b"); + + } + + break; + + } + case 'c': { afl->shm.cmplog_mode = 1; @@ -291,6 +398,7 @@ int main(int argc, char **argv_orig, char **envp) { case 's': { + if (optarg == NULL) { FATAL("No valid seed provided. Got NULL."); } rand_set_seed(afl, strtoul(optarg, 0L, 10)); afl->fixed_seed = 1; break; @@ -327,22 +435,26 @@ int main(int argc, char **argv_orig, char **envp) { afl->schedule = RARE; - } else if (!stricmp(optarg, "seek")) { - - afl->schedule = SEEK; + } else if (!stricmp(optarg, "explore") || !stricmp(optarg, "afl") || - } else if (!stricmp(optarg, "explore") || !stricmp(optarg, "default") || + !stricmp(optarg, "default") || - !stricmp(optarg, "normal") || !stricmp(optarg, "afl")) { + !stricmp(optarg, "normal")) { afl->schedule = EXPLORE; + } else if (!stricmp(optarg, "seek")) { + + afl->schedule = SEEK; + } else { FATAL("Unknown -p power schedule"); } + // have_p = 1; + break; case 'e': @@ -356,6 +468,7 @@ int main(int argc, char **argv_orig, char **envp) { case 'i': /* input dir */ if (afl->in_dir) { FATAL("Multiple -i options not supported"); } + if (optarg == NULL) { FATAL("Invalid -i option (got NULL)."); } afl->in_dir = optarg; if (!strcmp(afl->in_dir, "-")) { afl->in_place_resume = 1; } @@ -372,8 +485,28 @@ int main(int argc, char **argv_orig, char **envp) { u8 *c; + if (afl->non_instrumented_mode) { + + FATAL("-M is not supported in non-instrumented mode"); + + } + if (afl->sync_id) { FATAL("Multiple -S or -M options not supported"); } + + /* sanity check for argument: should not begin with '-' (possible + * option) */ + if (optarg && *optarg == '-') { + + FATAL( + "argument for -M started with a dash '-', which is used for " + "options"); + + } + afl->sync_id = ck_strdup(optarg); + afl->skip_deterministic = 0; // force deterministic fuzzing + afl->old_seed_selection = 1; // force old queue walking seed selection + afl->disable_trim = 1; // disable trimming if ((c = strchr(afl->sync_id, ':'))) { @@ -399,23 +532,57 @@ int main(int argc, char **argv_orig, char **envp) { case 'S': /* secondary sync id */ + if (afl->non_instrumented_mode) { + + FATAL("-S is not supported in non-instrumented mode"); + + } + if (afl->sync_id) { FATAL("Multiple -S or -M options not supported"); } + + /* sanity check for argument: should not begin with '-' (possible + * option) */ + if (optarg && *optarg == '-') { + + FATAL( + "argument for -M started with a dash '-', which is used for " + "options"); + + } + afl->sync_id = ck_strdup(optarg); afl->is_secondary_node = 1; - afl->skip_deterministic = 1; - afl->use_splicing = 1; break; case 'F': /* foreign sync dir */ - if (!afl->is_main_node) + if (!optarg) { FATAL("Missing path for -F"); } + if (!afl->is_main_node) { + FATAL( "Option -F can only be specified after the -M option for the " "main fuzzer of a fuzzing campaign"); - if (afl->foreign_sync_cnt >= FOREIGN_SYNCS_MAX) + + } + + if (afl->foreign_sync_cnt >= FOREIGN_SYNCS_MAX) { + FATAL("Maximum %u entried of -F option can be specified", FOREIGN_SYNCS_MAX); + + } + afl->foreign_syncs[afl->foreign_sync_cnt].dir = optarg; + while (afl->foreign_syncs[afl->foreign_sync_cnt] + .dir[strlen(afl->foreign_syncs[afl->foreign_sync_cnt].dir) - + 1] == '/') { + + afl->foreign_syncs[afl->foreign_sync_cnt] + .dir[strlen(afl->foreign_syncs[afl->foreign_sync_cnt].dir) - 1] = + 0; + + } + afl->foreign_sync_cnt++; break; @@ -428,8 +595,13 @@ int main(int argc, char **argv_orig, char **envp) { case 'x': /* dictionary */ - if (extras_dir) { FATAL("Multiple -x options not supported"); } - extras_dir = optarg; + if (extras_dir_cnt >= 4) { + + FATAL("More than four -x options are not supported"); + + } + + extras_dir[extras_dir_cnt++] = optarg; break; case 't': { /* timeout */ @@ -438,7 +610,8 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->timeout_given) { FATAL("Multiple -t options not supported"); } - if (sscanf(optarg, "%u%c", &afl->fsrv.exec_tmout, &suffix) < 1 || + if (!optarg || + sscanf(optarg, "%u%c", &afl->fsrv.exec_tmout, &suffix) < 1 || optarg[0] == '-') { FATAL("Bad syntax used for -t"); @@ -523,7 +696,6 @@ int main(int argc, char **argv_orig, char **envp) { case 'd': /* skip deterministic */ afl->skip_deterministic = 1; - afl->use_splicing = 1; break; case 'B': /* load bitmap */ @@ -542,7 +714,6 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->in_bitmap) { FATAL("Multiple -B options not supported"); } afl->in_bitmap = optarg; - read_bitmap(afl->in_bitmap, afl->virgin_bits, afl->fsrv.map_size); break; case 'C': /* crash mode */ @@ -553,6 +724,12 @@ int main(int argc, char **argv_orig, char **envp) { case 'n': /* dumb mode */ + if (afl->is_main_node || afl->is_secondary_node) { + + FATAL("Non instrumented mode is not supported with -M / -S"); + + } + if (afl->non_instrumented_mode) { FATAL("Multiple -n options not supported"); @@ -589,7 +766,7 @@ int main(int argc, char **argv_orig, char **envp) { case 'N': /* Unicorn mode */ if (afl->no_unlink) { FATAL("Multiple -N options not supported"); } - afl->no_unlink = 1; + afl->fsrv.no_unlink = (afl->no_unlink = true); break; @@ -615,7 +792,8 @@ int main(int argc, char **argv_orig, char **envp) { case 'V': { afl->most_time_key = 1; - if (sscanf(optarg, "%llu", &afl->most_time) < 1 || optarg[0] == '-') { + if (!optarg || sscanf(optarg, "%llu", &afl->most_time) < 1 || + optarg[0] == '-') { FATAL("Bad syntax used for -V"); @@ -626,7 +804,8 @@ int main(int argc, char **argv_orig, char **envp) { case 'E': { afl->most_execs_key = 1; - if (sscanf(optarg, "%llu", &afl->most_execs) < 1 || optarg[0] == '-') { + if (!optarg || sscanf(optarg, "%llu", &afl->most_execs) < 1 || + optarg[0] == '-') { FATAL("Bad syntax used for -E"); @@ -634,6 +813,49 @@ int main(int argc, char **argv_orig, char **envp) { } break; + case 'l': { + + if (!optarg) { FATAL("missing parameter for 'l'"); } + char *c = optarg; + while (*c) { + + switch (*c) { + + case '0': + case '1': + afl->cmplog_lvl = 1; + break; + case '2': + afl->cmplog_lvl = 2; + break; + case '3': + afl->cmplog_lvl = 3; + break; + case 'a': + case 'A': + afl->cmplog_enable_arith = 1; + break; + case 't': + case 'T': + afl->cmplog_enable_transform = 1; + break; + default: + FATAL("Unknown option value '%c' in -l %s", *c, optarg); + + } + + ++c; + + } + + if (afl->cmplog_lvl == CMPLOG_LVL_MAX) { + + afl->cmplog_max_filesize = MAX_FILE; + + } + + } break; + case 'L': { /* MOpt mode */ if (afl->limit_time_sig) { FATAL("Multiple -L options not supported"); } @@ -662,7 +884,7 @@ int main(int argc, char **argv_orig, char **envp) { u64 limit_time_puppet2 = afl->limit_time_puppet * 60 * 1000; - if (limit_time_puppet2 < afl->limit_time_puppet) { + if ((s32)limit_time_puppet2 < afl->limit_time_puppet) { FATAL("limit_time overflow"); @@ -672,7 +894,7 @@ int main(int argc, char **argv_orig, char **envp) { afl->swarm_now = 0; if (afl->limit_time_puppet == 0) { afl->key_puppet = 1; } - int i; + int j; int tmp_swarm = 0; if (afl->g_now > afl->g_max) { afl->g_now = 0; } @@ -685,70 +907,70 @@ int main(int argc, char **argv_orig, char **envp) { double total_puppet_temp = 0.0; afl->swarm_fitness[tmp_swarm] = 0.0; - for (i = 0; i < operator_num; ++i) { + for (j = 0; j < operator_num; ++j) { - afl->stage_finds_puppet[tmp_swarm][i] = 0; - afl->probability_now[tmp_swarm][i] = 0.0; - afl->x_now[tmp_swarm][i] = + afl->stage_finds_puppet[tmp_swarm][j] = 0; + afl->probability_now[tmp_swarm][j] = 0.0; + afl->x_now[tmp_swarm][j] = ((double)(random() % 7000) * 0.0001 + 0.1); - total_puppet_temp += afl->x_now[tmp_swarm][i]; - afl->v_now[tmp_swarm][i] = 0.1; - afl->L_best[tmp_swarm][i] = 0.5; - afl->G_best[i] = 0.5; - afl->eff_best[tmp_swarm][i] = 0.0; + total_puppet_temp += afl->x_now[tmp_swarm][j]; + afl->v_now[tmp_swarm][j] = 0.1; + afl->L_best[tmp_swarm][j] = 0.5; + afl->G_best[j] = 0.5; + afl->eff_best[tmp_swarm][j] = 0.0; } - for (i = 0; i < operator_num; ++i) { + for (j = 0; j < operator_num; ++j) { - afl->stage_cycles_puppet_v2[tmp_swarm][i] = - afl->stage_cycles_puppet[tmp_swarm][i]; - afl->stage_finds_puppet_v2[tmp_swarm][i] = - afl->stage_finds_puppet[tmp_swarm][i]; - afl->x_now[tmp_swarm][i] = - afl->x_now[tmp_swarm][i] / total_puppet_temp; + afl->stage_cycles_puppet_v2[tmp_swarm][j] = + afl->stage_cycles_puppet[tmp_swarm][j]; + afl->stage_finds_puppet_v2[tmp_swarm][j] = + afl->stage_finds_puppet[tmp_swarm][j]; + afl->x_now[tmp_swarm][j] = + afl->x_now[tmp_swarm][j] / total_puppet_temp; } double x_temp = 0.0; - for (i = 0; i < operator_num; ++i) { + for (j = 0; j < operator_num; ++j) { - afl->probability_now[tmp_swarm][i] = 0.0; - afl->v_now[tmp_swarm][i] = - afl->w_now * afl->v_now[tmp_swarm][i] + + afl->probability_now[tmp_swarm][j] = 0.0; + afl->v_now[tmp_swarm][j] = + afl->w_now * afl->v_now[tmp_swarm][j] + RAND_C * - (afl->L_best[tmp_swarm][i] - afl->x_now[tmp_swarm][i]) + - RAND_C * (afl->G_best[i] - afl->x_now[tmp_swarm][i]); + (afl->L_best[tmp_swarm][j] - afl->x_now[tmp_swarm][j]) + + RAND_C * (afl->G_best[j] - afl->x_now[tmp_swarm][j]); - afl->x_now[tmp_swarm][i] += afl->v_now[tmp_swarm][i]; + afl->x_now[tmp_swarm][j] += afl->v_now[tmp_swarm][j]; - if (afl->x_now[tmp_swarm][i] > v_max) { + if (afl->x_now[tmp_swarm][j] > v_max) { - afl->x_now[tmp_swarm][i] = v_max; + afl->x_now[tmp_swarm][j] = v_max; - } else if (afl->x_now[tmp_swarm][i] < v_min) { + } else if (afl->x_now[tmp_swarm][j] < v_min) { - afl->x_now[tmp_swarm][i] = v_min; + afl->x_now[tmp_swarm][j] = v_min; } - x_temp += afl->x_now[tmp_swarm][i]; + x_temp += afl->x_now[tmp_swarm][j]; } - for (i = 0; i < operator_num; ++i) { + for (j = 0; j < operator_num; ++j) { - afl->x_now[tmp_swarm][i] = afl->x_now[tmp_swarm][i] / x_temp; - if (likely(i != 0)) { + afl->x_now[tmp_swarm][j] = afl->x_now[tmp_swarm][j] / x_temp; + if (likely(j != 0)) { - afl->probability_now[tmp_swarm][i] = - afl->probability_now[tmp_swarm][i - 1] + - afl->x_now[tmp_swarm][i]; + afl->probability_now[tmp_swarm][j] = + afl->probability_now[tmp_swarm][j - 1] + + afl->x_now[tmp_swarm][j]; } else { - afl->probability_now[tmp_swarm][i] = afl->x_now[tmp_swarm][i]; + afl->probability_now[tmp_swarm][j] = afl->x_now[tmp_swarm][j]; } @@ -763,13 +985,13 @@ int main(int argc, char **argv_orig, char **envp) { } - for (i = 0; i < operator_num; ++i) { + for (j = 0; j < operator_num; ++j) { - afl->core_operator_finds_puppet[i] = 0; - afl->core_operator_finds_puppet_v2[i] = 0; - afl->core_operator_cycles_puppet[i] = 0; - afl->core_operator_cycles_puppet_v2[i] = 0; - afl->core_operator_cycles_puppet_v3[i] = 0; + afl->core_operator_finds_puppet[j] = 0; + afl->core_operator_finds_puppet_v2[j] = 0; + afl->core_operator_cycles_puppet[j] = 0; + afl->core_operator_cycles_puppet_v2[j] = 0; + afl->core_operator_cycles_puppet_v3[j] = 0; } @@ -796,20 +1018,18 @@ int main(int argc, char **argv_orig, char **envp) { if (optind == argc || !afl->in_dir || !afl->out_dir || show_help) { - usage(afl, argv[0], show_help); + usage(argv[0], show_help); } - if (!mem_limit_given && afl->shm.cmplog_mode) afl->fsrv.mem_limit += 260; + if (afl->fsrv.mem_limit && afl->shm.cmplog_mode) afl->fsrv.mem_limit += 260; OKF("afl++ is maintained by Marc \"van Hauser\" Heuse, Heiko \"hexcoder\" " "Eißfeldt, Andrea Fioraldi and Dominik Maier"); OKF("afl++ is open source, get it at " "https://github.com/AFLplusplus/AFLplusplus"); - OKF("Power schedules from github.com/mboehme/aflfast"); - OKF("Python Mutator and llvm_mode instrument file list from " - "github.com/choller/afl"); - OKF("MOpt Mutator from github.com/puppet-meteor/MOpt-AFL"); + OKF("NOTE: This is v3.x which changes defaults and behaviours - see " + "README.md"); if (afl->sync_id && afl->is_main_node && afl->afl_env.afl_custom_mutator_only) { @@ -836,11 +1056,23 @@ int main(int argc, char **argv_orig, char **envp) { #endif + afl->fsrv.kill_signal = + parse_afl_kill_signal_env(afl->afl_env.afl_kill_signal, SIGKILL); + setup_signal_handlers(); - check_asan_opts(); + check_asan_opts(afl); afl->power_name = power_names[afl->schedule]; + if (!afl->non_instrumented_mode && !afl->sync_id) { + + auto_sync = 1; + afl->sync_id = ck_strdup("default"); + afl->is_secondary_node = 1; + OKF("No -M/-S set, autoconfiguring for \"-S %s\"", afl->sync_id); + + } + if (afl->sync_id) { fix_up_sync(afl); } if (!strcmp(afl->in_dir, afl->out_dir)) { @@ -865,6 +1097,8 @@ int main(int argc, char **argv_orig, char **envp) { } + if (unlikely(afl->afl_env.afl_statsd)) { statsd_setup_format(afl); } + if (strchr(argv[optind], '/') == NULL && !afl->unicorn_mode) { WARNF(cLRD @@ -903,7 +1137,7 @@ int main(int argc, char **argv_orig, char **envp) { OKF("Using seek power schedule (SEEK)"); break; case EXPLORE: - OKF("Using exploration-based constant power schedule (EXPLORE, default)"); + OKF("Using exploration-based constant power schedule (EXPLORE)"); break; default: FATAL("Unknown power schedule"); @@ -911,6 +1145,15 @@ int main(int argc, char **argv_orig, char **envp) { } + if (afl->shm.cmplog_mode) { OKF("CmpLog level: %u", afl->cmplog_lvl); } + + /* Dynamically allocate memory for AFLFast schedules */ + if (afl->schedule >= FAST && afl->schedule <= RARE) { + + afl->n_fuzz = ck_alloc(N_FUZZ_SIZE * sizeof(u32)); + + } + if (get_afl_env("AFL_NO_FORKSRV")) { afl->no_forkserver = 1; } if (get_afl_env("AFL_NO_CPU_RED")) { afl->no_cpu_meter_red = 1; } if (get_afl_env("AFL_NO_ARITH")) { afl->no_arith = 1; } @@ -931,63 +1174,119 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->afl_env.afl_hang_tmout) { - afl->hang_tmout = atoi(afl->afl_env.afl_hang_tmout); - if (!afl->hang_tmout) { FATAL("Invalid value of AFL_HANG_TMOUT"); } + s32 hang_tmout = atoi(afl->afl_env.afl_hang_tmout); + if (hang_tmout < 1) { FATAL("Invalid value for AFL_HANG_TMOUT"); } + afl->hang_tmout = (u32)hang_tmout; } - if (afl->non_instrumented_mode == 2 && afl->no_forkserver) { + if (afl->afl_env.afl_max_det_extras) { - FATAL("AFL_DUMB_FORKSRV and AFL_NO_FORKSRV are mutually exclusive"); + s32 max_det_extras = atoi(afl->afl_env.afl_max_det_extras); + if (max_det_extras < 1) { FATAL("Invalid value for AFL_MAX_DET_EXTRAS"); } + afl->max_det_extras = (u32)max_det_extras; + + } else { + + afl->max_det_extras = MAX_DET_EXTRAS; } - afl->fsrv.use_fauxsrv = afl->non_instrumented_mode == 1 || afl->no_forkserver; + if (afl->afl_env.afl_testcache_size) { - if (getenv("LD_PRELOAD")) { + afl->q_testcase_max_cache_size = + (u64)atoi(afl->afl_env.afl_testcache_size) * 1048576; - WARNF( - "LD_PRELOAD is set, are you sure that is what to you want to do " - "instead of using AFL_PRELOAD?"); + } + + if (afl->afl_env.afl_testcache_entries) { + + afl->q_testcase_max_cache_entries = + (u32)atoi(afl->afl_env.afl_testcache_entries); + + // user_set_cache = 1; } - if (afl->afl_env.afl_preload) { + if (!afl->afl_env.afl_testcache_size || !afl->afl_env.afl_testcache_entries) { - if (afl->fsrv.qemu_mode) { + afl->afl_env.afl_testcache_entries = 0; + afl->afl_env.afl_testcache_size = 0; - u8 *qemu_preload = getenv("QEMU_SET_ENV"); - u8 *afl_preload = getenv("AFL_PRELOAD"); - u8 *buf; + } - s32 i, afl_preload_size = strlen(afl_preload); - for (i = 0; i < afl_preload_size; ++i) { + if (!afl->q_testcase_max_cache_size) { - if (afl_preload[i] == ',') { + ACTF( + "No testcache was configured. it is recommended to use a testcache, it " + "improves performance: set AFL_TESTCACHE_SIZE=(value in MB)"); - PFATAL( - "Comma (',') is not allowed in AFL_PRELOAD when -Q is " - "specified!"); + } else if (afl->q_testcase_max_cache_size < 2 * MAX_FILE) { - } + FATAL("AFL_TESTCACHE_SIZE must be set to %u or more, or 0 to disable", + (2 * MAX_FILE) % 1048576 == 0 ? (2 * MAX_FILE) / 1048576 + : 1 + ((2 * MAX_FILE) / 1048576)); - } + } else { + + OKF("Enabled testcache with %llu MB", + afl->q_testcase_max_cache_size / 1048576); - if (qemu_preload) { + } - buf = alloc_printf("%s,LD_PRELOAD=%s,DYLD_INSERT_LIBRARIES=%s", - qemu_preload, afl_preload, afl_preload); + if (afl->afl_env.afl_forksrv_init_tmout) { - } else { + afl->fsrv.init_tmout = atoi(afl->afl_env.afl_forksrv_init_tmout); + if (!afl->fsrv.init_tmout) { - buf = alloc_printf("LD_PRELOAD=%s,DYLD_INSERT_LIBRARIES=%s", - afl_preload, afl_preload); + FATAL("Invalid value of AFL_FORKSRV_INIT_TMOUT"); - } + } + + } else { + + afl->fsrv.init_tmout = afl->fsrv.exec_tmout * FORK_WAIT_MULT; + + } + + if (afl->afl_env.afl_crash_exitcode) { + + long exitcode = strtol(afl->afl_env.afl_crash_exitcode, NULL, 10); + if ((!exitcode && (errno == EINVAL || errno == ERANGE)) || + exitcode < -127 || exitcode > 128) { + + FATAL("Invalid crash exitcode, expected -127 to 128, but got %s", + afl->afl_env.afl_crash_exitcode); + + } + + afl->fsrv.uses_crash_exitcode = true; + // WEXITSTATUS is 8 bit unsigned + afl->fsrv.crash_exitcode = (u8)exitcode; + + } + + if (afl->non_instrumented_mode == 2 && afl->no_forkserver) { + + FATAL("AFL_DUMB_FORKSRV and AFL_NO_FORKSRV are mutually exclusive"); - setenv("QEMU_SET_ENV", buf, 1); + } + + afl->fsrv.use_fauxsrv = afl->non_instrumented_mode == 1 || afl->no_forkserver; - ck_free(buf); + if (getenv("LD_PRELOAD")) { + + WARNF( + "LD_PRELOAD is set, are you sure that is what to you want to do " + "instead of using AFL_PRELOAD?"); + + } + + if (afl->afl_env.afl_preload) { + + if (afl->fsrv.qemu_mode) { + + /* afl-qemu-trace takes care of converting AFL_PRELOAD. */ } else { @@ -1042,12 +1341,19 @@ int main(int argc, char **argv_orig, char **envp) { bind_to_free_cpu(afl); #endif /* HAVE_AFFINITY */ - afl->fsrv.trace_bits = - afl_shm_init(&afl->shm, afl->fsrv.map_size, afl->non_instrumented_mode); + #ifdef __HAIKU__ + /* Prioritizes performance over power saving */ + set_scheduler_mode(SCHEDULER_MODE_LOW_LATENCY); + #endif - if (!afl->in_bitmap) { memset(afl->virgin_bits, 255, afl->fsrv.map_size); } - memset(afl->virgin_tmout, 255, afl->fsrv.map_size); - memset(afl->virgin_crash, 255, afl->fsrv.map_size); + #ifdef __APPLE__ + if (pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0) != 0) { + + WARNF("general thread priority settings failed"); + + } + + #endif init_count_class16(); @@ -1056,12 +1362,14 @@ int main(int argc, char **argv_orig, char **envp) { WARNF("it is wasteful to run more than one main node!"); sleep(1); - } + } else if (!auto_sync && afl->is_secondary_node && - if (afl->is_secondary_node && check_main_node_exists(afl) == 0) { + check_main_node_exists(afl) == 0) { - WARNF("no -M main node found. You need to run one main instance!"); - sleep(3); + WARNF( + "no -M main node found. It is recommended to run exactly one main " + "instance."); + sleep(1); } @@ -1073,18 +1381,17 @@ int main(int argc, char **argv_orig, char **envp) { setup_custom_mutators(afl); + write_setup_file(afl, argc, argv); + setup_cmdline_file(afl, argv + optind); - read_testcases(afl); + read_testcases(afl, NULL); // read_foreign_testcases(afl, 1); for the moment dont do this - - load_auto(afl); + OKF("Loaded a total of %u seeds.", afl->queued_paths); pivot_inputs(afl); - if (extras_dir) { load_extras(afl, extras_dir); } - - if (!afl->timeout_given) { find_timeout(afl); } + if (!afl->timeout_given) { find_timeout(afl); } // only for resumes! if ((afl->tmp_dir = afl->afl_env.afl_tmpdir) != NULL && !afl->in_place_resume) { @@ -1122,10 +1429,10 @@ int main(int argc, char **argv_orig, char **envp) { if (!afl->fsrv.out_file) { - u32 i = optind + 1; - while (argv[i]) { + u32 j = optind + 1; + while (argv[j]) { - u8 *aa_loc = strstr(argv[i], "@@"); + u8 *aa_loc = strstr(argv[j], "@@"); if (aa_loc && !afl->fsrv.out_file) { @@ -1148,7 +1455,7 @@ int main(int argc, char **argv_orig, char **envp) { } - ++i; + ++j; } @@ -1164,7 +1471,11 @@ int main(int argc, char **argv_orig, char **envp) { } - if (!afl->fsrv.qemu_mode) { check_binary(afl, afl->cmplog_binary); } + if (!afl->fsrv.qemu_mode && !afl->non_instrumented_mode) { + + check_binary(afl, afl->cmplog_binary); + + } } @@ -1194,7 +1505,74 @@ int main(int argc, char **argv_orig, char **envp) { } + if (afl->non_instrumented_mode || afl->fsrv.qemu_mode || afl->unicorn_mode) { + + map_size = afl->fsrv.map_size = MAP_SIZE; + afl->virgin_bits = ck_realloc(afl->virgin_bits, map_size); + afl->virgin_tmout = ck_realloc(afl->virgin_tmout, map_size); + afl->virgin_crash = ck_realloc(afl->virgin_crash, map_size); + afl->var_bytes = ck_realloc(afl->var_bytes, map_size); + afl->top_rated = ck_realloc(afl->top_rated, map_size * sizeof(void *)); + afl->clean_trace = ck_realloc(afl->clean_trace, map_size); + afl->clean_trace_custom = ck_realloc(afl->clean_trace_custom, map_size); + afl->first_trace = ck_realloc(afl->first_trace, map_size); + afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, map_size); + + } + afl->argv = use_argv; + afl->fsrv.trace_bits = + afl_shm_init(&afl->shm, afl->fsrv.map_size, afl->non_instrumented_mode); + + if (!afl->non_instrumented_mode && !afl->fsrv.qemu_mode && + !afl->unicorn_mode) { + + if (map_size <= DEFAULT_SHMEM_SIZE && !afl->non_instrumented_mode && + !afl->fsrv.qemu_mode && !afl->unicorn_mode) { + + afl->fsrv.map_size = DEFAULT_SHMEM_SIZE; // dummy temporary value + char vbuf[16]; + snprintf(vbuf, sizeof(vbuf), "%u", DEFAULT_SHMEM_SIZE); + setenv("AFL_MAP_SIZE", vbuf, 1); + + } + + u32 new_map_size = afl_fsrv_get_mapsize( + &afl->fsrv, afl->argv, &afl->stop_soon, afl->afl_env.afl_debug_child); + + // only reinitialize when it makes sense + if ((map_size < new_map_size /*|| + (new_map_size != MAP_SIZE && new_map_size < map_size && + map_size - new_map_size > MAP_SIZE)*/)) { + + OKF("Re-initializing maps to %u bytes", new_map_size); + + afl->virgin_bits = ck_realloc(afl->virgin_bits, new_map_size); + afl->virgin_tmout = ck_realloc(afl->virgin_tmout, new_map_size); + afl->virgin_crash = ck_realloc(afl->virgin_crash, new_map_size); + afl->var_bytes = ck_realloc(afl->var_bytes, new_map_size); + afl->top_rated = + ck_realloc(afl->top_rated, new_map_size * sizeof(void *)); + afl->clean_trace = ck_realloc(afl->clean_trace, new_map_size); + afl->clean_trace_custom = + ck_realloc(afl->clean_trace_custom, new_map_size); + afl->first_trace = ck_realloc(afl->first_trace, new_map_size); + afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, new_map_size); + + afl_fsrv_kill(&afl->fsrv); + afl_shm_deinit(&afl->shm); + afl->fsrv.map_size = new_map_size; + afl->fsrv.trace_bits = + afl_shm_init(&afl->shm, new_map_size, afl->non_instrumented_mode); + setenv("AFL_NO_AUTODICT", "1", 1); // loaded already + afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon, + afl->afl_env.afl_debug_child); + + map_size = new_map_size; + + } + + } if (afl->cmplog_binary) { @@ -1205,22 +1583,157 @@ int main(int argc, char **argv_orig, char **envp) { afl->cmplog_fsrv.qemu_mode = afl->fsrv.qemu_mode; afl->cmplog_fsrv.cmplog_binary = afl->cmplog_binary; afl->cmplog_fsrv.init_child_func = cmplog_exec_child; - afl_fsrv_start(&afl->cmplog_fsrv, afl->argv, &afl->stop_soon, - afl->afl_env.afl_debug_child_output); + + if ((map_size <= DEFAULT_SHMEM_SIZE || + afl->cmplog_fsrv.map_size < map_size) && + !afl->non_instrumented_mode && !afl->fsrv.qemu_mode && + !afl->unicorn_mode) { + + afl->cmplog_fsrv.map_size = MAX(map_size, (u32)DEFAULT_SHMEM_SIZE); + char vbuf[16]; + snprintf(vbuf, sizeof(vbuf), "%u", afl->cmplog_fsrv.map_size); + setenv("AFL_MAP_SIZE", vbuf, 1); + + } + + u32 new_map_size = + afl_fsrv_get_mapsize(&afl->cmplog_fsrv, afl->argv, &afl->stop_soon, + afl->afl_env.afl_debug_child); + + // only reinitialize when it needs to be larger + if (map_size < new_map_size) { + + OKF("Re-initializing maps to %u bytes due cmplog", new_map_size); + + afl->virgin_bits = ck_realloc(afl->virgin_bits, new_map_size); + afl->virgin_tmout = ck_realloc(afl->virgin_tmout, new_map_size); + afl->virgin_crash = ck_realloc(afl->virgin_crash, new_map_size); + afl->var_bytes = ck_realloc(afl->var_bytes, new_map_size); + afl->top_rated = + ck_realloc(afl->top_rated, new_map_size * sizeof(void *)); + afl->clean_trace = ck_realloc(afl->clean_trace, new_map_size); + afl->clean_trace_custom = + ck_realloc(afl->clean_trace_custom, new_map_size); + afl->first_trace = ck_realloc(afl->first_trace, new_map_size); + afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, new_map_size); + + afl_fsrv_kill(&afl->fsrv); + afl_fsrv_kill(&afl->cmplog_fsrv); + afl_shm_deinit(&afl->shm); + + afl->cmplog_fsrv.map_size = new_map_size; // non-cmplog stays the same + map_size = new_map_size; + + setenv("AFL_NO_AUTODICT", "1", 1); // loaded already + afl->fsrv.trace_bits = + afl_shm_init(&afl->shm, new_map_size, afl->non_instrumented_mode); + afl->cmplog_fsrv.trace_bits = afl->fsrv.trace_bits; + afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon, + afl->afl_env.afl_debug_child); + afl_fsrv_start(&afl->cmplog_fsrv, afl->argv, &afl->stop_soon, + afl->afl_env.afl_debug_child); + + } + OKF("Cmplog forkserver successfully started"); } + if (afl->debug) { + + printf("NORMAL %u, CMPLOG %u\n", afl->fsrv.map_size, + afl->cmplog_fsrv.map_size); + fprintf(stderr, "NORMAL %u, CMPLOG %u\n", afl->fsrv.map_size, + afl->cmplog_fsrv.map_size); + + } + + load_auto(afl); + + if (extras_dir_cnt) { + + for (i = 0; i < extras_dir_cnt; i++) { + + load_extras(afl, extras_dir[i]); + + } + + } + + deunicode_extras(afl); + dedup_extras(afl); + if (afl->extras_cnt) { OKF("Loaded a total of %u extras.", afl->extras_cnt); } + + // after we have the correct bitmap size we can read the bitmap -B option + // and set the virgin maps + if (afl->in_bitmap) { + + read_bitmap(afl->in_bitmap, afl->virgin_bits, afl->fsrv.map_size); + + } else { + + memset(afl->virgin_bits, 255, map_size); + + } + + memset(afl->virgin_tmout, 255, map_size); + memset(afl->virgin_crash, 255, map_size); + perform_dry_run(afl); + if (afl->q_testcase_max_cache_entries) { + + afl->q_testcase_cache = + ck_alloc(afl->q_testcase_max_cache_entries * sizeof(size_t)); + if (!afl->q_testcase_cache) { PFATAL("malloc failed for cache entries"); } + + } + cull_queue(afl); + // ensure we have at least one seed that is not disabled. + u32 entry, valid_seeds = 0; + for (entry = 0; entry < afl->queued_paths; ++entry) + if (!afl->queue_buf[entry]->disabled) { ++valid_seeds; } + + if (!afl->pending_not_fuzzed || !valid_seeds) { + + FATAL("We need at least one valid input seed that does not crash!"); + + } + + if (afl->timeout_given == 2) { // -t ...+ option + + if (valid_seeds == 1) { + + WARNF( + "Only one valid seed is present, auto-calculating the timeout is " + "disabled!"); + afl->timeout_given = 1; + + } else { + + u64 max_ms = 0; + + for (entry = 0; entry < afl->queued_paths; ++entry) + if (!afl->queue_buf[entry]->disabled) + if (afl->queue_buf[entry]->exec_us > max_ms) + max_ms = afl->queue_buf[entry]->exec_us; + + afl->fsrv.exec_tmout = max_ms; + + } + + } + show_init_stats(afl); - seek_to = find_start_position(afl); + if (unlikely(afl->old_seed_selection)) seek_to = find_start_position(afl); - write_stats_file(afl, 0, 0, 0); - maybe_update_plot_file(afl, 0, 0); + afl->start_time = get_cur_time(); + if (afl->in_place_resume || afl->afl_env.afl_autoresume) load_stats_file(afl); + write_stats_file(afl, 0, 0, 0, 0); + maybe_update_plot_file(afl, 0, 0, 0); save_auto(afl); if (afl->stop_soon) { goto stop_fuzzing; } @@ -1229,8 +1742,7 @@ int main(int argc, char **argv_orig, char **envp) { if (!afl->not_on_tty) { - sleep(4); - afl->start_time += 4000; + sleep(1); if (afl->stop_soon) { goto stop_fuzzing; } } @@ -1239,28 +1751,58 @@ int main(int argc, char **argv_orig, char **envp) { // real start time, we reset, so this works correctly with -V afl->start_time = get_cur_time(); - while (1) { + u32 runs_in_current_cycle = (u32)-1; + u32 prev_queued_paths = 0; + u8 skipped_fuzz; - u8 skipped_fuzz; + #ifdef INTROSPECTION + char ifn[4096]; + snprintf(ifn, sizeof(ifn), "%s/introspection.txt", afl->out_dir); + if ((afl->introspection_file = fopen(ifn, "w")) == NULL) { + + PFATAL("could not create '%s'", ifn); + + } + + setvbuf(afl->introspection_file, NULL, _IONBF, 0); + OKF("Writing mutation introspection to '%s'", ifn); + #endif + + while (likely(!afl->stop_soon)) { cull_queue(afl); - if (!afl->queue_cur) { + if (unlikely((!afl->old_seed_selection && + runs_in_current_cycle > afl->queued_paths) || + (afl->old_seed_selection && !afl->queue_cur))) { ++afl->queue_cycle; - afl->current_entry = 0; + runs_in_current_cycle = (u32)-1; afl->cur_skipped_paths = 0; - afl->queue_cur = afl->queue; - while (seek_to) { + if (unlikely(afl->old_seed_selection)) { - ++afl->current_entry; - --seek_to; - afl->queue_cur = afl->queue_cur->next; + afl->current_entry = 0; + while (unlikely(afl->current_entry < afl->queued_paths && + afl->queue_buf[afl->current_entry]->disabled)) { - } + ++afl->current_entry; + + } + + if (afl->current_entry >= afl->queued_paths) { afl->current_entry = 0; } + + afl->queue_cur = afl->queue_buf[afl->current_entry]; - // show_stats(afl); + if (unlikely(seek_to)) { + + afl->current_entry = seek_to; + afl->queue_cur = afl->queue_buf[seek_to]; + seek_to = 0; + + } + + } if (unlikely(afl->not_on_tty)) { @@ -1272,46 +1814,72 @@ int main(int argc, char **argv_orig, char **envp) { /* If we had a full queue cycle with no new finds, try recombination strategies next. */ - if (afl->queued_paths == prev_queued && - (get_cur_time() - afl->start_time) >= 3600) { + if (unlikely(afl->queued_paths == prev_queued && + (get_cur_time() - afl->start_time) >= 3600)) { if (afl->use_splicing) { ++afl->cycles_wo_finds; + + if (unlikely(afl->shm.cmplog_mode && + afl->cmplog_max_filesize < MAX_FILE)) { + + afl->cmplog_max_filesize <<= 4; + + } + switch (afl->expand_havoc) { case 0: + // this adds extra splicing mutation options to havoc mode afl->expand_havoc = 1; break; case 1: - if (afl->limit_time_sig == 0) { + // add MOpt mutator + /* + if (afl->limit_time_sig == 0 && !afl->custom_only && + !afl->python_only) { afl->limit_time_sig = -1; afl->limit_time_puppet = 0; } + */ afl->expand_havoc = 2; + if (afl->cmplog_lvl && afl->cmplog_lvl < 2) afl->cmplog_lvl = 2; break; case 2: - // afl->cycle_schedules = 1; + // increase havoc mutations per fuzz attempt + afl->havoc_stack_pow2++; afl->expand_havoc = 3; break; case 3: + // further increase havoc mutations per fuzz attempt + afl->havoc_stack_pow2++; + afl->expand_havoc = 4; + break; + case 4: + afl->expand_havoc = 5; + if (afl->cmplog_lvl && afl->cmplog_lvl < 3) afl->cmplog_lvl = 3; + break; + case 5: + // if not in sync mode, enable deterministic mode? + // if (!afl->sync_id) afl->skip_deterministic = 0; + afl->expand_havoc = 6; + case 6: // nothing else currently break; } - if (afl->expand_havoc) { - - } else - - afl->expand_havoc = 1; - } else { + #ifndef NO_SPLICING afl->use_splicing = 1; + #else + afl->use_splicing = 0; + #endif } @@ -1357,12 +1925,14 @@ int main(int argc, char **argv_orig, char **envp) { } - struct queue_entry *q = afl->queue; // we must recalculate the scores of all queue entries - while (q) { + for (i = 0; i < (s32)afl->queued_paths; i++) { - update_bitmap_score(afl, q); - q = q->next; + if (likely(!afl->queue_buf[i]->disabled)) { + + update_bitmap_score(afl, afl->queue_buf[i]); + + } } @@ -1379,38 +1949,89 @@ int main(int argc, char **argv_orig, char **envp) { } - skipped_fuzz = fuzz_one(afl); + ++runs_in_current_cycle; - if (!skipped_fuzz && !afl->stop_soon && afl->sync_id) { + do { - if (unlikely(afl->is_main_node)) { + if (likely(!afl->old_seed_selection)) { - if (!(sync_interval_cnt++ % (SYNC_INTERVAL / 3))) { sync_fuzzers(afl); } + if (unlikely(prev_queued_paths < afl->queued_paths)) { - } else { + // we have new queue entries since the last run, recreate alias table + prev_queued_paths = afl->queued_paths; + create_alias_table(afl); - if (!(sync_interval_cnt++ % SYNC_INTERVAL)) { sync_fuzzers(afl); } + } + + afl->current_entry = select_next_queue_entry(afl); + afl->queue_cur = afl->queue_buf[afl->current_entry]; } - } + skipped_fuzz = fuzz_one(afl); + + if (unlikely(!afl->stop_soon && exit_1)) { afl->stop_soon = 2; } + + if (unlikely(afl->old_seed_selection)) { + + while (++afl->current_entry < afl->queued_paths && + afl->queue_buf[afl->current_entry]->disabled) + ; + if (unlikely(afl->current_entry >= afl->queued_paths || + afl->queue_buf[afl->current_entry] == NULL || + afl->queue_buf[afl->current_entry]->disabled)) + afl->queue_cur = NULL; + else + afl->queue_cur = afl->queue_buf[afl->current_entry]; + + } + + } while (skipped_fuzz && afl->queue_cur && !afl->stop_soon); + + if (likely(!afl->stop_soon && afl->sync_id)) { + + if (likely(afl->skip_deterministic)) { - if (!afl->stop_soon && exit_1) { afl->stop_soon = 2; } + if (unlikely(afl->is_main_node)) { - if (afl->stop_soon) { break; } + if (unlikely(get_cur_time() > + (SYNC_TIME >> 1) + afl->last_sync_time)) { - afl->queue_cur = afl->queue_cur->next; - ++afl->current_entry; + if (!(sync_interval_cnt++ % (SYNC_INTERVAL / 3))) { + + sync_fuzzers(afl); + + } + + } + + } else { + + if (unlikely(get_cur_time() > SYNC_TIME + afl->last_sync_time)) { + + if (!(sync_interval_cnt++ % SYNC_INTERVAL)) { sync_fuzzers(afl); } + + } + + } + + } else { + + sync_fuzzers(afl); + + } + + } } write_bitmap(afl); - maybe_update_plot_file(afl, 0, 0); + maybe_update_plot_file(afl, 0, 0, 0); save_auto(afl); stop_fuzzing: - write_stats_file(afl, 0, 0, 0); + write_stats_file(afl, 0, 0, 0, 0); afl->force_ui_update = 1; // ensure the screen is reprinted show_stats(afl); // print the screen one last time @@ -1477,6 +2098,7 @@ stop_fuzzing: ck_free(afl->fsrv.target_path); ck_free(afl->fsrv.out_file); ck_free(afl->sync_id); + if (afl->q_testcase_cache) { ck_free(afl->q_testcase_cache); } afl_state_deinit(afl); free(afl); /* not tracked */ diff --git a/src/afl-gcc.c b/src/afl-gcc.c deleted file mode 100644 index 22e6be8e..00000000 --- a/src/afl-gcc.c +++ /dev/null @@ -1,488 +0,0 @@ -/* - american fuzzy lop++ - wrapper for GCC and clang - ------------------------------------------------ - - Originally written by Michal Zalewski - - Now maintained by Marc Heuse <mh@mh-sec.de>, - Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and - Andrea Fioraldi <andreafioraldi@gmail.com> - - Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2020 AFLplusplus Project. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - 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 <stdio.h> -#include <unistd.h> -#include <stdlib.h> -#include <string.h> - -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(WORD_SIZE_64) - u8 m32_set = 0; -#endif - - cc_params = ck_alloc((argc + 128) * sizeof(u8 *)); - - name = strrchr(argv[0], '/'); - if (!name) { - - name = argv[0]; - - /* This should never happen but fixes a scan-build warning */ - if (!name) { FATAL("Empty argv set"); } - - } 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 ? alt_cxx : (u8 *)"clang++"; - - } else if (!strcmp(name, "afl-clang")) { - - u8 *alt_cc = getenv("AFL_CC"); - cc_params[0] = alt_cc && *alt_cc ? alt_cc : (u8 *)"clang"; - - } else { - - fprintf(stderr, "Name of the binary: %s\n", argv[0]); - FATAL("Name of the binary is not a known name, expected afl-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 if (!strcmp(name, "afl-gcc")) { - - cc_params[0] = getenv("AFL_CC"); - - } else { - - fprintf(stderr, "Name of the binary: %s\n", argv[0]); - FATAL("Name of the binary is not a known name, expected afl-gcc/g++/gcj"); - - } - - 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 ? alt_cxx : (u8 *)"g++"; - - } else if (!strcmp(name, "afl-gcj")) { - - u8 *alt_cc = getenv("AFL_GCJ"); - cc_params[0] = alt_cc && *alt_cc ? alt_cc : (u8 *)"gcj"; - - } else if (!strcmp(name, "afl-gcc")) { - - u8 *alt_cc = getenv("AFL_CC"); - cc_params[0] = alt_cc && *alt_cc ? alt_cc : (u8 *)"gcc"; - - } else { - - fprintf(stderr, "Name of the binary: %s\n", argv[0]); - FATAL("Name of the binary is not a known name, expected afl-gcc/g++/gcj"); - - } - -#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(WORD_SIZE_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_USE_UBSAN")) { - - cc_params[cc_par_cnt++] = "-fsanitize=undefined"; - cc_params[cc_par_cnt++] = "-fsanitize-undefined-trap-on-error"; - cc_params[cc_par_cnt++] = "-fno-sanitize-recover=all"; - - } - -#if defined(USEMMAP) && !defined(__HAIKU__) - cc_params[cc_par_cnt++] = "-lrt"; -#endif - - if (!getenv("AFL_DONT_OPTIMIZE")) { - -#if defined(__FreeBSD__) && defined(WORD_SIZE_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-bcmp"; - 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) { - - char *env_info = - "Environment variables used by afl-gcc:\n" - "AFL_CC: path to the C compiler to use\n" - "AFL_CXX: path to the C++ compiler to use\n" - "AFL_GCJ: path to the java compiler to use\n" - "AFL_PATH: path to the instrumenting assembler\n" - "AFL_DONT_OPTIMIZE: disable optimization instead of -O3\n" - "AFL_NO_BUILTIN: compile for use with libtokencap.so\n" - "AFL_QUIET: suppress verbose output\n" - "AFL_CAL_FAST: speed up the initial calibration\n" - "AFL_HARDEN: adds code hardening to catch memory bugs\n" - "AFL_USE_ASAN: activate address sanitizer\n" - "AFL_USE_MSAN: activate memory sanitizer\n" - "AFL_USE_UBSAN: activate undefined behaviour sanitizer\n" - - "\nEnvironment variables used by afl-as (called by afl-gcc):\n" - "AFL_AS: path to the assembler to use\n" - "TMPDIR: set the directory for temporary files of afl-as\n" - "TEMP: fall back path to directory for temporary files\n" - "TMP: fall back path to directory for temporary files\n" - "AFL_INST_RATIO: percentage of branches to instrument\n" - "AFL_QUIET: suppress verbose output\n" - "AFL_KEEP_ASSEMBLY: leave instrumented assembly files\n" - "AFL_AS_FORCE_INSTRUMENT: force instrumentation for asm sources\n"; - - if (argc == 2 && strcmp(argv[1], "-h") == 0) { - - printf("afl-cc" VERSION " by Michal Zalewski\n\n"); - printf("%s \n\n", argv[0]); - printf("afl-gcc has no command line options\n\n%s\n", env_info); - printf( - "NOTE: afl-gcc is deprecated, llvm_mode is much faster and has more " - "options\n"); - return -1; - - } - - if ((isatty(2) && !getenv("AFL_QUIET")) || getenv("AFL_DEBUG") != NULL) { - - SAYF(cCYA "afl-cc" VERSION cRST " by Michal Zalewski\n"); - SAYF(cYEL "[!] " cBRI "NOTE: " cRST - "afl-gcc is deprecated, llvm_mode is much faster and has more " - "options\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%s" - - , - BIN_PATH, BIN_PATH, env_info); - - exit(1); - - } - - u8 *ptr; - if (!be_quiet && - ((ptr = getenv("AFL_MAP_SIZE")) || (ptr = getenv("AFL_MAPSIZE")))) { - - u32 map_size = atoi(ptr); - if (map_size != MAP_SIZE) { - - WARNF("AFL_MAP_SIZE is not supported by afl-gcc"); - - } - - } - - 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/src/afl-gotcpu.c b/src/afl-gotcpu.c index bd0f7de6..ac002a93 100644 --- a/src/afl-gotcpu.c +++ b/src/afl-gotcpu.c @@ -35,9 +35,6 @@ #define _GNU_SOURCE #endif -#ifdef __ANDROID__ - #include "android-ashmem.h" -#endif #include <stdio.h> #include <stdlib.h> #include <unistd.h> @@ -65,7 +62,6 @@ #define cpu_set_t cpuset_t #elif defined(__NetBSD__) #include <pthread.h> - #include <sched.h> #elif defined(__APPLE__) #include <pthread.h> #include <mach/thread_act.h> diff --git a/src/afl-ld-lto.c b/src/afl-ld-lto.c new file mode 100644 index 00000000..0a978653 --- /dev/null +++ b/src/afl-ld-lto.c @@ -0,0 +1,368 @@ +/* + american fuzzy lop++ - wrapper for llvm 11+ lld + ----------------------------------------------- + + Written by Marc Heuse <mh@mh-sec.de> for afl++ + + Maintained by Marc Heuse <mh@mh-sec.de>, + Heiko Eißfeldt <heiko.eissfeldt@hexco.de> + Andrea Fioraldi <andreafioraldi@gmail.com> + Dominik Maier <domenukk@gmail.com> + + Copyright 2019-2020 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + The sole purpose of this wrapper is to preprocess clang LTO files when + linking with lld and performing the instrumentation on the whole program. + +*/ + +#define AFL_MAIN +#define _GNU_SOURCE + +#include "config.h" +#include "types.h" +#include "debug.h" +#include "alloc-inl.h" + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <ctype.h> +#include <fcntl.h> + +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/time.h> + +#include <dirent.h> + +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || \ + defined(__DragonFly__) + #include <limits.h> +#endif + +#ifdef __APPLE__ + #include <sys/syslimits.h> +#endif + +#define MAX_PARAM_COUNT 4096 + +static u8 **ld_params; /* Parameters passed to the real 'ld' */ + +static u8 *afl_path = AFL_PATH; +static u8 *real_ld = AFL_REAL_LD; + +static u8 be_quiet, /* Quiet mode (no stderr output) */ + debug, /* AFL_DEBUG */ + passthrough, /* AFL_LD_PASSTHROUGH - no link+optimize*/ + just_version; /* Just show version? */ + +static u32 ld_param_cnt = 1; /* Number of params to 'ld' */ + +/* Examine and modify parameters to pass to 'ld', 'llvm-link' and 'llmv-ar'. + 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) { + + u32 i, instrim = 0, gold_pos = 0, gold_present = 0, rt_present = 0, + rt_lto_present = 0, inst_present = 0; + char *ptr; + + ld_params = ck_alloc(4096 * sizeof(u8 *)); + + ld_params[0] = (u8 *)real_ld; + + if (!passthrough) { + + for (i = 1; i < (u32)argc; i++) { + + if (strstr(argv[i], "/afl-llvm-rt-lto.o") != NULL) rt_lto_present = 1; + if (strstr(argv[i], "/afl-llvm-rt.o") != NULL) rt_present = 1; + if (strstr(argv[i], "/afl-llvm-lto-instr") != NULL) inst_present = 1; + + } + + for (i = 1; i < (u32)argc && !gold_pos; i++) { + + if (strcmp(argv[i], "-plugin") == 0) { + + if (strncmp(argv[i], "-plugin=", strlen("-plugin=")) == 0) { + + if (strcasestr(argv[i], "LLVMgold.so") != NULL) + gold_present = gold_pos = i + 1; + + } else if (i < (u32)argc && + + strcasestr(argv[i + 1], "LLVMgold.so") != NULL) { + + gold_present = gold_pos = i + 2; + + } + + } + + } + + if (!gold_pos) { + + for (i = 1; i + 1 < (u32)argc && !gold_pos; i++) { + + if (argv[i][0] != '-') { + + if (argv[i - 1][0] == '-') { + + switch (argv[i - 1][1]) { + + case 'b': + break; + case 'd': + break; + case 'e': + break; + case 'F': + break; + case 'f': + break; + case 'I': + break; + case 'l': + break; + case 'L': + break; + case 'm': + break; + case 'o': + break; + case 'O': + break; + case 'p': + if (index(argv[i - 1], '=') == NULL) gold_pos = i; + break; + case 'R': + break; + case 'T': + break; + case 'u': + break; + case 'y': + break; + case 'z': + break; + case '-': { + + if (strcmp(argv[i - 1], "--oformat") == 0) break; + if (strcmp(argv[i - 1], "--output") == 0) break; + if (strncmp(argv[i - 1], "--opt-remarks-", 14) == 0) break; + gold_pos = i; + break; + + } + + default: + gold_pos = i; + + } + + } else + + gold_pos = i; + + } + + } + + } + + if (!gold_pos) gold_pos = 1; + + } + + if (getenv("AFL_LLVM_INSTRIM")) + instrim = 1; + else if ((ptr = getenv("AFL_LLVM_INSTRUMENT")) && + (strcasestr(ptr, "CFG") == 0 || strcasestr(ptr, "INSTRIM") == 0)) + instrim = 1; + + if (debug) + DEBUGF( + "passthrough=%s instrim=%u, gold_pos=%u, gold_present=%s " + "inst_present=%s rt_present=%s rt_lto_present=%s\n", + passthrough ? "true" : "false", instrim, gold_pos, + gold_present ? "true" : "false", inst_present ? "true" : "false", + rt_present ? "true" : "false", rt_lto_present ? "true" : "false"); + + for (i = 1; i < (u32)argc; i++) { + + if (ld_param_cnt >= MAX_PARAM_COUNT) + FATAL( + "Too many command line parameters because of unpacking .a archives, " + "this would need to be done by hand ... sorry! :-("); + + if (strcmp(argv[i], "--afl") == 0) { + + if (!be_quiet) OKF("afl++ test command line flag detected, exiting."); + exit(0); + + } + + if (i == gold_pos && !passthrough) { + + ld_params[ld_param_cnt++] = alloc_printf("-L%s/../lib", LLVM_BINDIR); + + if (!gold_present) { + + ld_params[ld_param_cnt++] = "-plugin"; + ld_params[ld_param_cnt++] = + alloc_printf("%s/../lib/LLVMgold.so", LLVM_BINDIR); + + } + + ld_params[ld_param_cnt++] = "--allow-multiple-definition"; + + if (!inst_present) { + + if (instrim) + ld_params[ld_param_cnt++] = + alloc_printf("-mllvm=-load=%s/afl-llvm-lto-instrim.so", afl_path); + else + ld_params[ld_param_cnt++] = alloc_printf( + "-mllvm=-load=%s/afl-llvm-lto-instrumentation.so", afl_path); + + } + + if (!rt_present) + ld_params[ld_param_cnt++] = alloc_printf("%s/afl-llvm-rt.o", afl_path); + if (!rt_lto_present) + ld_params[ld_param_cnt++] = + alloc_printf("%s/afl-llvm-rt-lto.o", afl_path); + + } + + ld_params[ld_param_cnt++] = argv[i]; + + } + + ld_params[ld_param_cnt] = NULL; + +} + +/* Main entry point */ + +int main(int argc, char **argv) { + + s32 pid, i, status; + char thecwd[PATH_MAX]; + + if (getenv("AFL_LD_CALLER") != NULL) { + + FATAL("ld loop detected! Set AFL_REAL_LD!\n"); + + } + + if (isatty(2) && !getenv("AFL_QUIET") && !getenv("AFL_DEBUG")) { + + SAYF(cCYA "afl-ld-to" VERSION cRST + " by Marc \"vanHauser\" Heuse <mh@mh-sec.de>\n"); + + } else + + be_quiet = 1; + + if (getenv("AFL_DEBUG") != NULL) debug = 1; + if (getenv("AFL_PATH") != NULL) afl_path = getenv("AFL_PATH"); + if (getenv("AFL_LD_PASSTHROUGH") != NULL) passthrough = 1; + if (getenv("AFL_REAL_LD") != NULL) real_ld = getenv("AFL_REAL_LD"); + + if (!afl_path || !*afl_path) afl_path = "/usr/local/lib/afl"; + + setenv("AFL_LD_CALLER", "1", 1); + + if (debug) { + + if (getcwd(thecwd, sizeof(thecwd)) != 0) strcpy(thecwd, "."); + + DEBUGF("cd \"%s\";", thecwd); + for (i = 0; i < argc; i++) + SAYF(" \"%s\"", argv[i]); + SAYF("\n"); + + } + + if (argc < 2) { + + SAYF( + "\n" + "This is a helper application for afl-clang-lto. It is a wrapper " + "around GNU " + "llvm's 'lld',\n" + "executed by the toolchain whenever using " + "afl-clang-lto/afl-clang-lto++.\n" + "You probably don't want to run this program directly but rather pass " + "it as LD parameter to configure scripts\n\n" + + "Environment variables:\n" + " AFL_LD_PASSTHROUGH do not link+optimize == no instrumentation\n" + " AFL_REAL_LD point to the real llvm 11 lld if necessary\n" + + "\nafl-ld-to was compiled with the fixed real 'ld' of %s and the " + "binary path of %s\n\n", + real_ld, LLVM_BINDIR); + + exit(1); + + } + + edit_params(argc, argv); // here most of the magic happens :-) + + if (debug) { + + DEBUGF("cd \"%s\";", thecwd); + for (i = 0; i < (s32)ld_param_cnt; i++) + SAYF(" \"%s\"", ld_params[i]); + SAYF("\n"); + + } + + if (!(pid = fork())) { + + if (strlen(real_ld) > 1) execvp(real_ld, (char **)ld_params); + execvp("ld", (char **)ld_params); // fallback + FATAL("Oops, failed to execute 'ld' - check your PATH"); + + } + + if (pid < 0) PFATAL("fork() failed"); + + if (waitpid(pid, &status, 0) <= 0) PFATAL("waitpid() failed"); + if (debug) DEBUGF("linker result: %d\n", status); + + if (!just_version) { + + if (status == 0) { + + if (!be_quiet) OKF("Linker was successful"); + + } else { + + SAYF(cLRD "[-] " cRST + "Linker failed, please investigate and send a bug report. Most " + "likely an 'ld' option is incompatible with %s.\n", + AFL_CLANG_FLTO); + + } + + } + + exit(WEXITSTATUS(status)); + +} + diff --git a/src/afl-performance.c b/src/afl-performance.c index 0c1697a8..89b170eb 100644 --- a/src/afl-performance.c +++ b/src/afl-performance.c @@ -22,16 +22,10 @@ #include <stdint.h> #include "afl-fuzz.h" #include "types.h" -#include "xxh3.h" -/* we use xoshiro256** instead of rand/random because it is 10x faster and has - better randomness properties. */ - -static inline uint64_t rotl(const uint64_t x, int k) { - - return (x << k) | (x >> (64 - k)); - -} +#define XXH_INLINE_ALL +#include "xxhash.h" +#undef XXH_INLINE_ALL void rand_set_seed(afl_state_t *afl, s64 init_seed) { @@ -39,102 +33,49 @@ void rand_set_seed(afl_state_t *afl, s64 init_seed) { afl->rand_seed[0] = hash64((u8 *)&afl->init_seed, sizeof(afl->init_seed), HASH_CONST); afl->rand_seed[1] = afl->rand_seed[0] ^ 0x1234567890abcdef; - afl->rand_seed[2] = afl->rand_seed[0] & 0x0123456789abcdef; - afl->rand_seed[3] = afl->rand_seed[0] | 0x01abcde43f567908; + afl->rand_seed[2] = (afl->rand_seed[0] & 0x1234567890abcdef) ^ + (afl->rand_seed[1] | 0xfedcba9876543210); } -uint64_t rand_next(afl_state_t *afl) { - - const uint64_t result = - rotl(afl->rand_seed[0] + afl->rand_seed[3], 23) + afl->rand_seed[0]; - - const uint64_t t = afl->rand_seed[1] << 17; +#define ROTL(d, lrot) ((d << (lrot)) | (d >> (8 * sizeof(d) - (lrot)))) - afl->rand_seed[2] ^= afl->rand_seed[0]; - afl->rand_seed[3] ^= afl->rand_seed[1]; - afl->rand_seed[1] ^= afl->rand_seed[2]; - afl->rand_seed[0] ^= afl->rand_seed[3]; +#ifdef WORD_SIZE_64 +// romuDuoJr +inline AFL_RAND_RETURN rand_next(afl_state_t *afl) { - afl->rand_seed[2] ^= t; - - afl->rand_seed[3] = rotl(afl->rand_seed[3], 45); - - return result; + AFL_RAND_RETURN xp = afl->rand_seed[0]; + afl->rand_seed[0] = 15241094284759029579u * afl->rand_seed[1]; + afl->rand_seed[1] = afl->rand_seed[1] - xp; + afl->rand_seed[1] = ROTL(afl->rand_seed[1], 27); + return xp; } -/* This is the jump function for the generator. It is equivalent - to 2^128 calls to rand_next(); it can be used to generate 2^128 - non-overlapping subsequences for parallel computations. */ - -void jump(afl_state_t *afl) { - - static const uint64_t JUMP[] = {0x180ec6d33cfd0aba, 0xd5a61266f0c9392c, - 0xa9582618e03fc9aa, 0x39abdc4529b1661c}; - int i, b; - uint64_t s0 = 0; - uint64_t s1 = 0; - uint64_t s2 = 0; - uint64_t s3 = 0; - for (i = 0; i < sizeof JUMP / sizeof *JUMP; i++) - for (b = 0; b < 64; b++) { - - if (JUMP[i] & UINT64_C(1) << b) { - - s0 ^= afl->rand_seed[0]; - s1 ^= afl->rand_seed[1]; - s2 ^= afl->rand_seed[2]; - s3 ^= afl->rand_seed[3]; - - } - - rand_next(afl); - - } - - afl->rand_seed[0] = s0; - afl->rand_seed[1] = s1; - afl->rand_seed[2] = s2; - afl->rand_seed[3] = s3; +#else +// RomuTrio32 +inline AFL_RAND_RETURN rand_next(afl_state_t *afl) { + + AFL_RAND_RETURN xp = afl->rand_seed[0], yp = afl->rand_seed[1], + zp = afl->rand_seed[2]; + afl->rand_seed[0] = 3323815723u * zp; + afl->rand_seed[1] = yp - xp; + afl->rand_seed[1] = ROTL(afl->rand_seed[1], 6); + afl->rand_seed[2] = zp - yp; + afl->rand_seed[2] = ROTL(afl->rand_seed[2], 22); + return xp; } -/* This is the long-jump function for the generator. It is equivalent to - 2^192 calls to rand_next(); it can be used to generate 2^64 starting points, - from each of which jump() will generate 2^64 non-overlapping - subsequences for parallel distributed computations. */ - -void long_jump(afl_state_t *afl) { - - static const uint64_t LONG_JUMP[] = {0x76e15d3efefdcbbf, 0xc5004e441c522fb3, - 0x77710069854ee241, 0x39109bb02acbe635}; - - int i, b; - uint64_t s0 = 0; - uint64_t s1 = 0; - uint64_t s2 = 0; - uint64_t s3 = 0; - for (i = 0; i < sizeof LONG_JUMP / sizeof *LONG_JUMP; i++) - for (b = 0; b < 64; b++) { - - if (LONG_JUMP[i] & UINT64_C(1) << b) { - - s0 ^= afl->rand_seed[0]; - s1 ^= afl->rand_seed[1]; - s2 ^= afl->rand_seed[2]; - s3 ^= afl->rand_seed[3]; +#endif - } +#undef ROTL - rand_next(afl); +/* returns a double between 0.000000000 and 1.000000000 */ - } +inline double rand_next_percent(afl_state_t *afl) { - afl->rand_seed[0] = s0; - afl->rand_seed[1] = s1; - afl->rand_seed[2] = s2; - afl->rand_seed[3] = s3; + return (double)(((double)rand_next(afl)) / (double)0xffffffffffffffff); } @@ -145,7 +86,7 @@ void long_jump(afl_state_t *afl) { u32 hash32(u8 *key, u32 len, u32 seed) { #else -u32 inline hash32(u8 *key, u32 len, u32 seed) { +inline u32 hash32(u8 *key, u32 len, u32 seed) { #endif @@ -157,7 +98,7 @@ u32 inline hash32(u8 *key, u32 len, u32 seed) { u64 hash64(u8 *key, u32 len, u64 seed) { #else -u64 inline hash64(u8 *key, u32 len, u64 seed) { +inline u64 hash64(u8 *key, u32 len, u64 seed) { #endif diff --git a/src/afl-sharedmem.c b/src/afl-sharedmem.c index 6eb63949..3241a130 100644 --- a/src/afl-sharedmem.c +++ b/src/afl-sharedmem.c @@ -66,9 +66,17 @@ static list_t shm_list = {.element_prealloc_count = 0}; void afl_shm_deinit(sharedmem_t *shm) { - if (shm == NULL) return; - + if (shm == NULL) { return; } list_remove(&shm_list, shm); + if (shm->shmemfuzz_mode) { + + unsetenv(SHM_FUZZ_ENV_VAR); + + } else { + + unsetenv(SHM_ENV_VAR); + + } #ifdef USEMMAP if (shm->map != NULL) { @@ -94,6 +102,8 @@ void afl_shm_deinit(sharedmem_t *shm) { if (shm->cmplog_mode) { + unsetenv(CMPLOG_SHM_ENV_VAR); + if (shm->cmp_map != NULL) { munmap(shm->cmp_map, shm->map_size); @@ -205,7 +215,7 @@ u8 *afl_shm_init(sharedmem_t *shm, size_t map_size, /* map the shared memory segment to the address space of the process */ shm->cmp_map = mmap(0, map_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm->cmplog_g_shm_fd, 0); - if (shm->map == MAP_FAILED) { + if (shm->cmp_map == MAP_FAILED) { close(shm->cmplog_g_shm_fd); shm->cmplog_g_shm_fd = -1; @@ -248,22 +258,26 @@ u8 *afl_shm_init(sharedmem_t *shm, size_t map_size, } - shm_str = alloc_printf("%d", shm->shm_id); + if (!non_instrumented_mode) { - /* If somebody is asking us to fuzz instrumented binaries in non-instrumented - mode, we don't want them to detect instrumentation, since we won't be - sending fork server commands. This should be replaced with better - auto-detection later on, perhaps? */ + shm_str = alloc_printf("%d", shm->shm_id); - if (!non_instrumented_mode) { setenv(SHM_ENV_VAR, shm_str, 1); } + /* If somebody is asking us to fuzz instrumented binaries in + non-instrumented mode, we don't want them to detect instrumentation, + since we won't be sending fork server commands. This should be replaced + with better auto-detection later on, perhaps? */ - ck_free(shm_str); + setenv(SHM_ENV_VAR, shm_str, 1); - if (shm->cmplog_mode) { + ck_free(shm_str); + + } + + if (shm->cmplog_mode && !non_instrumented_mode) { shm_str = alloc_printf("%d", shm->cmplog_shm_id); - if (!non_instrumented_mode) { setenv(CMPLOG_SHM_ENV_VAR, shm_str, 1); } + setenv(CMPLOG_SHM_ENV_VAR, shm_str, 1); ck_free(shm_str); @@ -274,6 +288,7 @@ u8 *afl_shm_init(sharedmem_t *shm, size_t map_size, if (shm->map == (void *)-1 || !shm->map) { shmctl(shm->shm_id, IPC_RMID, NULL); // do not leak shmem + if (shm->cmplog_mode) { shmctl(shm->cmplog_shm_id, IPC_RMID, NULL); // do not leak shmem @@ -291,11 +306,8 @@ u8 *afl_shm_init(sharedmem_t *shm, size_t map_size, if (shm->cmp_map == (void *)-1 || !shm->cmp_map) { shmctl(shm->shm_id, IPC_RMID, NULL); // do not leak shmem - if (shm->cmplog_mode) { - shmctl(shm->cmplog_shm_id, IPC_RMID, NULL); // do not leak shmem - - } + shmctl(shm->cmplog_shm_id, IPC_RMID, NULL); // do not leak shmem PFATAL("shmat() failed"); diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 71e975a1..7bf5a9c7 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -31,9 +31,6 @@ #define AFL_MAIN -#ifdef __ANDROID__ - #include "android-ashmem.h" -#endif #include "config.h" #include "types.h" #include "debug.h" @@ -42,6 +39,7 @@ #include "sharedmem.h" #include "forkserver.h" #include "common.h" +#include "hash.h" #include <stdio.h> #include <unistd.h> @@ -68,9 +66,11 @@ static char *stdin_file; /* stdin file */ static u8 *in_dir = NULL, /* input folder */ *out_file = NULL, *at_file = NULL; /* Substitution string for @@ */ -static u8 *in_data; /* Input data */ +static u8 *in_data, /* Input data */ + *coverage_map; /* Coverage map */ -static u32 total, highest; /* tuple content information */ +static u64 total; /* tuple content information */ +static u32 tcnt, highest; /* tuple content information */ static u32 in_len, /* Input data length */ arg_offset; /* Total number of execs */ @@ -83,7 +83,11 @@ static u8 quiet_mode, /* Hide non-essential messages? */ cmin_mode, /* Generate output in afl-cmin mode? */ binary_mode, /* Write output as a binary map */ keep_cores, /* Allow coredumps? */ - remove_shm = 1; /* remove shmem? */ + remove_shm = 1, /* remove shmem? */ + collect_coverage, /* collect coverage */ + have_coverage, /* have coverage? */ + no_classify, /* do not classify counts */ + debug; /* debug mode */ static volatile u8 stop_soon, /* Ctrl-C pressed? */ child_crashed; /* Child crashed? */ @@ -95,11 +99,24 @@ static sharedmem_t * shm_fuzz; /* 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. */ +#define TIMES4(x) x, x, x, x +#define TIMES8(x) TIMES4(x), TIMES4(x) +#define TIMES16(x) TIMES8(x), TIMES8(x) +#define TIMES32(x) TIMES16(x), TIMES16(x) +#define TIMES64(x) TIMES32(x), TIMES32(x) +#define TIMES96(x) TIMES64(x), TIMES32(x) +#define TIMES128(x) TIMES64(x), TIMES64(x) 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 + [0] = 0, + [1] = 1, + [2] = 2, + [3] = 3, + [4] = TIMES4(4), + [8] = TIMES8(5), + [16] = TIMES16(6), + [32] = TIMES96(7), + [128] = TIMES128(8) }; @@ -109,14 +126,22 @@ static const u8 count_class_binary[256] = { [1] = 1, [2] = 2, [3] = 4, - [4 ... 7] = 8, - [8 ... 15] = 16, - [16 ... 31] = 32, - [32 ... 127] = 64, - [128 ... 255] = 128 + [4] = TIMES4(8), + [8] = TIMES8(16), + [16] = TIMES16(32), + [32] = TIMES32(64), + [128] = TIMES64(128) }; +#undef TIMES128 +#undef TIMES96 +#undef TIMES64 +#undef TIMES32 +#undef TIMES16 +#undef TIMES8 +#undef TIMES4 + static void classify_counts(afl_forkserver_t *fsrv) { u8 * mem = fsrv->trace_bits; @@ -175,6 +200,25 @@ static void at_exit_handler(void) { } +/* Analyze results. */ + +static void analyze_results(afl_forkserver_t *fsrv) { + + u32 i; + for (i = 0; i < map_size; i++) { + + if (fsrv->trace_bits[i]) { + + total += fsrv->trace_bits[i]; + if (fsrv->trace_bits[i] > highest) highest = fsrv->trace_bits[i]; + if (!coverage_map[i]) { coverage_map[i] = 1; } + + } + + } + +} + /* Write results. */ static u32 write_results_to_file(afl_forkserver_t *fsrv, u8 *outfile) { @@ -187,6 +231,13 @@ static u32 write_results_to_file(afl_forkserver_t *fsrv, u8 *outfile) { if (!outfile) { FATAL("Output filename not set (Bug in AFL++?)"); } + if (cmin_mode && + (fsrv->last_run_timed_out || (!caa && child_crashed != cco))) { + + return ret; + + } + if (!strncmp(outfile, "/dev/", 5)) { fd = open(outfile, O_WRONLY); @@ -233,9 +284,6 @@ static u32 write_results_to_file(afl_forkserver_t *fsrv, u8 *outfile) { if (cmin_mode) { - if (fsrv->last_run_timed_out) { break; } - if (!caa && child_crashed != cco) { break; } - fprintf(f, "%u%u\n", fsrv->trace_bits[i], i); } else { @@ -256,11 +304,13 @@ static u32 write_results_to_file(afl_forkserver_t *fsrv, u8 *outfile) { /* Execute target application. */ -static void showmap_run_target_forkserver(afl_forkserver_t *fsrv, char **argv, - u8 *mem, u32 len) { +static void showmap_run_target_forkserver(afl_forkserver_t *fsrv, u8 *mem, + u32 len) { afl_fsrv_write_to_testcase(fsrv, mem, len); + if (!quiet_mode) { SAYF("-- Program output begins --\n" cRST); } + if (afl_fsrv_run_target(fsrv, fsrv->exec_tmout, &stop_soon) == FSRV_RUN_ERROR) { @@ -268,7 +318,50 @@ static void showmap_run_target_forkserver(afl_forkserver_t *fsrv, char **argv, } - classify_counts(fsrv); + if (fsrv->trace_bits[0] == 1) { + + fsrv->trace_bits[0] = 0; + have_coverage = 1; + + } else { + + have_coverage = 0; + + } + + if (!no_classify) { classify_counts(fsrv); } + + if (!quiet_mode) { SAYF(cRST "-- Program output ends --\n"); } + + if (!fsrv->last_run_timed_out && !stop_soon && + WIFSIGNALED(fsrv->child_status)) { + + child_crashed = 1; + + } else { + + child_crashed = 0; + + } + + if (!quiet_mode) { + + if (fsrv->last_run_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(fsrv->child_status)); + + } + + } if (stop_soon) { @@ -409,7 +502,18 @@ static void showmap_run_target(afl_forkserver_t *fsrv, char **argv) { } - classify_counts(fsrv); + if (fsrv->trace_bits[0] == 1) { + + fsrv->trace_bits[0] = 0; + have_coverage = 1; + + } else { + + have_coverage = 0; + + } + + if (!no_classify) { classify_counts(fsrv); } if (!quiet_mode) { SAYF(cRST "-- Program output ends --\n"); } @@ -444,6 +548,7 @@ static void showmap_run_target(afl_forkserver_t *fsrv, char **argv) { static void handle_stop_sig(int sig) { + (void)sig; stop_soon = 1; afl_fsrv_killall(); @@ -458,6 +563,7 @@ static void set_up_environment(afl_forkserver_t *fsrv) { "detect_leaks=0:" "allocator_may_return_null=1:" "symbolize=0:" + "detect_odr_violation=0:" "handle_segv=0:" "handle_sigbus=0:" "handle_abort=0:" @@ -493,38 +599,7 @@ static void set_up_environment(afl_forkserver_t *fsrv) { if (fsrv->qemu_mode) { - u8 *qemu_preload = getenv("QEMU_SET_ENV"); - u8 *afl_preload = getenv("AFL_PRELOAD"); - u8 *buf; - - s32 i, afl_preload_size = strlen(afl_preload); - for (i = 0; i < afl_preload_size; ++i) { - - if (afl_preload[i] == ',') { - - PFATAL( - "Comma (',') is not allowed in AFL_PRELOAD when -Q is " - "specified!"); - - } - - } - - if (qemu_preload) { - - buf = alloc_printf("%s,LD_PRELOAD=%s,DYLD_INSERT_LIBRARIES=%s", - qemu_preload, afl_preload, afl_preload); - - } else { - - buf = alloc_printf("LD_PRELOAD=%s,DYLD_INSERT_LIBRARIES=%s", - afl_preload, afl_preload); - - } - - setenv("QEMU_SET_ENV", buf, 1); - - ck_free(buf); + /* afl-qemu-trace takes care of converting AFL_PRELOAD. */ } else { @@ -580,19 +655,25 @@ static void usage(u8 *argv0) { "Execution control settings:\n" " -t msec - timeout for each run (none)\n" - " -m megs - memory limit for child process (%d MB)\n" + " -m megs - memory limit for child process (%u MB)\n" " -Q - use binary-only instrumentation (QEMU mode)\n" " -U - use Unicorn-based instrumentation (Unicorn mode)\n" " -W - use qemu-based instrumentation with Wine (Wine mode)\n" " (Not necessary, here for consistency with other afl-* " "tools)\n\n" "Other settings:\n" - " -i dir - process all files in this directory, -o must be a " + " -i dir - process all files in this directory, must be combined " + "with -o.\n" + " With -C, -o is a file, without -C it must be a " "directory\n" " and each bitmap will be written there individually.\n" + " -C - collect coverage, writes all edges to -o and gives a " + "summary\n" + " Must be combined with -i.\n" " -q - sink program's output and don't show messages\n" " -e - show edge coverage only, ignore hit counts\n" " -r - show real tuple values instead of AFL filter values\n" + " -s - do not classify the map\n" " -c - allow core dumps\n\n" "This tool displays raw tuple data captured by AFL instrumentation.\n" @@ -603,10 +684,15 @@ static void usage(u8 *argv0) { "AFL_CMIN_CRASHES_ONLY: (cmin_mode) only write tuples for crashing " "inputs\n" "AFL_CMIN_ALLOW_ANY: (cmin_mode) write tuples for crashing inputs also\n" + "AFL_CRASH_EXITCODE: optional child exit code to be interpreted as " + "crash\n" "AFL_DEBUG: enable extra developer output\n" + "AFL_FORKSRV_INIT_TMOUT: time spent waiting for forkserver during " + "startup (in milliseconds)\n" + "AFL_KILL_SIGNAL: Signal ID delivered to child processes on timeout, " + "etc. (default: SIGKILL)\n" "AFL_MAP_SIZE: the shared memory size for that target. must be >= the " - "size\n" - " the target was compiled for\n" + "size the target was compiled for\n" "AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n" "AFL_QUIET: do not print extra informational output\n", argv0, MEM_LIMIT, doc_path); @@ -623,12 +709,12 @@ int main(int argc, char **argv_orig, char **envp) { s32 opt, i; u8 mem_limit_given = 0, timeout_given = 0, unicorn_mode = 0, use_wine = 0; - u32 tcnt = 0; char **use_argv; char **argv = argv_cpy_dup(argc, argv_orig); afl_forkserver_t fsrv_var = {0}; + if (getenv("AFL_DEBUG")) { debug = 1; } fsrv = &fsrv_var; afl_fsrv_init(fsrv); map_size = get_map_size(); @@ -638,10 +724,19 @@ int main(int argc, char **argv_orig, char **envp) { if (getenv("AFL_QUIET") != NULL) { be_quiet = 1; } - while ((opt = getopt(argc, argv, "+i:o:f:m:t:A:eqZQUWbcrh")) > 0) { + while ((opt = getopt(argc, argv, "+i:o:f:m:t:A:eqCZQUWbcrsh")) > 0) { switch (opt) { + case 's': + no_classify = 1; + break; + + case 'C': + collect_coverage = 1; + quiet_mode = 1; + break; + case 'i': if (in_dir) { FATAL("Multiple -i options not supported"); } in_dir = optarg; @@ -709,8 +804,10 @@ int main(int argc, char **argv_orig, char **envp) { case 'f': // only in here to avoid a compiler warning for use_stdin - fsrv->use_stdin = 0; FATAL("Option -f is not supported in afl-showmap"); + // currently not reached: + fsrv->use_stdin = 0; + fsrv->out_file = strdup(optarg); break; @@ -744,7 +841,6 @@ int main(int argc, char **argv_orig, char **envp) { case 'q': - if (quiet_mode) { FATAL("Multiple -q options not supported"); } quiet_mode = 1; break; @@ -819,6 +915,13 @@ int main(int argc, char **argv_orig, char **envp) { if (optind == argc || !out_file) { usage(argv[0]); } + if (in_dir) { + + if (!out_file && !collect_coverage) + FATAL("for -i you need to specify either -C and/or -o"); + + } + if (fsrv->qemu_mode && !mem_limit_given) { fsrv->mem_limit = MEM_LIMIT_QEMU; } if (unicorn_mode && !mem_limit_given) { fsrv->mem_limit = MEM_LIMIT_UNICORN; } @@ -826,7 +929,7 @@ int main(int argc, char **argv_orig, char **envp) { if (getenv("AFL_DEBUG")) { - SAYF(cMGN "[D]" cRST); + DEBUGF(""); for (i = 0; i < argc; i++) SAYF(" %s", argv[i]); SAYF("\n"); @@ -835,14 +938,16 @@ int main(int argc, char **argv_orig, char **envp) { // if (afl->shmem_testcase_mode) { setup_testcase_shmem(afl); } + setenv("AFL_NO_AUTODICT", "1", 1); + /* initialize cmplog_mode */ shm.cmplog_mode = 0; - fsrv->trace_bits = afl_shm_init(&shm, map_size, 0); setup_signal_handlers(); set_up_environment(fsrv); fsrv->target_path = find_binary(argv[optind]); + fsrv->trace_bits = afl_shm_init(&shm, map_size, 0); if (!quiet_mode) { @@ -853,7 +958,6 @@ int main(int argc, char **argv_orig, char **envp) { if (in_dir) { - if (at_file) { PFATAL("Options -A and -i are mutually exclusive"); } detect_file_args(argv + optind, "", &fsrv->use_stdin); } else { @@ -895,6 +999,7 @@ int main(int argc, char **argv_orig, char **envp) { /* initialize cmplog_mode */ shm_fuzz->cmplog_mode = 0; u8 *map = afl_shm_init(shm_fuzz, MAX_FILE + sizeof(u32), 1); + shm_fuzz->shmemfuzz_mode = 1; if (!map) { FATAL("BUG: Zero return from afl_shm_init."); } #ifdef USEMMAP setenv(SHM_FUZZ_ENV_VAR, shm_fuzz->g_shm_file_path, 1); @@ -907,13 +1012,50 @@ int main(int argc, char **argv_orig, char **envp) { fsrv->shmem_fuzz_len = (u32 *)map; fsrv->shmem_fuzz = map + sizeof(u32); + if (!fsrv->qemu_mode && !unicorn_mode) { + + u32 save_be_quiet = be_quiet; + be_quiet = !debug; + fsrv->map_size = 4194304; // dummy temporary value + u32 new_map_size = + afl_fsrv_get_mapsize(fsrv, use_argv, &stop_soon, + (get_afl_env("AFL_DEBUG_CHILD") || + get_afl_env("AFL_DEBUG_CHILD_OUTPUT")) + ? 1 + : 0); + be_quiet = save_be_quiet; + + if (new_map_size) { + + // only reinitialize when it makes sense + if (map_size < new_map_size || + (new_map_size > map_size && new_map_size - map_size > MAP_SIZE)) { + + if (!be_quiet) + ACTF("Aquired new map size for target: %u bytes\n", new_map_size); + + afl_shm_deinit(&shm); + afl_fsrv_kill(fsrv); + fsrv->map_size = new_map_size; + fsrv->trace_bits = afl_shm_init(&shm, new_map_size, 0); + + } + + map_size = new_map_size; + + } + + fsrv->map_size = map_size; + + } + if (in_dir) { - DIR * dir_in, *dir_out; + DIR * dir_in, *dir_out = NULL; struct dirent *dir_ent; - int done = 0; - u8 infile[PATH_MAX], outfile[PATH_MAX]; - u8 wait_for_gdb = 0; + // int done = 0; + u8 infile[PATH_MAX], outfile[PATH_MAX]; + u8 wait_for_gdb = 0; #if !defined(DT_REG) struct stat statbuf; #endif @@ -923,20 +1065,43 @@ int main(int argc, char **argv_orig, char **envp) { fsrv->dev_null_fd = open("/dev/null", O_RDWR); if (fsrv->dev_null_fd < 0) { PFATAL("Unable to open /dev/null"); } + // if a queue subdirectory exists switch to that + u8 *dn = alloc_printf("%s/queue", in_dir); + if ((dir_in = opendir(dn)) != NULL) { + + closedir(dir_in); + in_dir = dn; + + } else + + ck_free(dn); + if (!be_quiet) ACTF("Reading from directory '%s'...", in_dir); + if (!(dir_in = opendir(in_dir))) { PFATAL("cannot open directory %s", in_dir); } - if (!(dir_out = opendir(out_file))) { + if (!collect_coverage) { + + if (!(dir_out = opendir(out_file))) { - if (mkdir(out_file, 0700)) { + if (mkdir(out_file, 0700)) { - PFATAL("cannot create output directory %s", out_file); + PFATAL("cannot create output directory %s", out_file); + + } } + } else { + + if ((coverage_map = (u8 *)malloc(map_size)) == NULL) + FATAL("coult not grab memory"); + edges_only = 0; + raw_instr_output = 1; + } u8 *use_dir = "."; @@ -948,10 +1113,12 @@ int main(int argc, char **argv_orig, char **envp) { } - stdin_file = - alloc_printf("%s/.afl-showmap-temp-%u", use_dir, (u32)getpid()); + stdin_file = at_file ? strdup(at_file) + : (char *)alloc_printf("%s/.afl-showmap-temp-%u", + use_dir, (u32)getpid()); unlink(stdin_file); atexit(at_exit_handler); + fsrv->out_file = stdin_file; fsrv->out_fd = open(stdin_file, O_RDWR | O_CREAT | O_EXCL, 0600); if (fsrv->out_fd < 0) { PFATAL("Unable to create '%s'", out_file); } @@ -963,11 +1130,11 @@ int main(int argc, char **argv_orig, char **envp) { if (get_afl_env("AFL_DEBUG")) { - int i = optind; - SAYF(cMGN "[D]" cRST " %s:", fsrv->target_path); - while (argv[i] != NULL) { + int j = optind; + DEBUGF("%s:", fsrv->target_path); + while (argv[j] != NULL) { - SAYF(" \"%s\"", argv[i++]); + SAYF(" \"%s\"", argv[j++]); } @@ -975,13 +1142,51 @@ int main(int argc, char **argv_orig, char **envp) { } + if (getenv("AFL_FORKSRV_INIT_TMOUT")) { + + s32 forksrv_init_tmout = atoi(getenv("AFL_FORKSRV_INIT_TMOUT")); + if (forksrv_init_tmout < 1) { + + FATAL("Bad value specified for AFL_FORKSRV_INIT_TMOUT"); + + } + + fsrv->init_tmout = (u32)forksrv_init_tmout; + + } + + fsrv->kill_signal = + parse_afl_kill_signal_env(getenv("AFL_KILL_SIGNAL"), SIGKILL); + + if (getenv("AFL_CRASH_EXITCODE")) { + + long exitcode = strtol(getenv("AFL_CRASH_EXITCODE"), NULL, 10); + if ((!exitcode && (errno == EINVAL || errno == ERANGE)) || + exitcode < -127 || exitcode > 128) { + + FATAL("Invalid crash exitcode, expected -127 to 128, but got %s", + getenv("AFL_CRASH_EXITCODE")); + + } + + fsrv->uses_crash_exitcode = true; + // WEXITSTATUS is 8 bit unsigned + fsrv->crash_exitcode = (u8)exitcode; + + } + afl_fsrv_start(fsrv, use_argv, &stop_soon, - get_afl_env("AFL_DEBUG_CHILD_OUTPUT") ? 1 : 0); + (get_afl_env("AFL_DEBUG_CHILD") || + get_afl_env("AFL_DEBUG_CHILD_OUTPUT")) + ? 1 + : 0); + + map_size = fsrv->map_size; if (fsrv->support_shmem_fuzz && !fsrv->use_shmem_fuzz) shm_fuzz = deinit_shmem(fsrv, shm_fuzz); - while (done == 0 && (dir_ent = readdir(dir_in))) { + while ((dir_ent = readdir(dir_in))) { if (dir_ent->d_name[0] == '.') { @@ -1004,7 +1209,8 @@ int main(int argc, char **argv_orig, char **envp) { if (-1 == stat(infile, &statbuf) || !S_ISREG(statbuf.st_mode)) continue; #endif - snprintf(outfile, sizeof(outfile), "%s/%s", out_file, dir_ent->d_name); + if (!collect_coverage) + snprintf(outfile, sizeof(outfile), "%s/%s", out_file, dir_ent->d_name); if (read_file(infile)) { @@ -1016,9 +1222,12 @@ int main(int argc, char **argv_orig, char **envp) { } - showmap_run_target_forkserver(fsrv, use_argv, in_data, in_len); + showmap_run_target_forkserver(fsrv, in_data, in_len); ck_free(in_data); - tcnt = write_results_to_file(fsrv, outfile); + if (collect_coverage) + analyze_results(fsrv); + else + tcnt = write_results_to_file(fsrv, outfile); } @@ -1029,6 +1238,13 @@ int main(int argc, char **argv_orig, char **envp) { closedir(dir_in); if (dir_out) { closedir(dir_out); } + if (collect_coverage) { + + memcpy(fsrv->trace_bits, coverage_map, map_size); + tcnt = write_results_to_file(fsrv, out_file); + + } + } else { if (fsrv->support_shmem_fuzz && !fsrv->use_shmem_fuzz) @@ -1036,14 +1252,26 @@ int main(int argc, char **argv_orig, char **envp) { showmap_run_target(fsrv, use_argv); tcnt = write_results_to_file(fsrv, out_file); + if (!quiet_mode) { + + OKF("Hash of coverage map: %llx", + hash64(fsrv->trace_bits, fsrv->map_size, HASH_CONST)); + + } } - if (!quiet_mode) { + if (!quiet_mode || collect_coverage) { - if (!tcnt) { FATAL("No instrumentation detected" cRST); } - OKF("Captured %u tuples (highest value %u, total values %u) in '%s'." cRST, + if (!tcnt && !have_coverage) { FATAL("No instrumentation detected" cRST); } + OKF("Captured %u tuples (highest value %u, total values %llu) in " + "'%s'." cRST, tcnt, highest, total, out_file); + if (collect_coverage) + OKF("A coverage of %u edges were achieved out of %u existing (%.02f%%) " + "with %llu input files.", + tcnt, map_size, ((float)tcnt * 100) / (float)map_size, + fsrv->total_execs); } @@ -1059,13 +1287,24 @@ int main(int argc, char **argv_orig, char **envp) { afl_shm_deinit(&shm); if (fsrv->use_shmem_fuzz) shm_fuzz = deinit_shmem(fsrv, shm_fuzz); - u32 ret = child_crashed * 2 + fsrv->last_run_timed_out; + u32 ret; + + if (cmin_mode && !!getenv("AFL_CMIN_CRASHES_ONLY")) { + + ret = fsrv->last_run_timed_out; + + } else { + + ret = child_crashed * 2 + fsrv->last_run_timed_out; + + } if (fsrv->target_path) { ck_free(fsrv->target_path); } afl_fsrv_deinit(fsrv); if (stdin_file) { ck_free(stdin_file); } + if (collect_coverage) { free(coverage_map); } argv_cpy_free(argv); if (fsrv->qemu_mode) { free(use_argv[2]); } diff --git a/src/afl-tmin.c b/src/afl-tmin.c index 68fcdd14..7ef8b9bf 100644 --- a/src/afl-tmin.c +++ b/src/afl-tmin.c @@ -29,10 +29,6 @@ #define AFL_MAIN -#ifdef __ANDROID__ - #include "android-ashmem.h" -#endif - #include "config.h" #include "types.h" #include "debug.h" @@ -51,6 +47,7 @@ #include <signal.h> #include <dirent.h> #include <fcntl.h> +#include <limits.h> #include <sys/wait.h> #include <sys/time.h> @@ -82,7 +79,8 @@ static u8 crash_mode, /* Crash-centric mode? */ edges_only, /* Ignore hit counts? */ exact_mode, /* Require path match for crashes? */ remove_out_file, /* remove out_file on exit? */ - remove_shm = 1; /* remove shmem on exit? */ + remove_shm = 1, /* remove shmem on exit? */ + debug; /* debug mode */ static volatile u8 stop_soon; /* Ctrl-C pressed? */ @@ -97,20 +95,31 @@ static sharedmem_t * shm_fuzz; /* Classify tuple counts. This is a slow & naive version, but good enough here. */ +#define TIMES4(x) x, x, x, x +#define TIMES8(x) TIMES4(x), TIMES4(x) +#define TIMES16(x) TIMES8(x), TIMES8(x) +#define TIMES32(x) TIMES16(x), TIMES16(x) +#define TIMES64(x) TIMES32(x), TIMES32(x) static const u8 count_class_lookup[256] = { [0] = 0, [1] = 1, [2] = 2, [3] = 4, - [4 ... 7] = 8, - [8 ... 15] = 16, - [16 ... 31] = 32, - [32 ... 127] = 64, - [128 ... 255] = 128 + [4] = TIMES4(8), + [8] = TIMES8(16), + [16] = TIMES16(32), + [32] = TIMES32(64), + [128] = TIMES64(128) }; +#undef TIMES64 +#undef TIMES32 +#undef TIMES16 +#undef TIMES8 +#undef TIMES4 + static sharedmem_t *deinit_shmem(afl_forkserver_t *fsrv, sharedmem_t * shm_fuzz) { @@ -250,7 +259,7 @@ static s32 write_to_file(u8 *path, u8 *mem, u32 len) { /* Execute target application. Returns 0 if the changes are a dud, or 1 if they should be kept. */ -static u8 tmin_run_target(afl_forkserver_t *fsrv, char **argv, u8 *mem, u32 len, +static u8 tmin_run_target(afl_forkserver_t *fsrv, u8 *mem, u32 len, u8 first_run) { afl_fsrv_write_to_testcase(fsrv, mem, len); @@ -342,7 +351,7 @@ static u8 tmin_run_target(afl_forkserver_t *fsrv, char **argv, u8 *mem, u32 len, /* Actually minimize! */ -static void minimize(afl_forkserver_t *fsrv, char **argv) { +static void minimize(afl_forkserver_t *fsrv) { static u32 alpha_map[256]; @@ -380,7 +389,7 @@ static void minimize(afl_forkserver_t *fsrv, char **argv) { memset(tmp_buf + set_pos, '0', use_len); u8 res; - res = tmin_run_target(fsrv, argv, tmp_buf, in_len, 0); + res = tmin_run_target(fsrv, tmp_buf, in_len, 0); if (res) { @@ -453,7 +462,7 @@ next_del_blksize: /* Tail */ memcpy(tmp_buf + del_pos, in_data + del_pos + del_len, tail_len); - res = tmin_run_target(fsrv, argv, tmp_buf, del_pos + tail_len, 0); + res = tmin_run_target(fsrv, tmp_buf, del_pos + tail_len, 0); if (res) { @@ -524,7 +533,7 @@ next_del_blksize: } - res = tmin_run_target(fsrv, argv, tmp_buf, in_len, 0); + res = tmin_run_target(fsrv, tmp_buf, in_len, 0); if (res) { @@ -560,7 +569,7 @@ next_del_blksize: if (orig == '0') { continue; } tmp_buf[i] = '0'; - res = tmin_run_target(fsrv, argv, tmp_buf, in_len, 0); + res = tmin_run_target(fsrv, tmp_buf, in_len, 0); if (res) { @@ -623,6 +632,7 @@ finalize_all: static void handle_stop_sig(int sig) { + (void)sig; stop_soon = 1; afl_fsrv_killall(); @@ -655,6 +665,7 @@ static void set_up_environment(afl_forkserver_t *fsrv) { unlink(out_file); + fsrv->out_file = out_file; fsrv->out_fd = open(out_file, O_RDWR | O_CREAT | O_EXCL, 0600); if (fsrv->out_fd < 0) { PFATAL("Unable to create '%s'", out_file); } @@ -671,12 +682,15 @@ static void set_up_environment(afl_forkserver_t *fsrv) { } - if (!strstr(x, "symbolize=0")) { +#ifndef ASAN_BUILD + if (!getenv("AFL_DEBUG") && !strstr(x, "symbolize=0")) { FATAL("Custom ASAN_OPTIONS set without symbolize=0 - please fix!"); } +#endif + } x = get_afl_env("MSAN_OPTIONS"); @@ -703,6 +717,7 @@ static void set_up_environment(afl_forkserver_t *fsrv) { "detect_leaks=0:" "allocator_may_return_null=1:" "symbolize=0:" + "detect_odr_violation=0:" "handle_segv=0:" "handle_sigbus=0:" "handle_abort=0:" @@ -738,38 +753,7 @@ static void set_up_environment(afl_forkserver_t *fsrv) { if (fsrv->qemu_mode) { - u8 *qemu_preload = getenv("QEMU_SET_ENV"); - u8 *afl_preload = getenv("AFL_PRELOAD"); - u8 *buf; - - s32 i, afl_preload_size = strlen(afl_preload); - for (i = 0; i < afl_preload_size; ++i) { - - if (afl_preload[i] == ',') { - - PFATAL( - "Comma (',') is not allowed in AFL_PRELOAD when -Q is " - "specified!"); - - } - - } - - if (qemu_preload) { - - buf = alloc_printf("%s,LD_PRELOAD=%s,DYLD_INSERT_LIBRARIES=%s", - qemu_preload, afl_preload, afl_preload); - - } else { - - buf = alloc_printf("LD_PRELOAD=%s,DYLD_INSERT_LIBRARIES=%s", - afl_preload, afl_preload); - - } - - setenv("QEMU_SET_ENV", buf, 1); - - ck_free(buf); + /* afl-qemu-trace takes care of converting AFL_PRELOAD. */ } else { @@ -818,8 +802,8 @@ static void usage(u8 *argv0) { "Execution control settings:\n" " -f file - input file read by the tested program (stdin)\n" - " -t msec - timeout for each run (%d ms)\n" - " -m megs - memory limit for child process (%d MB)\n" + " -t msec - timeout for each run (%u ms)\n" + " -m megs - memory limit for child process (%u MB)\n" " -Q - use binary-only instrumentation (QEMU mode)\n" " -U - use unicorn-based instrumentation (Unicorn mode)\n" " -W - use qemu-based instrumentation with Wine (Wine " @@ -836,16 +820,18 @@ static void usage(u8 *argv0) { "For additional tips, please consult %s/README.md.\n\n" "Environment variables used:\n" - "TMPDIR: directory to use for temporary input files\n" - "ASAN_OPTIONS: custom settings for ASAN\n" - " (must contain abort_on_error=1 and symbolize=0)\n" - "MSAN_OPTIONS: custom settings for MSAN\n" - " (must contain exitcode="STRINGIFY(MSAN_ERROR)" and symbolize=0)\n" + "AFL_CRASH_EXITCODE: optional child exit code to be interpreted as crash\n" + "AFL_FORKSRV_INIT_TMOUT: time spent waiting for forkserver during startup (in milliseconds)\n" + "AFL_KILL_SIGNAL: Signal ID delivered to child processes on timeout, etc. (default: SIGKILL)\n" "AFL_MAP_SIZE: the shared memory size for that target. must be >= the size\n" " the target was compiled for\n" "AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n" "AFL_TMIN_EXACT: require execution paths to match for crashing inputs\n" - + "ASAN_OPTIONS: custom settings for ASAN\n" + " (must contain abort_on_error=1 and symbolize=0)\n" + "MSAN_OPTIONS: custom settings for MSAN\n" + " (must contain exitcode="STRINGIFY(MSAN_ERROR)" and symbolize=0)\n" + "TMPDIR: directory to use for temporary input files\n" , argv0, EXEC_TIMEOUT, MEM_LIMIT, doc_path); exit(1); @@ -863,6 +849,7 @@ int main(int argc, char **argv_orig, char **envp) { char **argv = argv_cpy_dup(argc, argv_orig); afl_forkserver_t fsrv_var = {0}; + if (getenv("AFL_DEBUG")) { debug = 1; } fsrv = &fsrv_var; afl_fsrv_init(fsrv); map_size = get_map_size(); @@ -1059,10 +1046,10 @@ int main(int argc, char **argv_orig, char **envp) { if (optind == argc || !in_file || !output_file) { usage(argv[0]); } check_environment_vars(envp); + setenv("AFL_NO_AUTODICT", "1", 1); /* initialize cmplog_mode */ shm.cmplog_mode = 0; - fsrv->trace_bits = afl_shm_init(&shm, map_size, 0); atexit(at_exit_handler); setup_signal_handlers(); @@ -1070,6 +1057,7 @@ int main(int argc, char **argv_orig, char **envp) { set_up_environment(fsrv); fsrv->target_path = find_binary(argv[optind]); + fsrv->trace_bits = afl_shm_init(&shm, map_size, 0); detect_file_args(argv + optind, out_file, &fsrv->use_stdin); if (fsrv->qemu_mode) { @@ -1103,11 +1091,45 @@ int main(int argc, char **argv_orig, char **envp) { SAYF("\n"); + if (getenv("AFL_FORKSRV_INIT_TMOUT")) { + + s32 forksrv_init_tmout = atoi(getenv("AFL_FORKSRV_INIT_TMOUT")); + if (forksrv_init_tmout < 1) { + + FATAL("Bad value specified for AFL_FORKSRV_INIT_TMOUT"); + + } + + fsrv->init_tmout = (u32)forksrv_init_tmout; + + } + + fsrv->kill_signal = + parse_afl_kill_signal_env(getenv("AFL_KILL_SIGNAL"), SIGKILL); + + if (getenv("AFL_CRASH_EXITCODE")) { + + long exitcode = strtol(getenv("AFL_CRASH_EXITCODE"), NULL, 10); + if ((!exitcode && (errno == EINVAL || errno == ERANGE)) || + exitcode < -127 || exitcode > 128) { + + FATAL("Invalid crash exitcode, expected -127 to 128, but got %s", + getenv("AFL_CRASH_EXITCODE")); + + } + + fsrv->uses_crash_exitcode = true; + // WEXITSTATUS is 8 bit unsigned + fsrv->crash_exitcode = (u8)exitcode; + + } + shm_fuzz = ck_alloc(sizeof(sharedmem_t)); /* initialize cmplog_mode */ shm_fuzz->cmplog_mode = 0; u8 *map = afl_shm_init(shm_fuzz, MAX_FILE + sizeof(u32), 1); + shm_fuzz->shmemfuzz_mode = 1; if (!map) { FATAL("BUG: Zero return from afl_shm_init."); } #ifdef USEMMAP setenv(SHM_FUZZ_ENV_VAR, shm_fuzz->g_shm_file_path, 1); @@ -1122,8 +1144,51 @@ int main(int argc, char **argv_orig, char **envp) { read_initial_file(); - afl_fsrv_start(fsrv, use_argv, &stop_soon, - get_afl_env("AFL_DEBUG_CHILD_OUTPUT") ? 1 : 0); + if (!fsrv->qemu_mode && !unicorn_mode) { + + fsrv->map_size = 4194304; // dummy temporary value + u32 new_map_size = + afl_fsrv_get_mapsize(fsrv, use_argv, &stop_soon, + (get_afl_env("AFL_DEBUG_CHILD") || + get_afl_env("AFL_DEBUG_CHILD_OUTPUT")) + ? 1 + : 0); + + if (new_map_size) { + + if (map_size < new_map_size || + (new_map_size > map_size && new_map_size - map_size > MAP_SIZE)) { + + if (!be_quiet) + ACTF("Aquired new map size for target: %u bytes\n", new_map_size); + + afl_shm_deinit(&shm); + afl_fsrv_kill(fsrv); + fsrv->map_size = new_map_size; + fsrv->trace_bits = afl_shm_init(&shm, new_map_size, 0); + afl_fsrv_start(fsrv, use_argv, &stop_soon, + (get_afl_env("AFL_DEBUG_CHILD") || + get_afl_env("AFL_DEBUG_CHILD_OUTPUT")) + ? 1 + : 0); + + } + + map_size = new_map_size; + + } + + fsrv->map_size = map_size; + + } else { + + afl_fsrv_start(fsrv, use_argv, &stop_soon, + (get_afl_env("AFL_DEBUG_CHILD") || + get_afl_env("AFL_DEBUG_CHILD_OUTPUT")) + ? 1 + : 0); + + } if (fsrv->support_shmem_fuzz && !fsrv->use_shmem_fuzz) shm_fuzz = deinit_shmem(fsrv, shm_fuzz); @@ -1131,7 +1196,7 @@ int main(int argc, char **argv_orig, char **envp) { ACTF("Performing dry run (mem limit = %llu MB, timeout = %u ms%s)...", fsrv->mem_limit, fsrv->exec_tmout, edges_only ? ", edges only" : ""); - tmin_run_target(fsrv, use_argv, in_data, in_len, 1); + tmin_run_target(fsrv, in_data, in_len, 1); if (hang_mode && !fsrv->last_run_timed_out) { @@ -1169,7 +1234,7 @@ int main(int argc, char **argv_orig, char **envp) { } - minimize(fsrv, use_argv); + minimize(fsrv); ACTF("Writing output to '%s'...", output_file); |